Skip to content

Commit 124d829

Browse files
committed
factor out layer methods into objects, use promise based loading
1 parent 23761b6 commit 124d829

16 files changed

Lines changed: 430 additions & 384 deletions

File tree

app/assets/stylesheets/docs.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ nav.fixed-top~#docs {
2121
padding-bottom: 0.4rem;
2222
width: fit-content;
2323
border-bottom: 0.15rem solid transparent;
24-
border-image: linear-gradient(to right, var(--color-dark-moss-green), var(--color-light-sand)) 1;
24+
border-image: linear-gradient(to right, var(--color-light-sand), var(--color-dark-moss-green)) 1;
2525
}
2626

2727
h2, h3, h4 {

app/javascript/channels/map_channel.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import consumer from 'channels/consumer'
2-
import { initializeLayerStyles, layers, loadLayerDefinitions } from 'maplibre/layers/layers'
2+
import { createLayerInstance } from 'maplibre/layers/factory'
3+
import { initializeLayerSources, initializeLayerStyles, layers, loadLayerDefinitions } from 'maplibre/layers/layers'
34
import {
45
destroyFeature,
56
initializeMaplibreProperties, map,
@@ -101,19 +102,17 @@ export function initializeSocket () {
101102
case 'update_layer':
102103
const index = layers.findIndex(l => l.id === data.layer.id)
103104
if (index > -1) {
104-
// Remove geojson key before comparison
105-
const { ['geojson']: _, ...layerDef } = layers[index]
105+
const layerDef = layers[index].toJSON()
106106
if (JSON.stringify(layerDef) !== JSON.stringify(data.layer)) {
107-
// preserve geojson data when updating layer definition
108-
const geojson = layers[index].geojson
109-
layers[index] = data.layer
110-
if (geojson) { layers[index].geojson = geojson }
111107
console.log('Layer updated on server, reloading layer styles', data.layer)
108+
layers[index].update(data.layer)
112109
initializeLayerStyles(data.layer.id)
113-
setLayerVisibility(data.layer.type + '-source-' + data.layer.id, data.layer.show !== false)
110+
setLayerVisibility(layers[index].sourceId, data.layer.show !== false)
114111
}
115112
} else {
116-
layers.push(data.layer)
113+
const newLayer = createLayerInstance(data.layer)
114+
layers.push(newLayer)
115+
initializeLayerSources(data.layer.id)
117116
initializeLayerStyles(data.layer.id)
118117
}
119118
break

app/javascript/controllers/feature/edit_controller.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import { status } from 'helpers/status'
66
import { flyToFeature } from 'maplibre/animations'
77
import { draw, handleDelete } from 'maplibre/edit'
88
import { confirmImageLocation, featureIcon, featureImage, uploadImageToFeature } from 'maplibre/feature'
9-
import { renderGeoJSONLayer } from 'maplibre/layers/geojson'
10-
import { getFeature, getLayer } from 'maplibre/layers/layers'
9+
import { getFeature, getLayer, renderLayer } from 'maplibre/layers/layers'
1110
import { featureColor, featureOutlineColor } from 'maplibre/styles/styles'
1211
import { addUndoState } from 'maplibre/undo'
1312

@@ -41,7 +40,7 @@ export default class extends Controller {
4140
document.querySelector('#feature-edit-raw .error').innerHTML = ''
4241
try {
4342
feature.properties = JSON.parse(document.querySelector('#feature-edit-raw textarea').value)
44-
renderGeoJSONLayer(this.layerIdValue, true)
43+
renderLayer(this.layerIdValue, true)
4544
mapChannel.send_message('update_feature', feature)
4645
} catch (error) {
4746
console.error('Error updating feature:', error.message)
@@ -56,7 +55,7 @@ export default class extends Controller {
5655
feature.properties.title = title
5756
if (document.querySelector('#feature-show-title-on-map')?.checked) {
5857
feature.properties.label = title
59-
renderGeoJSONLayer(this.layerIdValue, false)
58+
renderLayer(this.layerIdValue, false)
6059
}
6160
document.querySelector('#feature-title').textContent = title
6261
functions.debounce(() => { this.saveFeature() }, 'title')
@@ -70,7 +69,7 @@ export default class extends Controller {
7069
} else {
7170
delete feature.properties.label
7271
}
73-
renderGeoJSONLayer(this.layerIdValue, false)
72+
renderLayer(this.layerIdValue, false)
7473
functions.debounce(() => { this.saveFeature() }, 'show-title-on-map', 1000)
7574
}
7675

@@ -86,7 +85,7 @@ export default class extends Controller {
8685
}
8786
feature.properties[propertyName] = value
8887
draw.setFeatureProperty(this.featureIdValue, propertyName, value)
89-
renderGeoJSONLayer(this.layerIdValue, true)
88+
renderLayer(this.layerIdValue, true)
9089
}
9190

9291
// called as preview on slider change
@@ -134,15 +133,15 @@ export default class extends Controller {
134133
document.querySelector('#stroke-color').removeAttribute('disabled')
135134
}
136135
feature.properties.stroke = color
137-
renderGeoJSONLayer(this.layerIdValue, true)
136+
renderLayer(this.layerIdValue, true)
138137
}
139138

140139
updateFillColor () {
141140
const feature = this.getEditFeature()
142141
const color = document.querySelector('#fill-color').value
143142
if (feature.geometry.type === 'Polygon' || feature.geometry.type === 'MultiPolygon') { feature.properties.fill = color }
144143
if (feature.geometry.type === 'Point') { feature.properties['marker-color'] = color }
145-
renderGeoJSONLayer(this.layerIdValue, true)
144+
renderLayer(this.layerIdValue, true)
146145
}
147146

148147
updateFillColorTransparent () {
@@ -158,7 +157,7 @@ export default class extends Controller {
158157
}
159158
if (feature.geometry.type === 'Polygon' || feature.geometry.type === 'MultiPolygon') { feature.properties.fill = color }
160159
if (feature.geometry.type === 'Point') { feature.properties['marker-color'] = color }
161-
renderGeoJSONLayer(this.layerIdValue, true)
160+
renderLayer(this.layerIdValue, true)
162161
}
163162

164163
updateShowKmMarkers () {
@@ -170,7 +169,7 @@ export default class extends Controller {
170169
delete feature.properties['show-km-markers']
171170
delete feature.properties['stroke-image-url']
172171
}
173-
renderGeoJSONLayer(this.layerIdValue, true)
172+
renderLayer(this.layerIdValue, true)
174173
}
175174

176175
updateMarkerSymbol () {
@@ -183,7 +182,7 @@ export default class extends Controller {
183182
// draw layer feature properties aren't getting updated by draw.set()
184183
draw.setFeatureProperty(this.featureIdValue, 'marker-symbol', symbol)
185184
functions.e('.feature-symbol', e => { e.innerHTML = featureIcon(feature) })
186-
renderGeoJSONLayer(this.layerIdValue, true)
185+
renderLayer(this.layerIdValue, true)
187186
}
188187

189188
async updateMarkerImage () {
@@ -205,7 +204,7 @@ export default class extends Controller {
205204
feature.geometry.coordinates = imageLocation
206205
flyToFeature(feature)
207206
}
208-
renderGeoJSONLayer(this.layerIdValue, true)
207+
renderLayer(this.layerIdValue, true)
209208
this.saveFeature()
210209
})
211210
}

app/javascript/controllers/map/context_menu_controller.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ import { mapChannel } from 'channels/map_channel'
33
import { status } from 'helpers/status'
44
import * as functions from 'helpers/functions'
55
import { hideContextMenu } from 'maplibre/controls/context_menu'
6-
import { getFeature } from 'maplibre/layers/layers'
6+
import { getFeature, renderLayers } from 'maplibre/layers/layers'
77
import { addFeature } from 'maplibre/map'
88
import { addUndoState } from 'maplibre/undo'
9-
import { renderGeoJSONLayers } from 'maplibre/layers/geojson'
109

1110
export default class extends Controller {
1211

@@ -18,7 +17,7 @@ export default class extends Controller {
1817
addUndoState('Feature update', feature)
1918
if (feature.geometry.type === 'LineString') { feature.geometry.coordinates.splice(vertexIndex, 1) }
2019
if (feature.geometry.type === 'Polygon') { feature.geometry.coordinates[0].splice(vertexIndex, 1) }
21-
renderGeoJSONLayers(true)
20+
renderLayers('geojson', true)
2221
mapChannel.send_message('update_feature', { ...feature })
2322
status('Point deleted')
2423
hideContextMenu()
@@ -36,7 +35,7 @@ export default class extends Controller {
3635
// Keep original feature, shorten it to the first segment
3736
addUndoState('Feature update', feature)
3837
feature.geometry.coordinates = firstCoords
39-
renderGeoJSONLayers(true)
38+
renderLayers('geojson', true)
4039
mapChannel.send_message('update_feature', { ...feature })
4140

4241
const secondFeature = {

app/javascript/controllers/map/layers_controller.js

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ import { status } from 'helpers/status'
66
import { flyToFeature } from 'maplibre/animations'
77
import { initLayersModal } from 'maplibre/controls/shared'
88
import { confirmImageLocation, uploadImageToFeature } from 'maplibre/feature'
9-
import { renderGeoJSONLayer } from 'maplibre/layers/geojson'
10-
import { initializeLayerSources, initializeLayerStyles, layers, loadAllLayerData, loadLayerData } from 'maplibre/layers/layers'
11-
import { initializeOverpassLayers } from 'maplibre/layers/overpass'
9+
import { createLayerInstance } from 'maplibre/layers/factory'
10+
import { initializeLayerSources, initializeLayerStyles, layers, loadAllLayerData, loadLayerData, renderLayer } from 'maplibre/layers/layers'
1211
import { queries } from 'maplibre/layers/queries'
1312
import { map, mapProperties, removeGeoJSONSource, setLayerVisibility, upsert } from 'maplibre/map'
1413

@@ -111,7 +110,7 @@ export default class extends Controller {
111110
uploadImageToFeature(file, feature).then( () => {
112111
upsert(feature)
113112
// redraw first geojson layer
114-
renderGeoJSONLayer(layers.find(l => l.type === 'geojson').id)
113+
renderLayer(layers.find(l => l.type === 'geojson').id)
115114
mapChannel.send_message('new_feature', { ...feature })
116115
status('Added image')
117116
flyToFeature(feature)
@@ -159,10 +158,9 @@ export default class extends Controller {
159158
layer["cluster"] = clustered
160159
layer["heatmap"] = layer.query.includes("heatmap=true")
161160
event.target.closest('.layer-item').querySelector('.layer-name').innerHTML = layer.name
162-
const { geojson: _geojson, ...sendLayer } = layer
163-
mapChannel.send_message('update_layer', sendLayer)
161+
mapChannel.send_message('update_layer', layer.toJSON())
164162
event.target.closest('.layer-item').querySelector('.reload-icon').classList.add('layer-refresh-animate')
165-
initializeOverpassLayers(layerId)
163+
layer.initialize().then(() => { initLayersModal() })
166164
}
167165

168166
refreshLayer (event) {
@@ -209,7 +207,7 @@ export default class extends Controller {
209207
const wasVisible = layer.show !== false
210208
layer.show = !wasVisible
211209

212-
setLayerVisibility(layer.type + '-source-' + layerId, layer.show)
210+
setLayerVisibility(layer.sourceId, layer.show)
213211

214212
// update UI (both desktop and mobile visibility buttons)
215213
layerElement.querySelectorAll('button.layer-visibility i, button.layer-visibility-mobile i').forEach(icon => {
@@ -240,8 +238,7 @@ export default class extends Controller {
240238

241239
// sync to server only in rw mode
242240
if (window.gon.map_mode === "rw") {
243-
const { geojson: _geojson, ...sendLayer } = layer
244-
mapChannel.send_message('update_layer', sendLayer)
241+
mapChannel.send_message('update_layer', layer.toJSON())
245242
}
246243
}
247244

@@ -266,20 +263,21 @@ export default class extends Controller {
266263
createLayer(type, name, query) {
267264
let layerId = functions.featureId()
268265
// must match server attribute order, for proper comparison in map_channel
269-
let layer = { "id": layerId, "type": type, "name": name, "heatmap": false, "cluster": true, "show": true}
266+
let layerData = { "id": layerId, "type": type, "name": name, "heatmap": false, "cluster": true, "show": true}
270267
if (type == 'overpass') {
271-
layer["query"] = query
268+
layerData["query"] = query
272269
// TODO: move cluster + heatmap to layer checkboxes
273-
const clustered = !layer.query.includes("heatmap=true") &&
274-
!layer.query.includes("cluster=false") &&
275-
!layer.query.includes("geom") // clustering breaks lines & geometries
276-
layer["cluster"] = clustered
277-
layer["heatmap"] = layer.query.includes("heatmap=true")
270+
const clustered = !layerData.query.includes("heatmap=true") &&
271+
!layerData.query.includes("cluster=false") &&
272+
!layerData.query.includes("geom") // clustering breaks lines & geometries
273+
layerData["cluster"] = clustered
274+
layerData["heatmap"] = layerData.query.includes("heatmap=true")
278275
}
276+
let layer = createLayerInstance(layerData)
279277
layers.push(layer)
280278
initializeLayerSources(layerId)
281279
initializeLayerStyles(layerId)
282-
mapChannel.send_message('new_layer', layer)
280+
mapChannel.send_message('new_layer', layerData)
283281
initLayersModal()
284282
document.querySelector('#layer-list-' + layerId + ' .reload-icon').classList.add('layer-refresh-animate')
285283
return layerId
@@ -291,12 +289,10 @@ export default class extends Controller {
291289
dom.closeTooltips()
292290
const layerElement = event.target.closest('.layer-item')
293291
const layerId = layerElement.getAttribute('data-layer-id')
294-
const layerType = layerElement.getAttribute('data-layer-type')
295292
const layer = layers.find(f => f.id === layerId)
296-
const { geojson: _geojson, ...sendLayer } = layer
297293
layers.splice(layers.indexOf(layer), 1)
298-
removeGeoJSONSource(layerType + '-source-' + layerId)
299-
mapChannel.send_message('delete_layer', sendLayer)
294+
removeGeoJSONSource(layer.sourceId)
295+
mapChannel.send_message('delete_layer', layer.toJSON())
300296
initLayersModal()
301297
}
302298
}

app/javascript/maplibre/animations.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import * as functions from 'helpers/functions'
77
import { status } from 'helpers/status'
88
import { resetControls } from 'maplibre/controls/shared'
99
import { highlightFeature } from 'maplibre/feature'
10-
import { renderGeoJSONLayers } from 'maplibre/layers/geojson'
11-
import { getFeatureSource } from 'maplibre/layers/layers'
10+
import { getFeatureSource, renderLayers } from 'maplibre/layers/layers'
1211
import { map, mapProperties } from 'maplibre/map'
1312

1413
export class AnimationManager {
@@ -51,7 +50,7 @@ export class AnimatePointAnimation extends AnimationManager {
5150
start[1] + (end[1] - start[1]) * progress
5251
]
5352
feature.geometry.coordinates = newCoordinates
54-
renderGeoJSONLayers(false)
53+
renderLayers('geojson', false)
5554
if (progress < 1) { this.animationId = requestAnimationFrame(animate) }
5655
}
5756
this.animationId = requestAnimationFrame(animate)
@@ -94,7 +93,7 @@ export class AnimateLineAnimation extends AnimationManager {
9493
// console.log("Frame #" + _frame + ", distance: " + distance + ", coord: " + coordinate)
9594

9695
line.geometry.coordinates.push(coordinate)
97-
renderGeoJSONLayers(false)
96+
renderLayers('geojson', false)
9897

9998
// Update camera position
10099
if (follow) { map.jumpTo({ center: coordinate }) }
@@ -125,7 +124,7 @@ export class AnimatePolygonAnimation extends AnimationManager {
125124
const progress = counter / steps
126125
polygon.properties['fill-extrusion-height'] = progress * height
127126
// console.log('New height: ' + polygon.properties['fill-extrusion-height'])
128-
renderGeoJSONLayers(false)
127+
renderLayers('geojson', false)
129128

130129
counter++
131130

@@ -137,7 +136,7 @@ export class AnimatePolygonAnimation extends AnimationManager {
137136
}
138137

139138
polygon.properties['fill-extrusion-height'] = 0
140-
renderGeoJSONLayers(true)
139+
renderLayers('geojson', true)
141140
this.animationId = requestAnimationFrame(animate)
142141
}
143142
}

app/javascript/maplibre/edit.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import { status } from 'helpers/status';
66
import { disableEditControls, enableEditControls, initializeEditControls } from 'maplibre/controls/edit';
77
import { initializeDefaultControls, resetControls } from 'maplibre/controls/shared';
88
import { highlightFeature } from 'maplibre/feature';
9-
import { renderGeoJSONLayers } from 'maplibre/layers/geojson';
10-
import { getFeature, hasFeatures, layers } from 'maplibre/layers/layers';
9+
import { getFeature, hasFeatures, initializeLayers, layers, renderLayers } from 'maplibre/layers/layers';
1110
import { addFeature, destroyFeature, map, mapProperties } from 'maplibre/map';
1211
import { getRouteElevation, getRouteUpdate } from 'maplibre/routing/openrouteservice';
1312
import { initDirections, resetDirections } from 'maplibre/routing/osrm';
@@ -75,15 +74,13 @@ export async function initializeEditMode () {
7574
initializeEditControls()
7675
initializeDefaultControls()
7776

78-
// Show map settings modal on untouched map
77+
// Show map settings modal on untouched map and handle URL feature selection
7978
map.once('load', async function (_e) {
80-
if (!layers) { await functions.waitForEvent(map, 'layers.load') }
79+
if (!layers) { await initializeLayers() }
8180
if (!mapProperties.name && !hasFeatures('geojson') && !layers?.filter(l => l.type !== 'geojson').length) {
8281
functions.e('.maplibregl-ctrl-map', e => { e.click() })
8382
}
84-
})
8583

86-
map.on('geojson.load', function (_e) {
8784
const urlFeatureId = new URLSearchParams(window.location.search).get('f')
8885
const feature = getFeature(urlFeatureId, 'geojson')
8986
if (feature) { map.fire('draw.selectionchange', {features: [feature]}) }
@@ -256,7 +253,7 @@ function handleCreate (e) {
256253
addUndoState('Feature added', feature)
257254
// redraw if the painted feature was changed in this method
258255
if (mode === 'directions_car' || mode === 'directions_bike' || mode === 'directions_foot' || mode === 'draw_paint_mode') {
259-
renderGeoJSONLayers(false)
256+
renderLayers('geojson', false)
260257
}
261258
mapChannel.send_message('new_feature', feature)
262259
if (feature.geometry.type === 'LineString') { updateElevation(feature) }
@@ -297,7 +294,7 @@ async function handleUpdate (e) {
297294

298295
status('Feature ' + feature.id + ' changed')
299296
geojsonFeature.geometry = feature.geometry
300-
renderGeoJSONLayers(false)
297+
renderLayers('geojson', false)
301298

302299
if (feature.geometry.type === 'LineString') {
303300
// gets also triggered on failure

0 commit comments

Comments
 (0)