Skip to content

Commit 69460b9

Browse files
authored
Merge pull request #680 from mapforge-org/add_layer_visibility_toggle
Add visibility toggle for layers
2 parents 7c292fa + bcc3e78 commit 69460b9

14 files changed

Lines changed: 178 additions & 21 deletions

File tree

app/channels/map_channel.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def map_atts(data)
9595
end
9696

9797
def layer_atts(data)
98-
data.slice("id", "type", "name", "query", "heatmap", "cluster")
98+
data.slice("id", "type", "name", "query", "heatmap", "cluster", "show")
9999
end
100100

101101
# load map with write access

app/javascript/channels/map_channel.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import consumer from 'channels/consumer'
2+
import { initializeLayerStyles, layers, loadLayerDefinitions } from 'maplibre/layers/layers'
23
import {
3-
upsert, destroyFeature, setBackgroundMapLayer, mapProperties,
4-
initializeMaplibreProperties, map,
5-
reloadMapProperties } from 'maplibre/map'
6-
import { layers, initializeLayerStyles, loadLayerDefinitions } from 'maplibre/layers/layers'
4+
destroyFeature,
5+
initializeMaplibreProperties, map,
6+
mapProperties,
7+
reloadMapProperties,
8+
setBackgroundMapLayer,
9+
setLayerVisibility,
10+
upsert
11+
} from 'maplibre/map'
712

813

914
export let mapChannel
@@ -99,9 +104,13 @@ export function initializeSocket () {
99104
// Remove geojson key before comparison
100105
const { ['geojson']: _, ...layerDef } = layers[index]
101106
if (JSON.stringify(layerDef) !== JSON.stringify(data.layer)) {
107+
// preserve geojson data when updating layer definition
108+
const geojson = layers[index].geojson
102109
layers[index] = data.layer
110+
if (geojson) { layers[index].geojson = geojson }
103111
console.log('Layer updated on server, reloading layer styles', data.layer)
104112
initializeLayerStyles(data.layer.id)
113+
setLayerVisibility(data.layer.type + '-source-' + data.layer.id, data.layer.show !== false)
105114
}
106115
} else {
107116
layers.push(data.layer)

app/javascript/controllers/map/layers_controller.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Controller } from '@hotwired/stimulus'
22
import { mapChannel } from 'channels/map_channel'
3-
import { map, upsert, mapProperties, removeGeoJSONSource } from 'maplibre/map'
3+
import { map, upsert, mapProperties, removeGeoJSONSource, setLayerVisibility } from 'maplibre/map'
44
import { initLayersModal } from 'maplibre/controls/shared'
55
import { uploadImageToFeature, confirmImageLocation } from 'maplibre/feature'
66
import { status } from 'helpers/status'
@@ -200,6 +200,39 @@ export default class extends Controller {
200200
list.classList.toggle('hidden')
201201
}
202202

