forked from MapComplete/MapComplete
		
	UX: fix zooming of minimaps, fix #1892
This commit is contained in:
		
							parent
							
								
									3968a1e841
								
							
						
					
					
						commit
						e9b7df6d2d
					
				
					 2 changed files with 62 additions and 51 deletions
				
			
		| 
						 | 
					@ -200,7 +200,7 @@ class LineRenderingLayer {
 | 
				
			||||||
        "lineCap",
 | 
					        "lineCap",
 | 
				
			||||||
        "offset",
 | 
					        "offset",
 | 
				
			||||||
        "fill",
 | 
					        "fill",
 | 
				
			||||||
        "fillColor",
 | 
					        "fillColor"
 | 
				
			||||||
    ] as const
 | 
					    ] as const
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static readonly lineConfigKeysColor = ["color", "fillColor"] as const
 | 
					    private static readonly lineConfigKeysColor = ["color", "fillColor"] as const
 | 
				
			||||||
| 
						 | 
					@ -264,8 +264,8 @@ class LineRenderingLayer {
 | 
				
			||||||
                        "icon-rotation-alignment": "map",
 | 
					                        "icon-rotation-alignment": "map",
 | 
				
			||||||
                        "icon-pitch-alignment": "map",
 | 
					                        "icon-pitch-alignment": "map",
 | 
				
			||||||
                        "icon-image": imgId,
 | 
					                        "icon-image": imgId,
 | 
				
			||||||
                        "icon-size": 0.055,
 | 
					                        "icon-size": 0.055
 | 
				
			||||||
                    },
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                const filter = img.if?.asMapboxExpression()
 | 
					                const filter = img.if?.asMapboxExpression()
 | 
				
			||||||
                if (filter) {
 | 
					                if (filter) {
 | 
				
			||||||
| 
						 | 
					@ -338,9 +338,9 @@ class LineRenderingLayer {
 | 
				
			||||||
                    type: "geojson",
 | 
					                    type: "geojson",
 | 
				
			||||||
                    data: {
 | 
					                    data: {
 | 
				
			||||||
                        type: "FeatureCollection",
 | 
					                        type: "FeatureCollection",
 | 
				
			||||||
                        features,
 | 
					                        features
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    promoteId: "id",
 | 
					                    promoteId: "id"
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                const linelayer = this._layername + "_line"
 | 
					                const linelayer = this._layername + "_line"
 | 
				
			||||||
                const layer: AddLayerObject = {
 | 
					                const layer: AddLayerObject = {
 | 
				
			||||||
| 
						 | 
					@ -351,11 +351,11 @@ class LineRenderingLayer {
 | 
				
			||||||
                        "line-color": ["feature-state", "color"],
 | 
					                        "line-color": ["feature-state", "color"],
 | 
				
			||||||
                        "line-opacity": ["feature-state", "color-opacity"],
 | 
					                        "line-opacity": ["feature-state", "color-opacity"],
 | 
				
			||||||
                        "line-width": ["feature-state", "width"],
 | 
					                        "line-width": ["feature-state", "width"],
 | 
				
			||||||
                        "line-offset": ["feature-state", "offset"],
 | 
					                        "line-offset": ["feature-state", "offset"]
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    layout: {
 | 
					                    layout: {
 | 
				
			||||||
                        "line-cap": "round",
 | 
					                        "line-cap": "round"
 | 
				
			||||||
                    },
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (this._config.dashArray) {
 | 
					                if (this._config.dashArray) {
 | 
				
			||||||
                    layer.paint["line-dasharray"] =
 | 
					                    layer.paint["line-dasharray"] =
 | 
				
			||||||
| 
						 | 
					@ -393,8 +393,8 @@ class LineRenderingLayer {
 | 
				
			||||||
                    layout: {},
 | 
					                    layout: {},
 | 
				
			||||||
                    paint: {
 | 
					                    paint: {
 | 
				
			||||||
                        "fill-color": ["feature-state", "fillColor"],
 | 
					                        "fill-color": ["feature-state", "fillColor"],
 | 
				
			||||||
                        "fill-opacity": ["feature-state", "fillColor-opacity"],
 | 
					                        "fill-opacity": ["feature-state", "fillColor-opacity"]
 | 
				
			||||||
                    },
 | 
					                    }
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                if (this._onClick) {
 | 
					                if (this._onClick) {
 | 
				
			||||||
                    map.on("click", polylayer, (e) => {
 | 
					                    map.on("click", polylayer, (e) => {
 | 
				
			||||||
| 
						 | 
					@ -425,7 +425,7 @@ class LineRenderingLayer {
 | 
				
			||||||
                this.currentSourceData = features
 | 
					                this.currentSourceData = features
 | 
				
			||||||
                src.setData({
 | 
					                src.setData({
 | 
				
			||||||
                    type: "FeatureCollection",
 | 
					                    type: "FeatureCollection",
 | 
				
			||||||
                    features: this.currentSourceData,
 | 
					                    features: this.currentSourceData
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -494,8 +494,7 @@ export default class ShowDataLayer {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        this._options = options
 | 
					        this._options = options
 | 
				
			||||||
        const self = this
 | 
					        this.onDestroy.push(map.addCallbackAndRunD((map) => this.initDrawFeatures(map)))
 | 
				
			||||||
        this.onDestroy.push(map.addCallbackAndRunD((map) => self.initDrawFeatures(map)))
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static showMultipleLayers(
 | 
					    public static showMultipleLayers(
 | 
				
			||||||
| 
						 | 
					@ -509,14 +508,24 @@ export default class ShowDataLayer {
 | 
				
			||||||
                layers.filter((l) => l.source !== null).map((l) => new FilteredLayer(l)),
 | 
					                layers.filter((l) => l.source !== null).map((l) => new FilteredLayer(l)),
 | 
				
			||||||
                features,
 | 
					                features,
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    constructStore: (features, layer) => new SimpleFeatureSource(layer, features),
 | 
					                    constructStore: (features, layer) => new SimpleFeatureSource(layer, features)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					        if (options?.zoomToFeatures) {
 | 
				
			||||||
 | 
					            options.zoomToFeatures = false
 | 
				
			||||||
 | 
					            features.features.addCallbackD(features => {
 | 
				
			||||||
 | 
					                ShowDataLayer.zoomToCurrentFeatures(mlmap.data, features)
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            mlmap.addCallbackD(map => {
 | 
				
			||||||
 | 
					                ShowDataLayer.zoomToCurrentFeatures(map, features.features.data)
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        perLayer.forEach((fs) => {
 | 
					        perLayer.forEach((fs) => {
 | 
				
			||||||
            new ShowDataLayer(mlmap, {
 | 
					            new ShowDataLayer(mlmap, {
 | 
				
			||||||
                layer: fs.layer.layerDef,
 | 
					                layer: fs.layer.layerDef,
 | 
				
			||||||
                features: fs,
 | 
					                features: fs,
 | 
				
			||||||
                ...(options ?? {}),
 | 
					                ...(options ?? {})
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -529,26 +538,31 @@ export default class ShowDataLayer {
 | 
				
			||||||
        return new ShowDataLayer(map, {
 | 
					        return new ShowDataLayer(map, {
 | 
				
			||||||
            layer: ShowDataLayer.rangeLayer,
 | 
					            layer: ShowDataLayer.rangeLayer,
 | 
				
			||||||
            features,
 | 
					            features,
 | 
				
			||||||
            doShowLayer,
 | 
					            doShowLayer
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public destruct() {}
 | 
					    public destruct() {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private zoomToCurrentFeatures(map: MlMap) {
 | 
					    private static zoomToCurrentFeatures(map: MlMap, features: Feature[]) {
 | 
				
			||||||
        if (this._options.zoomToFeatures) {
 | 
					        if (!features || !map || features.length == 0) {
 | 
				
			||||||
            const features = this._options.features.features.data
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        const bbox = BBox.bboxAroundAll(features.map(BBox.get))
 | 
					        const bbox = BBox.bboxAroundAll(features.map(BBox.get))
 | 
				
			||||||
 | 
					        console.log("Zooming to features", bbox.asGeoJson())
 | 
				
			||||||
 | 
					        window.requestAnimationFrame(() => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        map.resize()
 | 
					        map.resize()
 | 
				
			||||||
        map.fitBounds(bbox.toLngLat(), {
 | 
					        map.fitBounds(bbox.toLngLat(), {
 | 
				
			||||||
            padding: { top: 10, bottom: 10, left: 10, right: 10 },
 | 
					            padding: { top: 10, bottom: 10, left: 10, right: 10 },
 | 
				
			||||||
                animate: false,
 | 
					            animate: false
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private initDrawFeatures(map: MlMap) {
 | 
					    private initDrawFeatures(map: MlMap) {
 | 
				
			||||||
        let { features, doShowLayer, fetchStore, selectedElement } = this._options
 | 
					        const { features, doShowLayer, fetchStore, selectedElement } = this._options
 | 
				
			||||||
        let onClick = this._options.onClick
 | 
					        let onClick = this._options.onClick
 | 
				
			||||||
        if (!onClick && selectedElement) {
 | 
					        if (!onClick && selectedElement) {
 | 
				
			||||||
            onClick =
 | 
					            onClick =
 | 
				
			||||||
| 
						 | 
					@ -587,6 +601,8 @@ export default class ShowDataLayer {
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        features.features.addCallbackAndRunD((_) => this.zoomToCurrentFeatures(map))
 | 
					        if (this._options.zoomToFeatures) {
 | 
				
			||||||
 | 
					            features.features.addCallbackAndRunD((features) => ShowDataLayer.zoomToCurrentFeatures(map, features))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,13 +17,13 @@ export class MinimapViz implements SpecialVisualization {
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            doc: "The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close",
 | 
					            doc: "The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close",
 | 
				
			||||||
            name: "zoomlevel",
 | 
					            name: "zoomlevel",
 | 
				
			||||||
            defaultValue: "18",
 | 
					            defaultValue: "18"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            doc: "(Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap. (Note: if the key is 'id', list interpration is disabled)",
 | 
					            doc: "(Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap. (Note: if the key is 'id', list interpration is disabled)",
 | 
				
			||||||
            name: "idKey",
 | 
					            name: "idKey",
 | 
				
			||||||
            defaultValue: "id",
 | 
					            defaultValue: "id"
 | 
				
			||||||
        },
 | 
					        }
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`"
 | 
					    example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,41 +78,36 @@ export class MinimapViz implements SpecialVisualization {
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const mlmap = new UIEventSource(undefined)
 | 
					        const mlmap = new UIEventSource(undefined)
 | 
				
			||||||
 | 
					        const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
 | 
				
			||||||
        const mla = new MapLibreAdaptor(mlmap, {
 | 
					        const mla = new MapLibreAdaptor(mlmap, {
 | 
				
			||||||
            rasterLayer: state.mapProperties.rasterLayer,
 | 
					            rasterLayer: state.mapProperties.rasterLayer,
 | 
				
			||||||
 | 
					            zoom: new UIEventSource<number>(18)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mla.maxzoom.setData(17)
 | 
					        mla.allowMoving.setData(false)
 | 
				
			||||||
        let zoom = 18
 | 
					        mla.allowZooming.setData(false)
 | 
				
			||||||
 | 
					        mla.location.setData({lon, lat})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (args[0]) {
 | 
					        if (args[0]) {
 | 
				
			||||||
            const parsed = Number(args[0])
 | 
					            const parsed = Number(args[0])
 | 
				
			||||||
            if (!isNaN(parsed) && parsed > 0 && parsed < 25) {
 | 
					            if (!isNaN(parsed) && parsed > 0 && parsed < 25) {
 | 
				
			||||||
                zoom = parsed
 | 
					                mla.zoom.setData(parsed)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        featuresToShow.addCallbackAndRunD((features) => {
 | 
					        mlmap.addCallbackAndRun(map => console.log("Map for minimap vis is now", map))
 | 
				
			||||||
            if (features.length === 0) {
 | 
					 | 
				
			||||||
                return
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            const bboxGeojson = GeoOperations.bbox({ features, type: "FeatureCollection" })
 | 
					 | 
				
			||||||
            const [lon, lat] = GeoOperations.centerpointCoordinates(bboxGeojson)
 | 
					 | 
				
			||||||
            mla.bounds.setData(BBox.get(bboxGeojson))
 | 
					 | 
				
			||||||
            mla.location.setData({ lon, lat })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        mla.zoom.setData(zoom)
 | 
					 | 
				
			||||||
        mla.allowMoving.setData(false)
 | 
					 | 
				
			||||||
        mla.allowZooming.setData(false)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ShowDataLayer.showMultipleLayers(
 | 
					        ShowDataLayer.showMultipleLayers(
 | 
				
			||||||
            mlmap,
 | 
					            mlmap,
 | 
				
			||||||
            new StaticFeatureSource(featuresToShow),
 | 
					            new StaticFeatureSource(featuresToShow),
 | 
				
			||||||
            state.layout.layers
 | 
					            state.layout.layers,
 | 
				
			||||||
 | 
					            {zoomToFeatures: true}
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return new SvelteUIElement(MaplibreMap, {
 | 
					        return new SvelteUIElement(MaplibreMap, {
 | 
				
			||||||
            interactive: false,
 | 
					            interactive: false,
 | 
				
			||||||
            map: mlmap,
 | 
					            map: mlmap,
 | 
				
			||||||
            mapProperties: mla,
 | 
					            mapProperties: mla
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
            .SetClass("h-40 rounded")
 | 
					            .SetClass("h-40 rounded")
 | 
				
			||||||
            .SetStyle("overflow: hidden; pointer-events: none;")
 | 
					            .SetStyle("overflow: hidden; pointer-events: none;")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue