forked from MapComplete/MapComplete
Add themes to search functionality, including quickswitch between recent themes
This commit is contained in:
parent
b4866cdbac
commit
329865a15e
22 changed files with 679 additions and 431 deletions
|
|
@ -35,7 +35,7 @@
|
|||
"oauth_token",
|
||||
undefined,
|
||||
"Used to complete the login"
|
||||
),
|
||||
)
|
||||
})
|
||||
const state = new UserRelatedState(osmConnection)
|
||||
const t = Translations.t.index
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
let userLanguages = osmConnection.userDetails.map((ud) => ud.languages)
|
||||
let themeSearchText: UIEventSource<string | undefined> = new UIEventSource<string>(undefined)
|
||||
|
||||
document.addEventListener("keydown", function (event) {
|
||||
document.addEventListener("keydown", function(event) {
|
||||
if (event.ctrlKey && event.code === "KeyF") {
|
||||
document.getElementById("theme-search")?.focus()
|
||||
event.preventDefault()
|
||||
|
|
@ -55,20 +55,10 @@
|
|||
const hiddenThemes: LayoutInformation[] =
|
||||
(themeOverview["default"] ?? themeOverview)?.filter((layout) => layout.hideFromOverview) ?? []
|
||||
{
|
||||
const prefix = "mapcomplete-hidden-theme-"
|
||||
const userPreferences = state.osmConnection.preferencesHandler.preferences
|
||||
visitedHiddenThemes = userPreferences.map((preferences) => {
|
||||
const knownIds = new Set<string>(
|
||||
Object.keys(preferences)
|
||||
.filter((key) => key.startsWith(prefix))
|
||||
.map((key) => key.substring(prefix.length, key.length - "-enabled".length))
|
||||
)
|
||||
return hiddenThemes.filter(
|
||||
(theme) =>
|
||||
knownIds.has(theme.id) ||
|
||||
state.osmConnection.userDetails.data.name === "Pieter Vander Vennet"
|
||||
)
|
||||
})
|
||||
visitedHiddenThemes = MoreScreen.knownHiddenThemes(state.osmConnection)
|
||||
.map((knownIds) => hiddenThemes.filter((theme) =>
|
||||
knownIds.has(theme.id) || state.osmConnection.userDetails.data.name === "Pieter Vander Vennet"
|
||||
))
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -103,7 +93,7 @@
|
|||
|
||||
<form
|
||||
class="flex justify-center"
|
||||
on:submit|preventDefault={(_) => MoreScreen.applySearch(themeSearchText.data)}
|
||||
on:submit|preventDefault={() => MoreScreen.applySearch(themeSearchText.data)}
|
||||
>
|
||||
<label
|
||||
class="neutral-label my-2 flex w-full items-center rounded-full border-2 border-black sm:w-1/2"
|
||||
|
|
|
|||
|
|
@ -116,8 +116,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
let suggestions: Store<GeoCodeResult[]> = searchContents.stabilized(250).bindD(search =>
|
||||
UIEventSource.FromPromise(searcher.suggest(search), err => console.error(err))
|
||||
let suggestions: Store<{success: GeoCodeResult[]} | {error}> = searchContents.stabilized(250).bindD(search =>
|
||||
UIEventSource.FromPromiseWithErr(searcher.suggest(search))
|
||||
)
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,27 @@
|
|||
import { LayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
|
||||
import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import { Utils } from "../../Utils"
|
||||
import themeOverview from "../../assets/generated/theme_overview.json"
|
||||
import Locale from "../i18n/Locale"
|
||||
import { Translatable } from "../../Models/ThemeConfig/Json/Translatable"
|
||||
import { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
|
||||
export default class MoreScreen {
|
||||
public static readonly officialThemes: LayoutInformation[] = themeOverview
|
||||
|
||||
public static readonly officialThemes: MinimalLayoutInformation[] = themeOverview
|
||||
public static readonly officialThemesById: Map<string, MinimalLayoutInformation> = new Map<string, MinimalLayoutInformation>()
|
||||
static {
|
||||
for (const th of MoreScreen.officialThemes) {
|
||||
MoreScreen.officialThemesById.set(th.id, th)
|
||||
}
|
||||
}
|
||||
public static applySearch(searchTerm: string) {
|
||||
searchTerm = searchTerm.toLowerCase()
|
||||
if (!searchTerm) {
|
||||
return
|
||||
}
|
||||
if (searchTerm === "personal") {
|
||||
window.location.href = MoreScreen.createUrlFor({ id: "personal" }, false).data
|
||||
window.location.href = MoreScreen.createUrlFor({ id: "personal" }, false)
|
||||
}
|
||||
if (searchTerm === "bugs" || searchTerm === "issues") {
|
||||
window.location.href = "https://github.com/pietervdvn/MapComplete/issues"
|
||||
|
|
@ -38,22 +46,22 @@ export default class MoreScreen {
|
|||
MoreScreen.MatchesLayout(th, searchTerm)
|
||||
)
|
||||
if (publicTheme !== undefined) {
|
||||
window.location.href = MoreScreen.createUrlFor(publicTheme, false).data
|
||||
window.location.href = MoreScreen.createUrlFor(publicTheme, false)
|
||||
}
|
||||
const hiddenTheme = MoreScreen.officialThemes.find(
|
||||
(th) => th.id !== "personal" && MoreScreen.MatchesLayout(th, searchTerm)
|
||||
)
|
||||
if (hiddenTheme !== undefined) {
|
||||
window.location.href = MoreScreen.createUrlFor(hiddenTheme, false).data
|
||||
window.location.href = MoreScreen.createUrlFor(hiddenTheme, false)
|
||||
}
|
||||
}
|
||||
|
||||
public static MatchesLayout(
|
||||
layout: {
|
||||
id: string
|
||||
title: any
|
||||
shortDescription: any
|
||||
keywords?: any[]
|
||||
title: Translatable
|
||||
shortDescription: Translatable
|
||||
keywords?: (Translatable | TagRenderingConfigJson)[]
|
||||
},
|
||||
search: string
|
||||
): boolean {
|
||||
|
|
@ -72,7 +80,7 @@ export default class MoreScreen {
|
|||
if (entity === undefined) {
|
||||
continue
|
||||
}
|
||||
const term = entity["*"] ?? entity[Locale.language.data]
|
||||
const term: string = entity["*"] ?? entity[Locale.language.data]
|
||||
if (Utils.RemoveDiacritics(term?.toLowerCase())?.indexOf(search) >= 0) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -82,10 +90,10 @@ export default class MoreScreen {
|
|||
}
|
||||
|
||||
public static createUrlFor(
|
||||
layout: { id: string; definition?: string },
|
||||
layout: { id: string },
|
||||
isCustom: boolean,
|
||||
state?: { layoutToUse?: { id } }
|
||||
): Store<string> {
|
||||
): string {
|
||||
if (layout === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
|
@ -115,11 +123,22 @@ export default class MoreScreen {
|
|||
linkPrefix = `${path}/theme.html?userlayout=${layout.id}&`
|
||||
}
|
||||
|
||||
let hash = ""
|
||||
if (layout.definition !== undefined) {
|
||||
hash = "#" + btoa(JSON.stringify(layout.definition))
|
||||
}
|
||||
|
||||
return new ImmutableStore<string>(`${linkPrefix}${hash}`)
|
||||
return `${linkPrefix}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives all the IDs of the hidden themes which were previously visited
|
||||
* @param osmConnection
|
||||
*/
|
||||
public static knownHiddenThemes(osmConnection: OsmConnection): Store<Set<string>> {
|
||||
const prefix = "mapcomplete-hidden-theme-"
|
||||
const userPreferences = osmConnection.preferencesHandler.preferences
|
||||
return userPreferences.map((preferences) =>
|
||||
new Set<string>(
|
||||
Object.keys(preferences)
|
||||
.filter((key) => key.startsWith(prefix))
|
||||
.map((key) => key.substring(prefix.length, key.length - "-enabled".length))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,15 @@
|
|||
import TagRenderingAnswer from "../Popup/TagRendering/TagRenderingAnswer.svelte"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import ArrowUp from "@babeard/svelte-heroicons/mini/ArrowUp"
|
||||
import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
import MoreScreen from "./MoreScreen"
|
||||
|
||||
export let entry: GeoCodeResult
|
||||
export let state: SpecialVisualizationState
|
||||
let layer: LayerConfig
|
||||
let tags : UIEventSource<Record<string, string>>
|
||||
let tags: UIEventSource<Record<string, string>>
|
||||
if (entry.feature?.properties?.id) {
|
||||
layer = state.layout.getMatchingLayer(entry.feature.properties)
|
||||
tags = state.featureProperties.getStore(entry.feature.properties.id)
|
||||
|
|
@ -28,6 +32,8 @@
|
|||
let mapRotation = state.mapProperties.rotation
|
||||
let inView = state.mapProperties.bounds.mapD(bounds => bounds.contains([entry.lon, entry.lat]))
|
||||
|
||||
let otherTheme: MinimalLayoutInformation | undefined = <MinimalLayoutInformation>entry.payload
|
||||
|
||||
function select() {
|
||||
console.log("Selected search entry", entry)
|
||||
if (entry.boundingbox) {
|
||||
|
|
@ -41,44 +47,60 @@
|
|||
} else {
|
||||
state.mapProperties.flyTo(entry.lon, entry.lat, GeocodingUtils.categoryToZoomLevel[entry.category] ?? 17)
|
||||
}
|
||||
if (entry.feature) {
|
||||
if (entry.feature?.properties?.id) {
|
||||
state.selectedElement.set(entry.feature)
|
||||
}
|
||||
state.recentlySearched.addSelected(entry)
|
||||
dispatch("select")
|
||||
}
|
||||
</script>
|
||||
<button class="unstyled w-full link-no-underline" on:click={() => select() }>
|
||||
<div class="p-2 flex items-center w-full gap-y-2 w-full">
|
||||
|
||||
{#if layer}
|
||||
<ToSvelte construct={() => layer.defaultIcon(entry.feature.properties).SetClass("w-6 h-6")} />
|
||||
{:else if entry.category}
|
||||
<Icon icon={GeocodingUtils.categoryToIcon[entry.category]} clss="w-6 h-6 shrink-0" color="#aaa" />
|
||||
{/if}
|
||||
<div class="flex flex-col items-start pl-2 w-full">
|
||||
<div class="flex flex-wrap gap-x-2 justify-between w-full">
|
||||
<b class="nowrap">
|
||||
{#if layer && $tags?.id}
|
||||
<TagRenderingAnswer config={layer.title} selectedElement={entry.feature} {state} {tags} {layer} />
|
||||
{:else}
|
||||
{entry.display_name ?? entry.osm_id}
|
||||
{/if}
|
||||
</b>
|
||||
<div class="flex gap-x-1 items-center">
|
||||
{#if $bearing && !$inView}
|
||||
<ArrowUp class="w-4 h-4 shrink-0" style={`transform: rotate(${$bearing - $mapRotation}deg)`} />
|
||||
{/if}
|
||||
{#if $distance}
|
||||
{GeoOperations.distanceToHuman($distance)}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if entry.description}
|
||||
<div class="subtle flex justify-between w-full">
|
||||
{entry.description}
|
||||
</div>
|
||||
{/if}
|
||||
{#if otherTheme}
|
||||
<a href={ MoreScreen.createUrlFor(otherTheme, false)} class="flex items-center p-2 w-full gap-y-2 rounded-xl" >
|
||||
|
||||
<Icon icon={otherTheme.icon} clss="w-6 h-6 m-1" />
|
||||
<div class="flex flex-col">
|
||||
<b>
|
||||
<Tr t={new Translation(otherTheme.title)} />
|
||||
</b>
|
||||
<!--<Tr t={new Translation(otherTheme.shortDescription)} /> -->
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
{:else}
|
||||
<button class="unstyled w-full link-no-underline" on:click={() => select() }>
|
||||
<div class="p-2 flex items-center w-full gap-y-2">
|
||||
{#if layer}
|
||||
<ToSvelte construct={() => layer.defaultIcon(entry.feature.properties).SetClass("w-6 h-6")} />
|
||||
{:else if entry.category}
|
||||
<Icon icon={GeocodingUtils.categoryToIcon[entry.category]} clss="w-6 h-6 shrink-0" color="#aaa" />
|
||||
{/if}
|
||||
<div class="flex flex-col items-start pl-2 w-full">
|
||||
<div class="flex flex-wrap gap-x-2 justify-between w-full">
|
||||
<b class="nowrap">
|
||||
{#if layer && $tags?.id}
|
||||
<TagRenderingAnswer config={layer.title} selectedElement={entry.feature} {state} {tags} {layer} />
|
||||
{:else}
|
||||
{entry.display_name ?? entry.osm_id}
|
||||
{/if}
|
||||
</b>
|
||||
<div class="flex gap-x-1 items-center">
|
||||
{#if $bearing && !$inView}
|
||||
<ArrowUp class="w-4 h-4 shrink-0" style={`transform: rotate(${$bearing - $mapRotation}deg)`} />
|
||||
{/if}
|
||||
{#if $distance}
|
||||
{GeoOperations.distanceToHuman($distance)}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if entry.description}
|
||||
<div class="subtle flex justify-between w-full">
|
||||
{entry.description}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -3,59 +3,81 @@
|
|||
import SearchResult from "./SearchResult.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { XMarkIcon } from "@babeard/svelte-heroicons/solid"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import MoreScreen from "./MoreScreen"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let results: GeoCodeResult[]
|
||||
export let results: { success: GeoCodeResult[] } | { error }
|
||||
export let searchTerm: Store<string>
|
||||
export let isFocused: Store<boolean>
|
||||
export let isFocused: UIEventSource<boolean>
|
||||
|
||||
let recentlySeen: Store<GeoCodeResult[]> = state.recentlySearched.seenThisSession
|
||||
let recentThemes = state.userRelatedState.recentlyVisitedThemes.mapD(thms => thms.filter(th => th !== state.layout.id).slice(0, 3))
|
||||
let allowOtherThemes = state.featureSwitches.featureSwitchBackToThemeOverview
|
||||
</script>
|
||||
|
||||
<div class="w-full collapsable" style="height: 50rem;" class:collapsed={!$isFocused}>
|
||||
{#if $searchTerm.length > 0 && results === undefined}
|
||||
{#if results?.["error"] !== undefined}
|
||||
<div class="searchbox normal-background items-center">
|
||||
An error occured
|
||||
</div>
|
||||
|
||||
{:else if $searchTerm.length > 0 && results === undefined}
|
||||
<div class="searchbox normal-background items-center">
|
||||
<Loading />
|
||||
</div>
|
||||
{:else if results?.length > 0}
|
||||
{:else if results?.["success"]?.length > 0}
|
||||
<div class="relative w-full h-full">
|
||||
<div class="absolute top-0 right-0 searchbox normal-background"
|
||||
style="width: 25rem">
|
||||
<div style="max-height: calc(50vh - 1rem - 2px);" class="overflow-y-auto">
|
||||
|
||||
{#each results as entry (entry)}
|
||||
{#each results["success"] as entry (entry)}
|
||||
<SearchResult on:select {entry} {state} />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="absolute top-2 right-2 cursor-pointer" on:click={() => close()}>
|
||||
<div class="absolute top-2 right-2 cursor-pointer" on:click={() => isFocused.setData(false)}>
|
||||
<XMarkIcon class="w-4 h-4 hover:bg-stone-200 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
{:else }
|
||||
{:else if $searchTerm.length > 0 || $recentlySeen?.length > 0 || $recentThemes?.length > 0}
|
||||
|
||||
<div class="searchbox normal-background ">
|
||||
{#if $searchTerm.length > 0}
|
||||
<!-- TODO add translation -->
|
||||
<b class="flex justify-center p-4">No results found for {$searchTerm}</b>
|
||||
{/if}
|
||||
<div class="searchbox normal-background overflow-y-auto h-full">
|
||||
{#if $searchTerm.length > 0}
|
||||
<b class="flex justify-center p-4">
|
||||
<Tr t={Translations.t.general.search.nothingFor.Subs({term: $searchTerm})} />
|
||||
</b>
|
||||
{/if}
|
||||
|
||||
{#if $recentlySeen?.length > 0}
|
||||
<!-- TODO add translation -->
|
||||
<h4>Recent searches</h4>
|
||||
{#if $recentlySeen?.length > 0}
|
||||
<h3 class="mx-2">
|
||||
<Tr t={Translations.t.general.search.recents} />
|
||||
</h3>
|
||||
{#each $recentlySeen as entry}
|
||||
<SearchResult {entry} {state} on:select />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if $recentThemes?.length > 0 && $allowOtherThemes}
|
||||
<h3 class="mx-2">
|
||||
<Tr t={Translations.t.general.search.recentThemes} />
|
||||
</h3>
|
||||
{#each $recentThemes as themeId (themeId)}
|
||||
<SearchResult
|
||||
entry={{payload: MoreScreen.officialThemesById.get(themeId), display_name: themeId, lat: 0, lon: 0}} {state}
|
||||
on:select />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.searchbox {
|
||||
display: flex;
|
||||
|
|
@ -68,7 +90,8 @@
|
|||
|
||||
.collapsable {
|
||||
max-height: 50vh;
|
||||
transition: max-height 350ms ease-in-out;
|
||||
transition: max-height 400ms linear;
|
||||
transition-delay: 500ms;
|
||||
overflow: hidden;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { Translation } from "../i18n/Translation"
|
||||
import * as personal from "../../../assets/themes/personal/personal.json"
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import UserDetails, { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
|
|
|
|||
|
|
@ -23,15 +23,16 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
"dragRotate",
|
||||
"dragPan",
|
||||
"keyboard",
|
||||
"touchZoomRotate",
|
||||
"touchZoomRotate"
|
||||
]
|
||||
private static maplibre_zoom_handlers = [
|
||||
"scrollZoom",
|
||||
"boxZoom",
|
||||
"doubleClickZoom",
|
||||
"touchZoomRotate",
|
||||
"touchZoomRotate"
|
||||
]
|
||||
readonly location: UIEventSource<{ lon: number; lat: number }>
|
||||
private readonly isFlying = new UIEventSource(false)
|
||||
readonly zoom: UIEventSource<number>
|
||||
readonly bounds: UIEventSource<BBox>
|
||||
readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined>
|
||||
|
|
@ -105,6 +106,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
new RasterLayerHandler(this._maplibreMap, this.rasterLayer)
|
||||
|
||||
const clickmodes = ["left", "middle", "right"] as const
|
||||
|
||||
function handleClick(e: maplibregl.MapMouseEvent, mode?: "left" | "right" | "middle") {
|
||||
if (e.originalEvent["consumed"]) {
|
||||
// Workaround, 'ShowPointLayer' sets this flag
|
||||
|
|
@ -144,7 +146,13 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
self.SetRotation(self.rotation.data)
|
||||
self.setTerrain(self.useTerrain.data)
|
||||
this.updateStores(true)
|
||||
map.on("moveend", () => this.updateStores())
|
||||
map.on("movestart", () => {
|
||||
this.isFlying.setData(true)
|
||||
})
|
||||
map.on("moveend", () => {
|
||||
this.isFlying.setData(false)
|
||||
this.updateStores()
|
||||
})
|
||||
map.on("click", (e) => {
|
||||
handleClick(e)
|
||||
})
|
||||
|
|
@ -228,9 +236,9 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
return {
|
||||
map: mlmap,
|
||||
ui: new SvelteUIElement(MaplibreMap, {
|
||||
map: mlmap,
|
||||
map: mlmap
|
||||
}),
|
||||
mapproperties: new MapLibreAdaptor(mlmap),
|
||||
mapproperties: new MapLibreAdaptor(mlmap)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -298,7 +306,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
) {
|
||||
const event = {
|
||||
date: new Date(),
|
||||
key: key,
|
||||
key: key
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._onKeyNavigation.length; i++) {
|
||||
|
|
@ -487,7 +495,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
const bounds = map.getBounds()
|
||||
const bbox = new BBox([
|
||||
[bounds.getEast(), bounds.getNorth()],
|
||||
[bounds.getWest(), bounds.getSouth()],
|
||||
[bounds.getWest(), bounds.getSouth()]
|
||||
])
|
||||
if (this.bounds.data === undefined || !isSetup) {
|
||||
this.bounds.setData(bbox)
|
||||
|
|
@ -501,6 +509,9 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
if (!map || z === undefined) {
|
||||
return
|
||||
}
|
||||
if (this.isFlying.data) {
|
||||
return
|
||||
}
|
||||
if (Math.abs(map.getZoom() - z) > 0.01) {
|
||||
map.setZoom(z)
|
||||
}
|
||||
|
|
@ -648,9 +659,22 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
if (!hasDiff) {
|
||||
return
|
||||
}
|
||||
this.lockZoom()
|
||||
map.fitBounds(bounds.toLngLat())
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called before making an animation.
|
||||
* First, 'isFlying' is set to true. This will disable the zoom control
|
||||
* Then, zoom is set to '1', which is very low. This will generally disable all layers, after which this function will return
|
||||
*
|
||||
* Then, a zoom/pan/... animation can be made; after which a 'moveEnd'-event will trigger the 'isFlying' to be set to false and the zoom to be set correctly
|
||||
*/
|
||||
private lockZoom() {
|
||||
this.isFlying.setData(true)
|
||||
this.zoom.setData(1)
|
||||
}
|
||||
|
||||
private async setTerrain(useTerrain: boolean) {
|
||||
const map = this._maplibreMap.data
|
||||
if (!map) {
|
||||
|
|
@ -665,14 +689,14 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
type: "raster-dem",
|
||||
url:
|
||||
"https://api.maptiler.com/tiles/terrain-rgb/tiles.json?key=" +
|
||||
Constants.maptilerApiKey,
|
||||
Constants.maptilerApiKey
|
||||
})
|
||||
try {
|
||||
while (!map?.isStyleLoaded()) {
|
||||
await Utils.waitFor(250)
|
||||
}
|
||||
map.setTerrain({
|
||||
source: id,
|
||||
source: id
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
|
@ -680,10 +704,13 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
}
|
||||
}
|
||||
|
||||
public flyTo(lon: number, lat: number, zoom: number){
|
||||
this._maplibreMap.data?.flyTo({
|
||||
zoom,
|
||||
center: [lon, lat],
|
||||
public flyTo(lon: number, lat: number, zoom: number) {
|
||||
this.lockZoom()
|
||||
window.requestAnimationFrame(() => {
|
||||
this._maplibreMap.data?.flyTo({
|
||||
zoom,
|
||||
center: [lon, lat]
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||
import BaseUIElement from "./BaseUIElement"
|
||||
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
|
||||
import LayoutConfig, { MinimalLayoutInformation } from "../Models/ThemeConfig/LayoutConfig"
|
||||
import {
|
||||
FeatureSource,
|
||||
IndexedFeatureSource,
|
||||
|
|
@ -87,6 +87,7 @@ export interface SpecialVisualizationState {
|
|||
readonly showAllQuestionsAtOnce: UIEventSource<boolean>
|
||||
readonly preferencesAsTags: UIEventSource<Record<string, string>>
|
||||
readonly language: UIEventSource<string>
|
||||
readonly recentlyVisitedThemes: Store<string[]>
|
||||
}
|
||||
|
||||
readonly availableLayers: Store<RasterLayerPolygon[]>
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
return "offline"
|
||||
}
|
||||
}),
|
||||
message: osmApi,
|
||||
message: osmApi
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +90,7 @@
|
|||
}
|
||||
const files: string[] = s["success"]["allFiles"]
|
||||
return "Contains " + (files.length ?? "no") + " files"
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
{
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
return "degraded"
|
||||
}
|
||||
}),
|
||||
message: simpleMessage(testDownload(Constants.GeoIpServer + "/ip")),
|
||||
message: simpleMessage(testDownload(Constants.GeoIpServer + "/ip"))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +125,7 @@
|
|||
}
|
||||
return "degraded"
|
||||
}),
|
||||
message: simpleMessage(status),
|
||||
message: simpleMessage(status)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +144,7 @@
|
|||
}
|
||||
return "online"
|
||||
}),
|
||||
message: simpleMessage(status),
|
||||
message: simpleMessage(status)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +183,7 @@
|
|||
|
||||
const json = JSON.stringify(s["success"], null, " ")
|
||||
return "Database is " + Math.floor(timediffDays) + " days out of sync\n\n" + json
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +202,45 @@
|
|||
}
|
||||
return "degraded"
|
||||
}),
|
||||
message: status.map((s) => JSON.stringify(s)),
|
||||
message: status.map((s) => JSON.stringify(s))
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
const s = Constants.nominatimEndpoint
|
||||
const status = testDownload(s + "/search.php?q=Brugge")
|
||||
services.push({
|
||||
name: s,
|
||||
message: simpleMessage(status),
|
||||
status: status.mapD(s => {
|
||||
if (s["error"]) {
|
||||
return "offline"
|
||||
}
|
||||
const data = s["success"]
|
||||
if (Array.isArray(data)) {
|
||||
return "online"
|
||||
}
|
||||
return "degraded"
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
const s = Constants.photonEndpoint
|
||||
const status = testDownload(s + "/api/?q=Brugge")
|
||||
services.push({
|
||||
name: s,
|
||||
status: status.mapD(s => {
|
||||
if (s["error"]) {
|
||||
return "offline"
|
||||
}
|
||||
const data = s["success"]
|
||||
if (Array.isArray(data.features) && data.features.length > 0) {
|
||||
return "online"
|
||||
}
|
||||
return "degraded"
|
||||
}),
|
||||
message: simpleMessage(status)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +266,7 @@
|
|||
|
||||
return "online"
|
||||
}),
|
||||
message: simpleMessage(status),
|
||||
message: simpleMessage(status)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -241,7 +279,7 @@
|
|||
return "online"
|
||||
}
|
||||
return "offline"
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue