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
				
			
		|  | @ -154,7 +154,7 @@ class PointRenderingLayer { | |||
| 
 | ||||
|         if (this._onClick) { | ||||
|             const self = this | ||||
|             el.addEventListener("click", function (ev) { | ||||
|             el.addEventListener("click", function(ev) { | ||||
|                 ev.preventDefault() | ||||
|                 self._onClick(feature) | ||||
|                 // Workaround to signal the MapLibreAdaptor to ignore this click
 | ||||
|  | @ -200,7 +200,7 @@ class LineRenderingLayer { | |||
|         "lineCap", | ||||
|         "offset", | ||||
|         "fill", | ||||
|         "fillColor", | ||||
|         "fillColor" | ||||
|     ] as const | ||||
| 
 | ||||
|     private static readonly lineConfigKeysColor = ["color", "fillColor"] as const | ||||
|  | @ -264,8 +264,8 @@ class LineRenderingLayer { | |||
|                         "icon-rotation-alignment": "map", | ||||
|                         "icon-pitch-alignment": "map", | ||||
|                         "icon-image": imgId, | ||||
|                         "icon-size": 0.055, | ||||
|                     }, | ||||
|                         "icon-size": 0.055 | ||||
|                     } | ||||
|                 } | ||||
|                 const filter = img.if?.asMapboxExpression() | ||||
|                 if (filter) { | ||||
|  | @ -338,9 +338,9 @@ class LineRenderingLayer { | |||
|                     type: "geojson", | ||||
|                     data: { | ||||
|                         type: "FeatureCollection", | ||||
|                         features, | ||||
|                         features | ||||
|                     }, | ||||
|                     promoteId: "id", | ||||
|                     promoteId: "id" | ||||
|                 }) | ||||
|                 const linelayer = this._layername + "_line" | ||||
|                 const layer: AddLayerObject = { | ||||
|  | @ -351,11 +351,11 @@ class LineRenderingLayer { | |||
|                         "line-color": ["feature-state", "color"], | ||||
|                         "line-opacity": ["feature-state", "color-opacity"], | ||||
|                         "line-width": ["feature-state", "width"], | ||||
|                         "line-offset": ["feature-state", "offset"], | ||||
|                         "line-offset": ["feature-state", "offset"] | ||||
|                     }, | ||||
|                     layout: { | ||||
|                         "line-cap": "round", | ||||
|                     }, | ||||
|                         "line-cap": "round" | ||||
|                     } | ||||
|                 } | ||||
|                 if (this._config.dashArray) { | ||||
|                     layer.paint["line-dasharray"] = | ||||
|  | @ -393,8 +393,8 @@ class LineRenderingLayer { | |||
|                     layout: {}, | ||||
|                     paint: { | ||||
|                         "fill-color": ["feature-state", "fillColor"], | ||||
|                         "fill-opacity": ["feature-state", "fillColor-opacity"], | ||||
|                     }, | ||||
|                         "fill-opacity": ["feature-state", "fillColor-opacity"] | ||||
|                     } | ||||
|                 }) | ||||
|                 if (this._onClick) { | ||||
|                     map.on("click", polylayer, (e) => { | ||||
|  | @ -425,7 +425,7 @@ class LineRenderingLayer { | |||
|                 this.currentSourceData = features | ||||
|                 src.setData({ | ||||
|                     type: "FeatureCollection", | ||||
|                     features: this.currentSourceData, | ||||
|                     features: this.currentSourceData | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|  | @ -494,8 +494,7 @@ export default class ShowDataLayer { | |||
|         } | ||||
|     ) { | ||||
|         this._options = options | ||||
|         const self = this | ||||
|         this.onDestroy.push(map.addCallbackAndRunD((map) => self.initDrawFeatures(map))) | ||||
|         this.onDestroy.push(map.addCallbackAndRunD((map) => this.initDrawFeatures(map))) | ||||
|     } | ||||
| 
 | ||||
|     public static showMultipleLayers( | ||||
|  | @ -509,14 +508,24 @@ export default class ShowDataLayer { | |||
|                 layers.filter((l) => l.source !== null).map((l) => new FilteredLayer(l)), | ||||
|                 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) => { | ||||
|             new ShowDataLayer(mlmap, { | ||||
|                 layer: fs.layer.layerDef, | ||||
|                 features: fs, | ||||
|                 ...(options ?? {}), | ||||
|                 ...(options ?? {}) | ||||
|             }) | ||||
|         }) | ||||
|     } | ||||
|  | @ -529,34 +538,39 @@ export default class ShowDataLayer { | |||
|         return new ShowDataLayer(map, { | ||||
|             layer: ShowDataLayer.rangeLayer, | ||||
|             features, | ||||
|             doShowLayer, | ||||
|             doShowLayer | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     public destruct() {} | ||||
|     public destruct() { | ||||
|     } | ||||
| 
 | ||||
|     private zoomToCurrentFeatures(map: MlMap) { | ||||
|         if (this._options.zoomToFeatures) { | ||||
|             const features = this._options.features.features.data | ||||
|             const bbox = BBox.bboxAroundAll(features.map(BBox.get)) | ||||
|             map.resize() | ||||
|             map.fitBounds(bbox.toLngLat(), { | ||||
|                 padding: { top: 10, bottom: 10, left: 10, right: 10 }, | ||||
|                 animate: false, | ||||
|             }) | ||||
|     private static zoomToCurrentFeatures(map: MlMap, features: Feature[]) { | ||||
|         if (!features || !map || features.length == 0) { | ||||
|             return | ||||
|         } | ||||
|         const bbox = BBox.bboxAroundAll(features.map(BBox.get)) | ||||
|         console.log("Zooming to features", bbox.asGeoJson()) | ||||
|         window.requestAnimationFrame(() => { | ||||
| 
 | ||||
|         map.resize() | ||||
|         map.fitBounds(bbox.toLngLat(), { | ||||
|             padding: { top: 10, bottom: 10, left: 10, right: 10 }, | ||||
|             animate: false | ||||
|         }) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private initDrawFeatures(map: MlMap) { | ||||
|         let { features, doShowLayer, fetchStore, selectedElement } = this._options | ||||
|         const { features, doShowLayer, fetchStore, selectedElement } = this._options | ||||
|         let onClick = this._options.onClick | ||||
|         if (!onClick && selectedElement) { | ||||
|             onClick = | ||||
|                 this._options.layer.title === undefined | ||||
|                     ? undefined | ||||
|                     : (feature: Feature) => { | ||||
|                           selectedElement?.setData(feature) | ||||
|                       } | ||||
|                         selectedElement?.setData(feature) | ||||
|                     } | ||||
|         } | ||||
|         if (this._options.drawLines !== false) { | ||||
|             for (let i = 0; i < this._options.layer.lineRendering.length; i++) { | ||||
|  | @ -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", | ||||
|             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)", | ||||
|             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}`" | ||||
| 
 | ||||
|  | @ -78,41 +78,36 @@ export class MinimapViz implements SpecialVisualization { | |||
|         ) | ||||
| 
 | ||||
|         const mlmap = new UIEventSource(undefined) | ||||
|         const [lon, lat] = GeoOperations.centerpointCoordinates(feature) | ||||
|         const mla = new MapLibreAdaptor(mlmap, { | ||||
|             rasterLayer: state.mapProperties.rasterLayer, | ||||
|             zoom: new UIEventSource<number>(18) | ||||
|         }) | ||||
| 
 | ||||
|         mla.maxzoom.setData(17) | ||||
|         let zoom = 18 | ||||
|         mla.allowMoving.setData(false) | ||||
|         mla.allowZooming.setData(false) | ||||
|         mla.location.setData({lon, lat}) | ||||
| 
 | ||||
| 
 | ||||
|         if (args[0]) { | ||||
|             const parsed = Number(args[0]) | ||||
|             if (!isNaN(parsed) && parsed > 0 && parsed < 25) { | ||||
|                 zoom = parsed | ||||
|                 mla.zoom.setData(parsed) | ||||
|             } | ||||
|         } | ||||
|         featuresToShow.addCallbackAndRunD((features) => { | ||||
|             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) | ||||
|         mlmap.addCallbackAndRun(map => console.log("Map for minimap vis is now", map)) | ||||
| 
 | ||||
|         ShowDataLayer.showMultipleLayers( | ||||
|             mlmap, | ||||
|             new StaticFeatureSource(featuresToShow), | ||||
|             state.layout.layers | ||||
|             state.layout.layers, | ||||
|             {zoomToFeatures: true} | ||||
|         ) | ||||
| 
 | ||||
|         return new SvelteUIElement(MaplibreMap, { | ||||
|             interactive: false, | ||||
|             map: mlmap, | ||||
|             mapProperties: mla, | ||||
|             mapProperties: mla | ||||
|         }) | ||||
|             .SetClass("h-40 rounded") | ||||
|             .SetStyle("overflow: hidden; pointer-events: none;") | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue