Feature: allow to disable map rotation

This commit is contained in:
Pieter Vander Vennet 2023-07-18 01:26:04 +02:00
parent bc385259ed
commit 2cc943889d
6 changed files with 108 additions and 65 deletions

View file

@ -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": {

View file

@ -36,6 +36,7 @@ export default class UserRelatedState {
public readonly installedUserThemes: Store<string[]>
public readonly showAllQuestionsAtOnce: UIEventSource<boolean>
public readonly showTags: UIEventSource<"no" | undefined | "always" | "yes" | "full">
public readonly fixateNorth: UIEventSource<undefined | "yes">
public readonly homeLocation: FeatureSource
public readonly language: UIEventSource<string>
/**
@ -87,7 +88,7 @@ export default class UserRelatedState {
)
this.language = this.osmConnection.GetPreference("language")
this.showTags = <UIEventSource<any>>this.osmConnection.GetPreference("show_tags")
this.fixateNorth = <any>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])
}
}
})

View file

@ -11,7 +11,7 @@ export interface MapProperties {
readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined>
readonly maxbounds: UIEventSource<undefined | BBox>
readonly allowMoving: UIEventSource<true | boolean>
readonly allowRotating: UIEventSource<true | boolean>
readonly lastClickLocation: Store<{ lon: number; lat: number }>
readonly allowZooming: UIEventSource<true | boolean>

View file

@ -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<Feature>
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<Feature | undefined>(undefined, "Selected element")
this.selectedLayer = new UIEventSource<LayerConfig>(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 : <Feature[]>[bbox.asGeoJson({id: "range"})]
bbox === undefined ? empty : <Feature[]>[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)
}

View file

@ -35,6 +35,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined>
readonly maxbounds: UIEventSource<BBox | undefined>
readonly allowMoving: UIEventSource<true | boolean | undefined>
readonly allowRotating: UIEventSource<true | boolean | undefined>
readonly allowZooming: UIEventSource<true | boolean | undefined>
readonly lastClickLocation: Store<undefined | { lon: number; lat: number }>
readonly minzoom: UIEventSource<number>
@ -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<boolean>(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) {

View file

@ -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<number> = writable(1)
const styleUrl = AvailableRasterLayers.maplibre.properties.url