Feature: add possibility to show a scale on the map

This commit is contained in:
Pieter Vander Vennet 2024-09-12 01:53:47 +02:00
parent 0fdbf445be
commit 5a9ae3f104
6 changed files with 52 additions and 10 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "mapcomplete", "name": "mapcomplete",
"version": "0.46.5", "version": "0.46.6",
"repository": "https://github.com/pietervdvn/MapComplete", "repository": "https://github.com/pietervdvn/MapComplete",
"description": "A small website to edit OSM easily", "description": "A small website to edit OSM easily",
"bugs": "https://github.com/pietervdvn/MapComplete/issues", "bugs": "https://github.com/pietervdvn/MapComplete/issues",

View file

@ -69,6 +69,8 @@ export default class UserRelatedState {
"button" | "button_click_right" | "button_click" | "click" | "click_right" "button" | "button_click_right" | "button_click" | "click" | "click_right"
>("button_click_right") >("button_click_right")
public readonly showScale : UIEventSource<boolean>
/** /**
* Preferences as tags exposes many preferences and state properties as record. * Preferences as tags exposes many preferences and state properties as record.
* This is used to bridge the internal state with the usersettings.json layerconfig file * This is used to bridge the internal state with the usersettings.json layerconfig file
@ -123,6 +125,7 @@ export default class UserRelatedState {
documentation: "How adding a new feature is done", documentation: "How adding a new feature is done",
} }
) )
this.showScale = UIEventSource.asBoolean(this.osmConnection.GetPreference("preference-show-scale","false"))
this.imageLicense = this.osmConnection.GetPreference("pictures-license", "CC0", { this.imageLicense = this.osmConnection.GetPreference("pictures-license", "CC0", {
documentation: "The license under which new images are uploaded", documentation: "The license under which new images are uploaded",

View file

@ -22,6 +22,7 @@ export interface MapProperties {
readonly lastClickLocation: Store<{ lon: number; lat: number }> readonly lastClickLocation: Store<{ lon: number; lat: number }>
readonly allowZooming: UIEventSource<true | boolean> readonly allowZooming: UIEventSource<true | boolean>
readonly useTerrain: Store<boolean> readonly useTerrain: Store<boolean>
readonly showScale: UIEventSource<boolean>
/** /**
* Triggered when the user navigated by using the keyboard. * Triggered when the user navigated by using the keyboard.

View file

@ -889,6 +889,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
} }
}) })
}) })
this.userRelatedState.showScale.addCallbackAndRun(showScale => {
this.mapProperties.showScale.set(showScale)
})
new ThemeViewStateHashActor(this) new ThemeViewStateHashActor(this)
new MetaTagging(this) new MetaTagging(this)
new TitleHandler(this.selectedElement, this.featureProperties, this) new TitleHandler(this.selectedElement, this.featureProperties, this)

View file

@ -1,5 +1,5 @@
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource" import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
import maplibregl, { Map as MLMap, Map as MlMap, SourceSpecification } from "maplibre-gl" import maplibregl, { Map as MLMap, Map as MlMap, ScaleControl, SourceSpecification } from "maplibre-gl"
import { RasterLayerPolygon } from "../../Models/RasterLayers" import { RasterLayerPolygon } from "../../Models/RasterLayers"
import { Utils } from "../../Utils" import { Utils } from "../../Utils"
import { BBox } from "../../Logic/BBox" import { BBox } from "../../Logic/BBox"
@ -23,13 +23,13 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
"dragRotate", "dragRotate",
"dragPan", "dragPan",
"keyboard", "keyboard",
"touchZoomRotate", "touchZoomRotate"
] ]
private static maplibre_zoom_handlers = [ private static maplibre_zoom_handlers = [
"scrollZoom", "scrollZoom",
"boxZoom", "boxZoom",
"doubleClickZoom", "doubleClickZoom",
"touchZoomRotate", "touchZoomRotate"
] ]
readonly location: UIEventSource<{ lon: number; lat: number }> readonly location: UIEventSource<{ lon: number; lat: number }>
readonly zoom: UIEventSource<number> readonly zoom: UIEventSource<number>
@ -47,6 +47,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
readonly rotation: UIEventSource<number> readonly rotation: UIEventSource<number>
readonly pitch: UIEventSource<number> readonly pitch: UIEventSource<number>
readonly useTerrain: Store<boolean> readonly useTerrain: Store<boolean>
readonly showScale: UIEventSource<boolean>
private static pmtilesInited = false private static pmtilesInited = false
/** /**
@ -92,6 +93,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
this.useTerrain = state?.useTerrain ?? new ImmutableStore<boolean>(false) this.useTerrain = state?.useTerrain ?? new ImmutableStore<boolean>(false)
this.rasterLayer = this.rasterLayer =
state?.rasterLayer ?? new UIEventSource<RasterLayerPolygon | undefined>(undefined) state?.rasterLayer ?? new UIEventSource<RasterLayerPolygon | undefined>(undefined)
this.showScale = state?.showScale ?? new UIEventSource<boolean>(false)
const lastClickLocation = new UIEventSource<{ const lastClickLocation = new UIEventSource<{
lat: number lat: number
@ -104,6 +106,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
new RasterLayerHandler(this._maplibreMap, this.rasterLayer) new RasterLayerHandler(this._maplibreMap, this.rasterLayer)
const clickmodes = ["left", "middle", "right"] as const const clickmodes = ["left", "middle", "right"] as const
function handleClick(e: maplibregl.MapMouseEvent, mode?: "left" | "right" | "middle") { function handleClick(e: maplibregl.MapMouseEvent, mode?: "left" | "right" | "middle") {
if (e.originalEvent["consumed"]) { if (e.originalEvent["consumed"]) {
// Workaround, 'ShowPointLayer' sets this flag // Workaround, 'ShowPointLayer' sets this flag
@ -129,6 +132,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
self.setMaxzoom(self.maxzoom.data) self.setMaxzoom(self.maxzoom.data)
self.setBounds(self.bounds.data) self.setBounds(self.bounds.data)
self.setTerrain(self.useTerrain.data) self.setTerrain(self.useTerrain.data)
self.setScale(self.showScale.data)
this.updateStores(true) this.updateStores(true)
}) })
self.MoveMapToCurrentLoc(self.location.data) self.MoveMapToCurrentLoc(self.location.data)
@ -142,6 +146,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
self.setBounds(self.bounds.data) self.setBounds(self.bounds.data)
self.SetRotation(self.rotation.data) self.SetRotation(self.rotation.data)
self.setTerrain(self.useTerrain.data) self.setTerrain(self.useTerrain.data)
self.setScale(self.showScale.data)
this.updateStores(true) this.updateStores(true)
map.on("moveend", () => this.updateStores()) map.on("moveend", () => this.updateStores())
map.on("click", (e) => { map.on("click", (e) => {
@ -213,6 +218,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
this.allowZooming.addCallbackAndRun((allowZooming) => self.setAllowZooming(allowZooming)) this.allowZooming.addCallbackAndRun((allowZooming) => self.setAllowZooming(allowZooming))
this.bounds.addCallbackAndRunD((bounds) => self.setBounds(bounds)) this.bounds.addCallbackAndRunD((bounds) => self.setBounds(bounds))
this.useTerrain?.addCallbackAndRun((useTerrain) => self.setTerrain(useTerrain)) this.useTerrain?.addCallbackAndRun((useTerrain) => self.setTerrain(useTerrain))
this.showScale?.addCallbackAndRun(showScale => self.setScale(showScale))
} }
/** /**
@ -227,9 +233,9 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
return { return {
map: mlmap, map: mlmap,
ui: new SvelteUIElement(MaplibreMap, { ui: new SvelteUIElement(MaplibreMap, {
map: mlmap, map: mlmap
}), }),
mapproperties: new MapLibreAdaptor(mlmap), mapproperties: new MapLibreAdaptor(mlmap)
} }
} }
@ -297,7 +303,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
) { ) {
const event = { const event = {
date: new Date(), date: new Date(),
key: key, key: key
} }
for (let i = 0; i < this._onKeyNavigation.length; i++) { for (let i = 0; i < this._onKeyNavigation.length; i++) {
@ -486,7 +492,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
const bounds = map.getBounds() const bounds = map.getBounds()
const bbox = new BBox([ const bbox = new BBox([
[bounds.getEast(), bounds.getNorth()], [bounds.getEast(), bounds.getNorth()],
[bounds.getWest(), bounds.getSouth()], [bounds.getWest(), bounds.getSouth()]
]) ])
if (this.bounds.data === undefined || !isSetup) { if (this.bounds.data === undefined || !isSetup) {
this.bounds.setData(bbox) this.bounds.setData(bbox)
@ -664,18 +670,42 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
type: "raster-dem", type: "raster-dem",
url: url:
"https://api.maptiler.com/tiles/terrain-rgb/tiles.json?key=" + "https://api.maptiler.com/tiles/terrain-rgb/tiles.json?key=" +
Constants.maptilerApiKey, Constants.maptilerApiKey
}) })
try { try {
while (!map?.isStyleLoaded()) { while (!map?.isStyleLoaded()) {
await Utils.waitFor(250) await Utils.waitFor(250)
} }
map.setTerrain({ map.setTerrain({
source: id, source: id
}) })
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
} }
} }
private scaleControl: maplibregl.ScaleControl = undefined
private setScale(showScale: boolean) {
const map = this._maplibreMap.data
if (!map) {
return
}
if (!showScale && this.scaleControl) {
map.removeControl(this.scaleControl)
return
}
console.log("Adding scale")
if (this.scaleControl === undefined) {
this.scaleControl = new ScaleControl({
maxWidth: 80,
unit: "metric"
})
}
if (!map.hasControl(this.scaleControl)) {
map.addControl(this.scaleControl, "bottom-right")
}
}
} }

View file

@ -390,6 +390,11 @@
{/if} {/if}
</div> </div>
</If> </If>
<If condition={state.mapProperties.showScale}>
<div class="h-6">
<!-- Empty. We just provide some space for the maplibre scalecontrol -->
</div>
</If>
</div> </div>
</div> </div>
</div> </div>