203+
toggleLayerVisibility (event) {
204+
event.preventDefault()
205+
dom.closeTooltips()
206+
const layerElement = event.target.closest('.layer-item')
207+
const layerId = layerElement.getAttribute('data-layer-id')
208+
const layer = layers.find(l => l.id === layerId)
209+
const wasVisible = layer.show !== false
210+
layer.show = !wasVisible
211+
212+
setLayerVisibility(layer.type + '-source-' + layerId, layer.show)
213+
214+
// update UI
215+
const icon = layerElement.querySelector('button.layer-visibility i')
216+
if (layer.show) {
217+
icon.classList.replace('bi-eye-slash', 'bi-eye')
218+
layerElement.classList.remove('opacity-50')
219+
} else {
220+
icon.classList.replace('bi-eye', 'bi-eye-slash')
221+
layerElement.classList.add('opacity-50')
222+
}
223+
224+
// when showing: initialize styles (and load data for overpass/wikipedia if needed)
225+
if (layer.show) {
226+
initializeLayerStyles(layerId)
227+
}
228+
229+
// sync to server only in rw mode
230+
if (window.gon.map_mode === "rw") {
231+
const { geojson: _geojson, ...sendLayer } = layer
232+
mapChannel.send_message('update_layer', sendLayer)
233+
}
234+
}
235+
203236
createWikipediaLayer() {
204237
this.createLayer('wikipedia', 'Wikipedia', '')
205238
}
@@ -221,7 +254,7 @@ export default class extends Controller {
221254
createLayer(type, name, query) {
222255
let layerId = functions.featureId()
223256
// must match server attribute order, for proper comparison in map_channel
224-
let layer = { "id": layerId, "type": type, "name": name, "heatmap": false, "cluster": true}
257+
let layer = { "id": layerId, "type": type, "name": name, "heatmap": false, "cluster": true, "show": true}
225258
if (type == 'overpass') {
226259
layer["query"] = query
227260
// TODO: move cluster + heatmap to layer checkboxes

app/javascript/helpers/dom.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ export function initTooltips (root = document) {
5757
}
5858
}
5959

60-
export function closeTooltips () {
61-
functions.e('[data-toggle="tooltip"]', e => {
62-
const tooltip = bootstrap.Tooltip.getInstance(e);
63-
if (tooltip) tooltip.dispose()
64-
})
60+
export function closeTooltips (root = document) {
61+
root.querySelectorAll('[data-toggle="tooltip"]').forEach(e => {
62+
const tooltip = bootstrap.Tooltip.getInstance(e)
63+
if (tooltip) tooltip.dispose()
64+
})
6565
}
6666

6767
export function scrollToId(elementId) {

app/javascript/maplibre/controls/shared.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ export function initSettingsModal () {
175175
// create the list of layers + features
176176
export function initLayersModal () {
177177
functions.e('#layers', e => {
178+
dom.closeTooltips(e)
178179
e.innerHTML = ''
179180
const template = document.querySelector('#layer-item-template')
180181
layers.forEach(layer => {
@@ -200,6 +201,13 @@ export function initLayersModal () {
200201
featureCount.textContent = '(' + features.length + ')'
201202
head.parentNode.insertBefore(featureCount, head.nextSibling)
202203
e.appendChild(layerElement)
204+
// visibility toggle for all layers
205+
const visBtn = layerElement.querySelector('button.layer-visibility')
206+
visBtn.classList.remove('hidden')
207+
if (layer.show === false) {
208+
visBtn.querySelector('i').classList.replace('bi-eye', 'bi-eye-slash')
209+
layerElement.classList.add('opacity-50')
210+
}
203211
if (layer.type !== 'geojson') {
204212
layerElement.querySelector('button.layer-refresh').classList.remove('hidden')
205213
layerElement.querySelector('button.layer-delete').classList.remove('hidden')

app/javascript/maplibre/layers/geojson.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import { lineString } from "@turf/helpers"
44
import { length } from "@turf/length"
55
import { draw, select } from 'maplibre/edit'
66
import { getFeature, getFeatures, layers } from 'maplibre/layers/layers'
7-
import { map, mapProperties } from 'maplibre/map'
7+
import { map, mapProperties, removeStyleLayers } from 'maplibre/map'
88
import { defaultLineWidth, featureColor, initializeClusterStyles, initializeViewStyles, labelFont, setSource, styles } from 'maplibre/styles/styles'
99

1010
export function initializeGeoJSONLayers(id = null) {
1111
// console.log('Initializing geojson layers')
12-
let initLayers = layers.filter(l => l.type === 'geojson')
12+
let initLayers = layers.filter(l => l.type === 'geojson' && l.show !== false)
1313
if (id) { initLayers = initLayers.filter(l => l.id === id) }
1414

1515
initLayers.forEach((layer) => {
@@ -188,6 +188,7 @@ export function kmMarkerStyles (_id) {
188188
}
189189

190190
export function initializeKmMarkerStyles(id) {
191+
removeStyleLayers('km-marker-source-' + id)
191192
kmMarkerStyles(id).forEach(style => {
192193
style = setSource (style, 'km-marker-source-' + id)
193194
map.addLayer(style)

app/javascript/maplibre/layers/layers.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export function initializeLayerSources(id = null) {
3737
initLayers.forEach((layer) => {
3838
// drop cluster when heatmap is set
3939
const cluster = !!layer.cluster && !layer.heatmap
40-
console.log('Adding source for layer', layer.type, layer.id, cluster)
40+
console.log('Adding source for layer', layer)
4141
addGeoJSONSource(layer.type + '-source-' + layer.id, cluster)
4242
// add one source for km markers per geojson layer
4343
if (layer.type === 'geojson') {
@@ -66,6 +66,10 @@ export async function initializeLayerStyles(id = null) {
6666
// triggered by layer reload in the layers modal
6767
export function loadLayerData(id) {
6868
let layer = layers.find(l => l.id === id)
69+
if (layer.show === false) {
70+
console.log("Skipped loading data for not shown layer", layer)
71+
return Promise.resolve()
72+
}
6973
// geojson layers are loaded in loadLayerDefinitions
7074
if (layer.type === 'wikipedia') {
7175
return loadWikipediaLayer(layer.id)

app/javascript/maplibre/layers/overpass.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { map } from 'maplibre/map'
77
import { initializeClusterStyles, initializeViewStyles } from 'maplibre/styles/styles'
88

99
export function initializeOverpassLayers(id = null) {
10-
let initLayers = layers.filter(l => l.type === 'overpass')
10+
let initLayers = layers.filter(l => l.type === 'overpass' && l.show !== false)
1111
if (id) { initLayers = initLayers.filter(l => l.id === id) }
1212
return initLayers.map((layer) => {
1313
const clustered = !layer.query.includes("heatmap=true") &&

app/javascript/maplibre/layers/wikipedia.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { initializeClusterStyles, initializeViewStyles } from 'maplibre/styles/s
77

88
export function initializeWikipediaLayers(id = null) {
99
// console.log('Initializing Wikipedia layers')
10-
let initLayers = layers.filter(l => l.type === 'wikipedia')
10+
let initLayers = layers.filter(l => l.type === 'wikipedia' && l.show !== false)
1111
if (id) { initLayers = initLayers.filter(l => l.id === id) }
1212

1313
return initLayers.map((layer) => {

app/javascript/maplibre/map.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,21 @@ export function removeStyleLayers(sourceName) {
216216
}
217217
}
218218

219+
export function setLayerVisibility(sourceName, visible) {
220+
if (map.getStyle && map.getStyle().layers) {
221+
const sources = [sourceName]
222+
// geojson layers have a companion km-marker source
223+
if (sourceName.startsWith('geojson-source-')) {
224+
sources.push(sourceName.replace('geojson-source-', 'km-marker-source-'))
225+
}
226+
map.getStyle().layers
227+
.filter(l => sources.includes(l.source))
228+
.forEach(l => {
229+
if (map.getLayer(l.id)) map.setLayoutProperty(l.id, 'visibility', visible ? 'visible' : 'none')
230+
})
231+
}
232+
}
233+
219234
export function removeGeoJSONSource(sourceName) {
220235
removeStyleLayers(sourceName)
221236
if (map.getSource(sourceName)) {

0 commit comments

Comments
 (0)