diff --git a/assets/layers/usersettings/usersettings.json b/assets/layers/usersettings/usersettings.json index 9b79a82af..c4b0e0ad3 100644 --- a/assets/layers/usersettings/usersettings.json +++ b/assets/layers/usersettings/usersettings.json @@ -221,6 +221,26 @@ } ] }, + { + "id": "fixate-north", + "question": { + "en": "Should north always be up?" + }, + "mappings": [ + { + "if": "mapcomplete-fixate-north=", + "then": { + "en": "Allow to rotate the map" + } + }, + { + "if": "mapcomplete-fixate-north=yes", + "then": { + "en": "Always keep north pointing up" + } + } + ] + }, { "id": "mangrove-keys", "render": { diff --git a/src/Logic/State/UserRelatedState.ts b/src/Logic/State/UserRelatedState.ts index fc0729710..390aa0657 100644 --- a/src/Logic/State/UserRelatedState.ts +++ b/src/Logic/State/UserRelatedState.ts @@ -36,6 +36,7 @@ export default class UserRelatedState { public readonly installedUserThemes: Store public readonly showAllQuestionsAtOnce: UIEventSource public readonly showTags: UIEventSource<"no" | undefined | "always" | "yes" | "full"> + public readonly fixateNorth: UIEventSource public readonly homeLocation: FeatureSource public readonly language: UIEventSource /** @@ -87,7 +88,7 @@ export default class UserRelatedState { ) this.language = this.osmConnection.GetPreference("language") this.showTags = >this.osmConnection.GetPreference("show_tags") - + this.fixateNorth = this.osmConnection.GetPreference("fixate-north") this.mangroveIdentity = new MangroveIdentity( this.osmConnection.GetLongPreference("identity", "mangrove") ) @@ -364,7 +365,14 @@ export default class UserRelatedState { // Language is managed seperately continue } - this.osmConnection.GetPreference(key, undefined, { prefix: "" }).setData(tags[key]) + if (tags[key + "-combined-0"]) { + // A combined value exists + this.osmConnection.GetLongPreference(key, "").setData(tags[key]) + } else { + this.osmConnection + .GetPreference(key, undefined, { prefix: "" }) + .setData(tags[key]) + } } }) diff --git a/src/Models/MapProperties.ts b/src/Models/MapProperties.ts index a6b4058bc..a47acac57 100644 --- a/src/Models/MapProperties.ts +++ b/src/Models/MapProperties.ts @@ -11,7 +11,7 @@ export interface MapProperties { readonly rasterLayer: UIEventSource readonly maxbounds: UIEventSource readonly allowMoving: UIEventSource - + readonly allowRotating: UIEventSource readonly lastClickLocation: Store<{ lon: number; lat: number }> readonly allowZooming: UIEventSource diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 8f48750f4..395c4a7c9 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -1,23 +1,27 @@ import LayoutConfig from "./ThemeConfig/LayoutConfig" -import {SpecialVisualizationState} from "../UI/SpecialVisualization" -import {Changes} from "../Logic/Osm/Changes" -import {ImmutableStore, Store, UIEventSource} from "../Logic/UIEventSource" -import {FeatureSource, IndexedFeatureSource, WritableFeatureSource,} from "../Logic/FeatureSource/FeatureSource" -import {OsmConnection} from "../Logic/Osm/OsmConnection" -import {ExportableMap, MapProperties} from "./MapProperties" +import { SpecialVisualizationState } from "../UI/SpecialVisualization" +import { Changes } from "../Logic/Osm/Changes" +import { ImmutableStore, Store, UIEventSource } from "../Logic/UIEventSource" +import { + FeatureSource, + IndexedFeatureSource, + WritableFeatureSource, +} from "../Logic/FeatureSource/FeatureSource" +import { OsmConnection } from "../Logic/Osm/OsmConnection" +import { ExportableMap, MapProperties } from "./MapProperties" import LayerState from "../Logic/State/LayerState" -import {Feature, Point, Polygon} from "geojson" +import { Feature, Point, Polygon } from "geojson" import FullNodeDatabaseSource from "../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" -import {Map as MlMap} from "maplibre-gl" +import { Map as MlMap } from "maplibre-gl" import InitialMapPositioning from "../Logic/Actors/InitialMapPositioning" -import {MapLibreAdaptor} from "../UI/Map/MapLibreAdaptor" -import {GeoLocationState} from "../Logic/State/GeoLocationState" +import { MapLibreAdaptor } from "../UI/Map/MapLibreAdaptor" +import { GeoLocationState } from "../Logic/State/GeoLocationState" import FeatureSwitchState from "../Logic/State/FeatureSwitchState" -import {QueryParameters} from "../Logic/Web/QueryParameters" +import { QueryParameters } from "../Logic/Web/QueryParameters" import UserRelatedState from "../Logic/State/UserRelatedState" import LayerConfig from "./ThemeConfig/LayerConfig" import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler" -import {AvailableRasterLayers, RasterLayerPolygon, RasterLayerUtils} from "./RasterLayers" +import { AvailableRasterLayers, RasterLayerPolygon, RasterLayerUtils } from "./RasterLayers" import LayoutSource from "../Logic/FeatureSource/Sources/LayoutSource" import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource" import FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropertiesStore" @@ -28,26 +32,28 @@ import TitleHandler from "../Logic/Actors/TitleHandler" import ChangeToElementsActor from "../Logic/Actors/ChangeToElementsActor" import PendingChangesUploader from "../Logic/Actors/PendingChangesUploader" import SelectedElementTagsUpdater from "../Logic/Actors/SelectedElementTagsUpdater" -import {BBox} from "../Logic/BBox" +import { BBox } from "../Logic/BBox" import Constants from "./Constants" import Hotkeys from "../UI/Base/Hotkeys" import Translations from "../UI/i18n/Translations" -import {GeoIndexedStoreForLayer} from "../Logic/FeatureSource/Actors/GeoIndexedStore" -import {LastClickFeatureSource} from "../Logic/FeatureSource/Sources/LastClickFeatureSource" -import {MenuState} from "./MenuState" +import { GeoIndexedStoreForLayer } from "../Logic/FeatureSource/Actors/GeoIndexedStore" +import { LastClickFeatureSource } from "../Logic/FeatureSource/Sources/LastClickFeatureSource" +import { MenuState } from "./MenuState" import MetaTagging from "../Logic/MetaTagging" import ChangeGeometryApplicator from "../Logic/FeatureSource/Sources/ChangeGeometryApplicator" -import {NewGeometryFromChangesFeatureSource} from "../Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource" +import { NewGeometryFromChangesFeatureSource } from "../Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource" import OsmObjectDownloader from "../Logic/Osm/OsmObjectDownloader" import ShowOverlayRasterLayer from "../UI/Map/ShowOverlayRasterLayer" -import {Utils} from "../Utils" -import {EliCategory} from "./RasterLayerProperties" +import { Utils } from "../Utils" +import { EliCategory } from "./RasterLayerProperties" import BackgroundLayerResetter from "../Logic/Actors/BackgroundLayerResetter" import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage" import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor" -import NoElementsInViewDetector, {FeatureViewState,} from "../Logic/Actors/NoElementsInViewDetector" -import FilteredLayer from "./FilteredLayer"; +import NoElementsInViewDetector, { + FeatureViewState, +} from "../Logic/Actors/NoElementsInViewDetector" +import FilteredLayer from "./FilteredLayer" /** * @@ -69,6 +75,7 @@ export default class ThemeViewState implements SpecialVisualizationState { readonly osmConnection: OsmConnection readonly selectedElement: UIEventSource + readonly selectedElementAndLayer: Store<{ feature: Feature; layer: LayerConfig }> readonly mapProperties: MapProperties & ExportableMap readonly osmObjectDownloader: OsmObjectDownloader @@ -133,8 +140,23 @@ export default class ThemeViewState implements SpecialVisualizationState { layout, this.featureSwitches ) + this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => { + this.mapProperties.allowRotating.setData(fixated !== "yes") + }) this.selectedElement = new UIEventSource(undefined, "Selected element") this.selectedLayer = new UIEventSource(undefined, "Selected layer") + + this.selectedElementAndLayer = this.selectedElement.mapD( + (feature) => { + const layer = this.selectedLayer.data + if (!layer) { + return undefined + } + return { layer, feature } + }, + [this.selectedLayer] + ) + this.geolocation = new GeoLocationHandler( geolocationState, this.selectedElement, @@ -155,7 +177,7 @@ export default class ThemeViewState implements SpecialVisualizationState { rasterInfo.defaultState ?? true, "Wether or not overlayer layer " + rasterInfo.id + " is shown" ) - const state = {isDisplayed} + const state = { isDisplayed } overlayLayerStates.set(rasterInfo.id, state) new ShowOverlayRasterLayer(rasterInfo, this.map, this.mapProperties, state) } @@ -373,7 +395,7 @@ export default class ThemeViewState implements SpecialVisualizationState { private initHotkeys() { Hotkeys.RegisterHotkey( - {nomod: "Escape", onUp: true}, + { nomod: "Escape", onUp: true }, Translations.t.hotkeyDocumentation.closeSidebar, () => { this.selectedElement.setData(undefined) @@ -394,7 +416,7 @@ export default class ThemeViewState implements SpecialVisualizationState { ) Hotkeys.RegisterHotkey( - {shift: "O"}, + { shift: "O" }, Translations.t.hotkeyDocumentation.selectMapnik, () => { this.mapProperties.rasterLayer.setData(AvailableRasterLayers.osmCarto) @@ -413,17 +435,17 @@ export default class ThemeViewState implements SpecialVisualizationState { } Hotkeys.RegisterHotkey( - {nomod: "O"}, + { nomod: "O" }, Translations.t.hotkeyDocumentation.selectOsmbasedmap, () => setLayerCategory("osmbasedmap") ) - Hotkeys.RegisterHotkey({nomod: "M"}, Translations.t.hotkeyDocumentation.selectMap, () => + Hotkeys.RegisterHotkey({ nomod: "M" }, Translations.t.hotkeyDocumentation.selectMap, () => setLayerCategory("map") ) Hotkeys.RegisterHotkey( - {nomod: "P"}, + { nomod: "P" }, Translations.t.hotkeyDocumentation.selectAerial, () => setLayerCategory("photo") ) @@ -491,7 +513,7 @@ export default class ThemeViewState implements SpecialVisualizationState { ), range: new StaticFeatureSource( this.mapProperties.maxbounds.map((bbox) => - bbox === undefined ? empty : [bbox.asGeoJson({id: "range"})] + bbox === undefined ? empty : [bbox.asGeoJson({ id: "range" })] ) ), current_view: this.currentView, @@ -521,12 +543,13 @@ export default class ThemeViewState implements SpecialVisualizationState { }) } - const rangeFLayer: FilteredLayer = this.layerState.filteredLayers - .get("range") + const rangeFLayer: FilteredLayer = this.layerState.filteredLayers.get("range") const rangeIsDisplayed = rangeFLayer?.isDisplayed - if (!QueryParameters.wasInitialized(FilteredLayer.queryParameterKey(rangeFLayer.layerDef))) { + if ( + !QueryParameters.wasInitialized(FilteredLayer.queryParameterKey(rangeFLayer.layerDef)) + ) { rangeIsDisplayed?.syncWith(this.featureSwitches.featureSwitchIsTesting, true) } diff --git a/src/UI/Map/MapLibreAdaptor.ts b/src/UI/Map/MapLibreAdaptor.ts index 4b0c60e91..6193ee7ca 100644 --- a/src/UI/Map/MapLibreAdaptor.ts +++ b/src/UI/Map/MapLibreAdaptor.ts @@ -35,6 +35,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { readonly rasterLayer: UIEventSource readonly maxbounds: UIEventSource readonly allowMoving: UIEventSource + readonly allowRotating: UIEventSource readonly allowZooming: UIEventSource readonly lastClickLocation: Store readonly minzoom: UIEventSource @@ -69,6 +70,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { }) this.maxbounds = state?.maxbounds ?? new UIEventSource(undefined) this.allowMoving = state?.allowMoving ?? new UIEventSource(true) + this.allowRotating = state?.allowRotating ?? new UIEventSource(true) this.allowZooming = state?.allowZooming ?? new UIEventSource(true) this.bounds = state?.bounds ?? new UIEventSource(undefined) this.rasterLayer = @@ -95,6 +97,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { self.SetZoom(self.zoom.data) self.setMaxBounds(self.maxbounds.data) self.setAllowMoving(self.allowMoving.data) + self.setAllowRotating(self.allowRotating.data) self.setAllowZooming(self.allowZooming.data) self.setMinzoom(self.minzoom.data) self.setMaxzoom(self.maxzoom.data) @@ -105,6 +108,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { self.SetZoom(self.zoom.data) self.setMaxBounds(self.maxbounds.data) self.setAllowMoving(self.allowMoving.data) + self.setAllowRotating(self.allowRotating.data) self.setAllowZooming(self.allowZooming.data) self.setMinzoom(self.minzoom.data) self.setMaxzoom(self.maxzoom.data) @@ -134,6 +138,9 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { this.zoom.addCallbackAndRunD((z) => self.SetZoom(z)) this.maxbounds.addCallbackAndRun((bbox) => self.setMaxBounds(bbox)) this.allowMoving.addCallbackAndRun((allowMoving) => self.setAllowMoving(allowMoving)) + this.allowRotating.addCallbackAndRunD((allowRotating) => + self.setAllowRotating(allowRotating) + ) this.allowZooming.addCallbackAndRun((allowZooming) => self.setAllowZooming(allowZooming)) this.bounds.addCallbackAndRunD((bounds) => self.setBounds(bounds)) } @@ -181,13 +188,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { drawOn.width = Math.ceil(drawOn.width * dpiFactor) drawOn.height = Math.ceil(drawOn.height * dpiFactor) ctx.scale(dpiFactor, dpiFactor) - console.log( - "Resizing canvas with setDPI:", - drawOn.width, - drawOn.height, - drawOn.style.width, - drawOn.style.height - ) } /** @@ -228,27 +228,14 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { drawOn.width = map.getCanvas().width drawOn.height = map.getCanvas().height - console.log("Canvas size:", drawOn.width, drawOn.height) const ctx = drawOn.getContext("2d") // Set up CSS size. MapLibreAdaptor.setDpi(drawOn, ctx, dpiFactor / map.getPixelRatio()) await this.exportBackgroundOnCanvas(ctx) - console.log("Getting markers") // MapLibreAdaptor.setDpi(drawOn, ctx, 1) const markers = await this.drawMarkers(dpiFactor) - console.log( - "Drawing markers (" + - markers.width + - "*" + - markers.height + - ") onto drawOn (" + - drawOn.width + - "*" + - drawOn.height + - ")" - ) ctx.drawImage(markers, 0, 0, drawOn.width, drawOn.height) ctx.scale(dpiFactor, dpiFactor) this._maplibreMap.data?.resize() @@ -288,14 +275,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { } const width = map.getCanvas().clientWidth const height = map.getCanvas().clientHeight - console.log( - "Canvas size markers:", - map.getCanvas().width, - map.getCanvas().height, - "canvasClientRect:", - width, - height - ) map.getCanvas().style.display = "none" const img = await htmltoimage.toCanvas(map.getCanvasContainer(), { pixelRatio: dpiFactor, @@ -394,7 +373,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { return } const background: RasterLayerProperties = this.rasterLayer?.data?.properties - console.log("Setting background to", background) if (!background) { console.error( "Attempting to 'setBackground', but the background is", @@ -416,7 +394,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { } if (background.type === "vector") { - console.log("Background layer is vector", background.id) this.removeCurrentLayer(map) map.setStyle(background.url) return @@ -473,6 +450,21 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { } } + private setAllowRotating(allow: true | boolean | undefined) { + const map = this._maplibreMap.data + if (!map) { + return + } + console.log("Rotation allowed:", allow) + if (allow === false) { + map.rotateTo(0, { duration: 0 }) + map.setPitch(0) + map.dragRotate.disable() + } else { + map.dragRotate.enable() + } + } + private setAllowMoving(allow: true | boolean | undefined) { const map = this._maplibreMap.data if (!map) { @@ -487,6 +479,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { map[id].enable() } } + this.setAllowRotating(this.allowRotating.data) } private setMinzoom(minzoom: number) { diff --git a/src/UI/Map/MaplibreMap.svelte b/src/UI/Map/MaplibreMap.svelte index ee8b64b5c..268a12cbd 100644 --- a/src/UI/Map/MaplibreMap.svelte +++ b/src/UI/Map/MaplibreMap.svelte @@ -23,7 +23,6 @@ export let attribution = false export let center: {lng: number, lat: number} | Readable<{ lng: number; lat: number }> = writable({lng: 0, lat: 0}) - console.trace("Center is", center) export let zoom: Readable = writable(1) const styleUrl = AvailableRasterLayers.maplibre.properties.url