Themes: add possibility to enable terrain, some fixes to overlaymap and pitch control

This commit is contained in:
Pieter Vander Vennet 2024-02-03 14:33:10 +01:00
parent eede2d17dd
commit e3dec8aafa
10 changed files with 70 additions and 7 deletions

View file

@ -1,2 +1,2 @@
SPDX-FileCopyrightText: Gouvernement fran<61><6E>ais, Roulex 45
SPDX-License-Identifier: CC0
SPDX-License-Identifier: CC0-1.0

View file

@ -7,6 +7,7 @@
"en": "Everything you need to go skiing"
},
"icon": "./assets/layers/aerialway/chair_lift.svg",
"enableTerrain": true,
"layers": [
"ski_piste",
"aerialway",

View file

@ -1,4 +1,4 @@
import { UIEventSource } from "../UIEventSource"
import { ImmutableStore, Store, UIEventSource } from "../UIEventSource"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import { LocalStorageSource } from "../Web/LocalStorageSource"
import { QueryParameters } from "../Web/QueryParameters"
@ -15,6 +15,7 @@ import { QueryParameters } from "../Web/QueryParameters"
export default class InitialMapPositioning {
public zoom: UIEventSource<number>
public location: UIEventSource<{ lon: number; lat: number }>
public useTerrain: Store<boolean>
constructor(layoutToUse: LayoutConfig) {
function localStorageSynced(
key: string,
@ -55,10 +56,11 @@ export default class InitialMapPositioning {
)
this.location = new UIEventSource({ lon: lon.data, lat: lat.data })
// Note: this syncs only in one direction
this.location.addCallbackD((loc) => {
lat.setData(loc.lat)
lon.setData(loc.lon)
})
// Note: this syncs only in one direction
this.useTerrain = new ImmutableStore<boolean>(layoutToUse.enableTerrain)
}
}

View file

@ -156,6 +156,7 @@ export default class Constants {
"addSmall",
] as const
public static readonly defaultPinIcons: string[] = <any>Constants._defaultPinIcons
public static readonly maptilerApiKey = "GvoVAJgu46I5rZapJuAy"
private static isRetina(): boolean {
if (Utils.runningFromConsole) {

View file

@ -18,8 +18,10 @@ export interface MapProperties {
readonly allowMoving: UIEventSource<true | boolean>
readonly allowRotating: UIEventSource<true | boolean>
readonly rotation: UIEventSource<number>
readonly pitch: UIEventSource<number>
readonly lastClickLocation: Store<{ lon: number; lat: number }>
readonly allowZooming: UIEventSource<true | boolean>
readonly useTerrain: Store<boolean>
/**
* Triggered when the user navigated by using the keyboard.

View file

@ -5,6 +5,7 @@ import { BBox } from "../Logic/BBox"
import { Store, Stores } from "../Logic/UIEventSource"
import { GeoOperations } from "../Logic/GeoOperations"
import { RasterLayerProperties } from "./RasterLayerProperties"
import Constants from "./Constants"
export class AvailableRasterLayers {
public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> &
@ -51,7 +52,7 @@ export class AvailableRasterLayers {
type: "Feature",
properties: {
name: "MapTiler",
url: "https://api.maptiler.com/maps/15cc8f61-0353-4be6-b8da-13daea5f7432/style.json?key=GvoVAJgu46I5rZapJuAy",
url: "https://api.maptiler.com/maps/15cc8f61-0353-4be6-b8da-13daea5f7432/style.json?key="+Constants.maptilerApiKey,
category: "osmbasedmap",
id: "maptiler",
type: "vector",

View file

@ -388,6 +388,18 @@ export interface LayoutConfigJson {
*/
enableNoteImports?: true | boolean
/**
* question: Should the map use elevation data to give a 3D-feel?
*
* This is especially useful for hiking maps, skiing maps etc...
*
* funset: MapComplete default: don't use terrain
* iftrue: Use elevation and render 3D
* iffalse: Do not use terrain
* group: advanced
*/
enableTerrain?: false | boolean
/**
* question: What overpass-api instance should be used for this layout?
*

View file

@ -61,6 +61,7 @@ export default class LayoutConfig implements LayoutInformation {
public readonly enableShowAllQuestions: boolean
public readonly enableExportButton: boolean
public readonly enablePdfDownload: boolean
public readonly enableTerrain: boolean
public readonly customCss?: string
@ -208,6 +209,7 @@ export default class LayoutConfig implements LayoutInformation {
this.enableShowAllQuestions = json.enableShowAllQuestions ?? false
this.enableExportButton = json.enableDownload ?? true
this.enablePdfDownload = json.enablePdfDownload ?? true
this.enableTerrain = json.enableTerrain ?? false
this.customCss = json.customCss
this.overpassUrl = json.overpassUrl ?? Constants.defaultOverpassUrls
this.overpassTimeout = json.overpassTimeout ?? 30

View file

@ -1,7 +1,7 @@
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
import type { Map as MLMap } from "maplibre-gl"
import { Map as MlMap, SourceSpecification } from "maplibre-gl"
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers"
import { RasterLayerPolygon } from "../../Models/RasterLayers"
import { Utils } from "../../Utils"
import { BBox } from "../../Logic/BBox"
import { ExportableMap, KeyNavigationEvent, MapProperties } from "../../Models/MapProperties"
@ -10,6 +10,7 @@ import MaplibreMap from "./MaplibreMap.svelte"
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
import * as htmltoimage from "html-to-image"
import RasterLayerHandler from "./RasterLayerHandler"
import Constants from "../../Models/Constants"
/**
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`
@ -42,6 +43,8 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
readonly minzoom: UIEventSource<number>
readonly maxzoom: UIEventSource<number>
readonly rotation: UIEventSource<number>
readonly pitch: UIEventSource<number>
readonly useTerrain: Store<boolean>
/**
* Functions that are called when one of those actions has happened
@ -78,6 +81,8 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
this.allowZooming = state?.allowZooming ?? new UIEventSource(true)
this.bounds = state?.bounds ?? new UIEventSource(undefined)
this.rotation = state?.rotation ?? new UIEventSource<number>(0)
this.pitch = state?.pitch ?? new UIEventSource<number>(0)
this.useTerrain = state?.useTerrain ?? new ImmutableStore<boolean>(false)
this.rasterLayer =
state?.rasterLayer ?? new UIEventSource<RasterLayerPolygon | undefined>(undefined)
@ -108,6 +113,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
self.setMinzoom(self.minzoom.data)
self.setMaxzoom(self.maxzoom.data)
self.setBounds(self.bounds.data)
self.setTerrain(self.useTerrain.data)
rasterLayerHandler.setBackground()
this.updateStores(true)
})
@ -121,6 +127,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
self.setMaxzoom(self.maxzoom.data)
self.setBounds(self.bounds.data)
self.SetRotation(self.rotation.data)
self.setTerrain(self.useTerrain.data)
rasterLayerHandler.setBackground()
this.updateStores(true)
map.on("moveend", () => this.updateStores())
@ -136,6 +143,9 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
map.on("rotateend", (_) => {
this.updateStores()
})
map.on("pitchend", () => {
this.updateStores()
})
map.getContainer().addEventListener("keydown", (event) => {
let locked: "islocked" = undefined
if (!this.allowMoving.data) {
@ -183,6 +193,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
)
this.allowZooming.addCallbackAndRun((allowZooming) => self.setAllowZooming(allowZooming))
this.bounds.addCallbackAndRunD((bounds) => self.setBounds(bounds))
this.useTerrain?.addCallbackAndRun(useTerrain => self.setTerrain(useTerrain))
}
/**
@ -248,7 +259,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
const w = map.getContainer().getBoundingClientRect().width
const h = map.getContainer().getBoundingClientRect().height
let dpi = map.getPixelRatio()
const dpi = map.getPixelRatio()
// The 'css'-size stays constant...
drawOn.style.width = w + "px"
drawOn.style.height = h + "px"
@ -423,6 +434,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
this.bounds.setData(bbox)
}
this.rotation.setData(map.getBearing())
this.pitch.setData(map.getPitch())
}
private SetZoom(z: number): void {
@ -579,4 +591,32 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
}
map.fitBounds(bounds.toLngLat())
}
private async setTerrain(useTerrain: boolean) {
const map = this._maplibreMap.data
if (!map) {
return
}
const id = "maptiler-terrain-data"
if (useTerrain) {
if(map.getTerrain()){
return
}
map.addSource(id, {
"type": "raster-dem",
"url": "https://api.maptiler.com/tiles/terrain-rgb/tiles.json?key=" + Constants.maptilerApiKey
})
try{
while (!map?.isStyleLoaded()) {
await Utils.waitFor(250)
}
map.setTerrain({
source: id
})
}catch (e) {
console.error(e)
}
}
}
}

View file

@ -24,6 +24,8 @@
let altproperties = new MapLibreAdaptor(altmap, {
rasterLayer,
zoom: UIEventSource.feedFrom(placedOverMapProperties.zoom),
rotation: UIEventSource.feedFrom(placedOverMapProperties.rotation),
pitch: UIEventSource.feedFrom(placedOverMapProperties.pitch)
})
altproperties.allowMoving.setData(false)
altproperties.allowZooming.setData(false)