UX: fix #2089, improve background selection hotkeys and add emoji to indicate categories of background layers

This commit is contained in:
Pieter Vander Vennet 2024-08-23 03:47:04 +02:00
parent 5b67ccc9e2
commit 2dc386fd9a
15 changed files with 600 additions and 525 deletions

View file

@ -90,11 +90,11 @@
"minzoom": 6, "minzoom": 6,
"title": { "title": {
"render": { "render": {
"en": "Cycle highway", "en": "cycle highway",
"de": "Radschnellweg", "de": "Radschnellweg",
"ca": "Via ciclista", "ca": "via ciclista",
"fr": "Aménagement cyclable", "fr": "Aménagement cyclable",
"nl": "Fietssnelweg", "nl": "fietssnelweg",
"es": "autovía ciclista", "es": "autovía ciclista",
"nb_NO": "sykkelmotorvei", "nb_NO": "sykkelmotorvei",
"da": "cykelmotorvej", "da": "cykelmotorvej",

View file

@ -374,11 +374,15 @@
"fr": "Quel fond souhaitez-vous utiliser par défaut?", "fr": "Quel fond souhaitez-vous utiliser par défaut?",
"da": "Hvilket baggrundslag skal vises som standard?" "da": "Hvilket baggrundslag skal vises som standard?"
}, },
"questionHint":{
"en": "To set a specific background as default, select it in the background menu first after which it will appear here."
},
"condition": "_theme:backgroundLayer=", "condition": "_theme:backgroundLayer=",
"mappings": [ "mappings": [
{ {
"if": "mapcomplete-preferred-background-layer=default", "if": "mapcomplete-preferred-background-layer=default",
"alsoShowIf": "mapcomplete-preferred-background-layer=", "alsoShowIf": "mapcomplete-preferred-background-layer=",
"icon": "./assets/svg/generic_map.svg",
"then": { "then": {
"en": "Use the default background layer", "en": "Use the default background layer",
"ca": "Utilitzeu la capa de fons predeterminada", "ca": "Utilitzeu la capa de fons predeterminada",
@ -391,6 +395,9 @@
}, },
{ {
"if": "mapcomplete-preferred-background-layer=osm", "if": "mapcomplete-preferred-background-layer=osm",
"icon": {
"path":"./assets/svg/osm-logo.svg"
},
"then": { "then": {
"en": "Use OpenStreetMap-carto as default layer", "en": "Use OpenStreetMap-carto as default layer",
"ca": "Utilitzeu OpenStreetMap-carto com a capa predeterminada", "ca": "Utilitzeu OpenStreetMap-carto com a capa predeterminada",
@ -403,6 +410,7 @@
}, },
{ {
"if": "mapcomplete-preferred-background-layer=photo", "if": "mapcomplete-preferred-background-layer=photo",
"icon": "\uD83D\uDEF0\uFE0F",
"then": { "then": {
"en": "Use aerial imagery as default background", "en": "Use aerial imagery as default background",
"ca": "Utilitzeu imatges aèries com a fons predeterminat", "ca": "Utilitzeu imatges aèries com a fons predeterminat",
@ -415,6 +423,7 @@
}, },
{ {
"if": "mapcomplete-preferred-background-layer=map", "if": "mapcomplete-preferred-background-layer=map",
"icon": "./assets/svg/generic_map.svg",
"then": { "then": {
"en": "Use a non-openstreetmap based map as default background", "en": "Use a non-openstreetmap based map as default background",
"ca": "Utilitzeu un mapa que no sigui openstreetmap com a fons predeterminat", "ca": "Utilitzeu un mapa que no sigui openstreetmap com a fons predeterminat",

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 128 KiB

View file

@ -74,7 +74,6 @@
] ]
} }
}, },
"cycle_highways" "cycle_highways"
], ],
"overpassTimeout": 60, "overpassTimeout": 60,

View file

@ -39,10 +39,10 @@
.mapping-icon-small { .mapping-icon-small {
/* A mapping icon type */ /* A mapping icon type */
width: 1.5rem; width: 2rem;
height: fit-content; height: fit-content;
max-height: 1.5rem; max-height: 2rem;
margin-right: 0.5rem; margin-right: 0.5rem;
} }

View file

@ -19,13 +19,14 @@ export default class BackgroundLayerResetter {
return return
} }
currentBackgroundLayer.addCallbackAndRunD((l) => { currentBackgroundLayer.addCallbackAndRunD(async (l) => {
if ( if (
l.geometry !== undefined && l.geometry !== undefined &&
AvailableRasterLayers.globalLayers.find( AvailableRasterLayers.globalLayers.find(
(global) => global.properties.id !== l.properties.id (global) => global.properties.id !== l.properties.id
) )
) { ) {
await AvailableRasterLayers.editorLayerIndex()
BackgroundLayerResetter.installHandler( BackgroundLayerResetter.installHandler(
currentBackgroundLayer, currentBackgroundLayer,
availableLayers.store availableLayers.store

View file

@ -61,9 +61,9 @@ export class PreferredRasterLayerSelector {
* Returns 'true' if the target layer is set or is the current layer * Returns 'true' if the target layer is set or is the current layer
* @private * @private
*/ */
private updateLayer() { private async updateLayer() {
// What is the ID of the layer we have to (try to) load? // What is the ID of the layer we have to (try to) load?
const targetLayerId = this._queryParameter.data ?? this._preferredBackgroundLayer.data const targetLayerId = (this._queryParameter.data ?? this._preferredBackgroundLayer.data)?.toLowerCase()
if (targetLayerId === undefined || targetLayerId === "default") { if (targetLayerId === undefined || targetLayerId === "default") {
return return
} }
@ -74,12 +74,13 @@ export class PreferredRasterLayerSelector {
this._rasterLayerSetting.setData(global) this._rasterLayerSetting.setData(global)
return return
} }
await AvailableRasterLayers.editorLayerIndex()
const isCategory = const isCategory =
targetLayerId === "photo" || targetLayerId === "osmbasedmap" || targetLayerId === "map" targetLayerId === "photo" || targetLayerId === "osmbasedmap" || targetLayerId === "map"
const available = this._availableLayers.store.data const available = this._availableLayers.store.data
const foundLayer = isCategory const foundLayer = isCategory
? available.find((l) => l.properties.category === targetLayerId) ? available.find((l) => l.properties.category === targetLayerId)
: available.find((l) => l.properties.id === targetLayerId) : available.find((l) => l.properties.id.toLowerCase() === targetLayerId)
console.debug("Updating background layer to", foundLayer?.id, { console.debug("Updating background layer to", foundLayer?.id, {
targetLayerId, targetLayerId,
queryParam: this._queryParameter?.data, queryParam: this._queryParameter?.data,

View file

@ -28,7 +28,7 @@ export class AvailableRasterLayers {
return this._editorLayerIndex return this._editorLayerIndex
} }
public static globalLayers: RasterLayerPolygon[] = globallayers.layers public static globalLayers: ReadonlyArray<RasterLayerPolygon> = globallayers.layers
.filter( .filter(
(properties) => (properties) =>
properties.id !== "osm.carto" && properties.id !== "Bing" /*Added separately*/ properties.id !== "osm.carto" && properties.id !== "Bing" /*Added separately*/
@ -140,28 +140,24 @@ export class RasterLayerUtils {
* @param available * @param available
* @param preferredCategory * @param preferredCategory
* @param ignoreLayer * @param ignoreLayer
* @param skipLayers Skip the first N layers
*/ */
public static SelectBestLayerAccordingTo( public static SelectBestLayerAccordingTo(
available: RasterLayerPolygon[], available: RasterLayerPolygon[],
preferredCategory: string, preferredCategory: string,
ignoreLayer?: RasterLayerPolygon ignoreLayer?: RasterLayerPolygon,
skipLayers: number = 0
): RasterLayerPolygon { ): RasterLayerPolygon {
let secondBest: RasterLayerPolygon = undefined const inCategory = available.filter(l => l.properties.category === preferredCategory)
for (const rasterLayer of available) { const best : RasterLayerPolygon[] = inCategory.filter(l => l.properties.best)
if (rasterLayer === ignoreLayer) { const others : RasterLayerPolygon[] = inCategory.filter(l => !l.properties.best)
continue let all = best.concat(others)
} console.log("Selected layers are:", all.map(l => l.properties.id))
const p = rasterLayer.properties if(others.length > skipLayers){
if (p.category === preferredCategory) { all = all.slice(skipLayers)
if (p.best) {
return rasterLayer
}
if (!secondBest) {
secondBest = rasterLayer
}
}
} }
return secondBest
return all.find(l => l !== ignoreLayer)
} }
} }

View file

@ -161,7 +161,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.featureSwitches = new FeatureSwitchState(layout) this.featureSwitches = new FeatureSwitchState(layout)
this.guistate = new MenuState( this.guistate = new MenuState(
this.featureSwitches.featureSwitchWelcomeMessage.data, this.featureSwitches.featureSwitchWelcomeMessage.data,
layout.id layout.id,
) )
this.map = new UIEventSource<MlMap>(undefined) this.map = new UIEventSource<MlMap>(undefined)
const geolocationState = new GeoLocationState() const geolocationState = new GeoLocationState()
@ -177,14 +177,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
oauth_token: QueryParameters.GetQueryParameter( oauth_token: QueryParameters.GetQueryParameter(
"oauth_token", "oauth_token",
undefined, undefined,
"Used to complete the login" "Used to complete the login",
), ),
}) })
this.userRelatedState = new UserRelatedState( this.userRelatedState = new UserRelatedState(
this.osmConnection, this.osmConnection,
layout, layout,
this.featureSwitches, this.featureSwitches,
this.mapProperties this.mapProperties,
) )
this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => { this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => {
this.mapProperties.allowRotating.setData(fixated !== "yes") this.mapProperties.allowRotating.setData(fixated !== "yes")
@ -195,20 +195,20 @@ export default class ThemeViewState implements SpecialVisualizationState {
geolocationState, geolocationState,
this.selectedElement, this.selectedElement,
this.mapProperties, this.mapProperties,
this.userRelatedState.gpsLocationHistoryRetentionTime this.userRelatedState.gpsLocationHistoryRetentionTime,
) )
this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties) this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties)
this.availableLayers = AvailableRasterLayers.layersAvailableAt( this.availableLayers = AvailableRasterLayers.layersAvailableAt(
this.mapProperties.location, this.mapProperties.location,
this.osmConnection.isLoggedIn this.osmConnection.isLoggedIn,
) )
this.layerState = new LayerState( this.layerState = new LayerState(
this.osmConnection, this.osmConnection,
layout.layers, layout.layers,
layout.id, layout.id,
this.featureSwitches.featureSwitchLayerDefault this.featureSwitches.featureSwitchLayerDefault,
) )
{ {
@ -217,7 +217,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const isDisplayed = QueryParameters.GetBooleanQueryParameter( const isDisplayed = QueryParameters.GetBooleanQueryParameter(
"overlay-" + rasterInfo.id, "overlay-" + rasterInfo.id,
rasterInfo.defaultState ?? true, rasterInfo.defaultState ?? true,
"Whether or not overlay layer " + rasterInfo.id + " is shown" "Whether or not overlay layer " + rasterInfo.id + " is shown",
) )
const state = { isDisplayed } const state = { isDisplayed }
overlayLayerStates.set(rasterInfo.id, state) overlayLayerStates.set(rasterInfo.id, state)
@ -242,7 +242,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.osmConnection.Backend(), this.osmConnection.Backend(),
(id) => this.layerState.filteredLayers.get(id).isDisplayed, (id) => this.layerState.filteredLayers.get(id).isDisplayed,
mvtAvailableLayers, mvtAvailableLayers,
this.fullNodeDatabase this.fullNodeDatabase,
) )
let currentViewIndex = 0 let currentViewIndex = 0
@ -260,7 +260,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
id: "current_view_" + currentViewIndex, id: "current_view_" + currentViewIndex,
}), }),
] ]
}) }),
) )
this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds) this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds)
@ -278,19 +278,19 @@ export default class ThemeViewState implements SpecialVisualizationState {
featureSwitches: this.featureSwitches, featureSwitches: this.featureSwitches,
}, },
layout?.isLeftRightSensitive() ?? false, layout?.isLeftRightSensitive() ?? false,
(e) => this.reportError(e) (e) => this.reportError(e),
) )
this.historicalUserLocations = this.geolocation.historicalUserLocations this.historicalUserLocations = this.geolocation.historicalUserLocations
this.newFeatures = new NewGeometryFromChangesFeatureSource( this.newFeatures = new NewGeometryFromChangesFeatureSource(
this.changes, this.changes,
layoutSource, layoutSource,
this.featureProperties this.featureProperties,
) )
layoutSource.addSource(this.newFeatures) layoutSource.addSource(this.newFeatures)
const perLayer = new PerLayerFeatureSourceSplitter( const perLayer = new PerLayerFeatureSourceSplitter(
Array.from(this.layerState.filteredLayers.values()).filter( Array.from(this.layerState.filteredLayers.values()).filter(
(l) => l.layerDef?.source !== null (l) => l.layerDef?.source !== null,
), ),
new ChangeGeometryApplicator(this.indexedFeatures, this.changes), new ChangeGeometryApplicator(this.indexedFeatures, this.changes),
{ {
@ -301,10 +301,10 @@ export default class ThemeViewState implements SpecialVisualizationState {
"Got ", "Got ",
features.length, features.length,
"leftover features, such as", "leftover features, such as",
features[0].properties features[0].properties,
) )
}, },
} },
) )
this.perLayer = perLayer.perLayer this.perLayer = perLayer.perLayer
} }
@ -344,12 +344,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.lastClickObject = new LastClickFeatureSource( this.lastClickObject = new LastClickFeatureSource(
this.layout, this.layout,
this.mapProperties.lastClickLocation, this.mapProperties.lastClickLocation,
this.userRelatedState.addNewFeatureMode this.userRelatedState.addNewFeatureMode,
) )
this.osmObjectDownloader = new OsmObjectDownloader( this.osmObjectDownloader = new OsmObjectDownloader(
this.osmConnection.Backend(), this.osmConnection.Backend(),
this.changes this.changes,
) )
this.perLayerFiltered = this.showNormalDataOn(this.map) this.perLayerFiltered = this.showNormalDataOn(this.map)
@ -360,7 +360,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
currentZoom: this.mapProperties.zoom, currentZoom: this.mapProperties.zoom,
layerState: this.layerState, layerState: this.layerState,
bounds: this.visualFeedbackViewportBounds, bounds: this.visualFeedbackViewportBounds,
} },
) )
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
this.imageUploadManager = new ImageUploadManager( this.imageUploadManager = new ImageUploadManager(
@ -368,7 +368,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
Imgur.singleton, Imgur.singleton,
this.featureProperties, this.featureProperties,
this.osmConnection, this.osmConnection,
this.changes this.changes,
) )
this.favourites = new FavouritesFeatureSource(this) this.favourites = new FavouritesFeatureSource(this)
const longAgo = new Date() const longAgo = new Date()
@ -414,7 +414,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
LayoutSource.fromCacheZoomLevel, LayoutSource.fromCacheZoomLevel,
fs, fs,
this.featureProperties, this.featureProperties,
fs.layer.layerDef.maxAgeOfCache fs.layer.layerDef.maxAgeOfCache,
) )
toLocalStorage.set(layerId, storage) toLocalStorage.set(layerId, storage)
}) })
@ -427,7 +427,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const doShowLayer = this.mapProperties.zoom.map( const doShowLayer = this.mapProperties.zoom.map(
(z) => (z) =>
(fs.layer.isDisplayed?.data ?? true) && z >= (fs.layer.layerDef?.minzoom ?? 0), (fs.layer.isDisplayed?.data ?? true) && z >= (fs.layer.layerDef?.minzoom ?? 0),
[fs.layer.isDisplayed] [fs.layer.isDisplayed],
) )
if (!doShowLayer.data && this.featureSwitches.featureSwitchFilter.data === false) { if (!doShowLayer.data && this.featureSwitches.featureSwitchFilter.data === false) {
@ -444,7 +444,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
fs.layer, fs.layer,
fs, fs,
(id) => this.featureProperties.getStore(id), (id) => this.featureProperties.getStore(id),
this.layerState.globalFilters this.layerState.globalFilters,
) )
filteringFeatureSource.set(layerName, filtered) filteringFeatureSource.set(layerName, filtered)
@ -588,7 +588,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
return return
} }
this.selectClosestAtCenter(0) this.selectClosestAtCenter(0)
} },
) )
for (let i = 1; i < 9; i++) { for (let i = 1; i < 9; i++) {
@ -606,7 +606,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
onUp: true, onUp: true,
}, },
doc, doc,
() => this.selectClosestAtCenter(i - 1) () => this.selectClosestAtCenter(i - 1),
) )
} }
@ -623,7 +623,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
if (this.featureSwitches.featureSwitchBackgroundSelection.data) { if (this.featureSwitches.featureSwitchBackgroundSelection.data) {
this.guistate.backgroundLayerSelectionIsOpened.setData(true) this.guistate.backgroundLayerSelectionIsOpened.setData(true)
} }
} },
) )
Hotkeys.RegisterHotkey( Hotkeys.RegisterHotkey(
{ {
@ -635,18 +635,11 @@ export default class ThemeViewState implements SpecialVisualizationState {
if (this.featureSwitches.featureSwitchFilter.data) { if (this.featureSwitches.featureSwitchFilter.data) {
this.guistate.openFilterView() this.guistate.openFilterView()
} }
} },
) )
Hotkeys.RegisterHotkey( const setLayerCategory = (category: EliCategory, skipLayers: number = 0) => {
{ shift: "O" },
Translations.t.hotkeyDocumentation.selectMapnik,
() => {
this.mapProperties.rasterLayer.setData(AvailableRasterLayers.osmCarto)
}
)
const setLayerCategory = (category: EliCategory) => {
const timeOfCall = new Date() const timeOfCall = new Date()
const available = this.availableLayers.store.addCallbackAndRunD((available) => { this.availableLayers.store.addCallbackAndRunD((available) => {
const now = new Date() const now = new Date()
const timeDiff = (now.getTime() - timeOfCall.getTime()) / 1000 const timeDiff = (now.getTime() - timeOfCall.getTime()) / 1000
if (timeDiff > 3) { if (timeDiff > 3) {
@ -656,9 +649,13 @@ export default class ThemeViewState implements SpecialVisualizationState {
const best = RasterLayerUtils.SelectBestLayerAccordingTo( const best = RasterLayerUtils.SelectBestLayerAccordingTo(
available, available,
category, category,
current.data current.data,
skipLayers
) )
console.log("Best layer for category", category, "is", best.properties.id) if(!best){
return
}
console.log("Best layer for category", category, "is", best?.properties?.id)
current.setData(best) current.setData(best)
}) })
} }
@ -666,26 +663,43 @@ export default class ThemeViewState implements SpecialVisualizationState {
Hotkeys.RegisterHotkey( Hotkeys.RegisterHotkey(
{ nomod: "O" }, { nomod: "O" },
Translations.t.hotkeyDocumentation.selectOsmbasedmap, Translations.t.hotkeyDocumentation.selectOsmbasedmap,
() => setLayerCategory("osmbasedmap") () => setLayerCategory("osmbasedmap"),
) )
Hotkeys.RegisterHotkey( Hotkeys.RegisterHotkey(
{ nomod: "M" }, { nomod: "M" },
Translations.t.hotkeyDocumentation.selectMap, Translations.t.hotkeyDocumentation.selectMap,
() => setLayerCategory("map") () => setLayerCategory("map"),
) )
Hotkeys.RegisterHotkey( Hotkeys.RegisterHotkey(
{ nomod: "P" }, { nomod: "P" },
Translations.t.hotkeyDocumentation.selectAerial, Translations.t.hotkeyDocumentation.selectAerial,
() => setLayerCategory("photo") () => setLayerCategory("photo"),
)
Hotkeys.RegisterHotkey(
{ shift: "O" },
Translations.t.hotkeyDocumentation.selectOsmbasedmap,
() => setLayerCategory("osmbasedmap",2),
)
Hotkeys.RegisterHotkey(
{ shift: "M" },
Translations.t.hotkeyDocumentation.selectMap,
() => setLayerCategory("map",2),
)
Hotkeys.RegisterHotkey(
{ shift: "P" },
Translations.t.hotkeyDocumentation.selectAerial,
() => setLayerCategory("photo",2),
) )
Hotkeys.RegisterHotkey( Hotkeys.RegisterHotkey(
{ nomod: "L" }, { nomod: "L" },
Translations.t.hotkeyDocumentation.geolocate, Translations.t.hotkeyDocumentation.geolocate,
() => { () => {
this.geolocationControl.handleClick() this.geolocationControl.handleClick()
} },
) )
return true return true
}) })
@ -697,7 +711,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
Translations.t.hotkeyDocumentation.translationMode, Translations.t.hotkeyDocumentation.translationMode,
() => { () => {
Locale.showLinkToWeblate.setData(!Locale.showLinkToWeblate.data) Locale.showLinkToWeblate.setData(!Locale.showLinkToWeblate.data)
} },
) )
} }
@ -708,7 +722,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const normalLayers = this.layout.layers.filter( const normalLayers = this.layout.layers.filter(
(l) => (l) =>
Constants.priviliged_layers.indexOf(<any>l.id) < 0 && Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
!l.id.startsWith("note_import") !l.id.startsWith("note_import"),
) )
const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom)) const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom))
@ -716,7 +730,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
(l) => (l) =>
Constants.priviliged_layers.indexOf(<any>l.id) < 0 && Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
l.source.geojsonSource === undefined && l.source.geojsonSource === undefined &&
l.doCount l.doCount,
) )
const summaryTileSource = new SummaryTileSource( const summaryTileSource = new SummaryTileSource(
Constants.SummaryServer, Constants.SummaryServer,
@ -725,7 +739,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.mapProperties, this.mapProperties,
{ {
isActive: this.mapProperties.zoom.map((z) => z < maxzoom), isActive: this.mapProperties.zoom.map((z) => z < maxzoom),
} },
) )
return new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers) return new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers)
@ -746,12 +760,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
gps_location_history: this.geolocation.historicalUserLocations, gps_location_history: this.geolocation.historicalUserLocations,
gps_track: this.geolocation.historicalUserLocationsTrack, gps_track: this.geolocation.historicalUserLocationsTrack,
selected_element: new StaticFeatureSource( selected_element: new StaticFeatureSource(
this.selectedElement.map((f) => (f === undefined ? empty : [f])) this.selectedElement.map((f) => (f === undefined ? empty : [f])),
), ),
range: new StaticFeatureSource( range: new StaticFeatureSource(
this.mapProperties.maxbounds.map((bbox) => 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, current_view: this.currentView,
favourite: this.favourites, favourite: this.favourites,
@ -766,7 +780,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
ShowDataLayer.showRange( ShowDataLayer.showRange(
this.map, this.map,
new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]), new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]),
this.featureSwitches.featureSwitchIsTesting this.featureSwitches.featureSwitchIsTesting,
) )
} }
const currentViewLayer = this.layout.layers.find((l) => l.id === "current_view") const currentViewLayer = this.layout.layers.find((l) => l.id === "current_view")
@ -780,7 +794,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
currentViewLayer, currentViewLayer,
this.layout, this.layout,
this.osmObjectDownloader, this.osmObjectDownloader,
this.featureProperties this.featureProperties,
) )
}) })
} }
@ -824,20 +838,20 @@ export default class ThemeViewState implements SpecialVisualizationState {
const lastClickLayerConfig = new LayerConfig( const lastClickLayerConfig = new LayerConfig(
<LayerConfigJson>last_click_layerconfig, <LayerConfigJson>last_click_layerconfig,
"last_click" "last_click",
) )
const lastClickFiltered = const lastClickFiltered =
lastClickLayerConfig.isShown === undefined lastClickLayerConfig.isShown === undefined
? specialLayers.last_click ? specialLayers.last_click
: specialLayers.last_click.features.mapD((fs) => : specialLayers.last_click.features.mapD((fs) =>
fs.filter((f) => { fs.filter((f) => {
const matches = lastClickLayerConfig.isShown.matchesProperties( const matches = lastClickLayerConfig.isShown.matchesProperties(
f.properties f.properties,
) )
console.debug("LastClick ", f, "matches", matches) console.debug("LastClick ", f, "matches", matches)
return matches return matches
}) }),
) )
new ShowDataLayer(this.map, { new ShowDataLayer(this.map, {
features: new StaticFeatureSource(lastClickFiltered), features: new StaticFeatureSource(lastClickFiltered),
layer: lastClickLayerConfig, layer: lastClickLayerConfig,
@ -884,7 +898,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.mapProperties.rasterLayer, this.mapProperties.rasterLayer,
this.availableLayers, this.availableLayers,
this.featureSwitches.backgroundLayerId, this.featureSwitches.backgroundLayerId,
this.userRelatedState.preferredBackgroundLayer this.userRelatedState.preferredBackgroundLayer,
) )
} }
@ -900,7 +914,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
? ">>> _Not_ reporting error to report server as testmode is on" ? ">>> _Not_ reporting error to report server as testmode is on"
: ">>> Reporting error to", : ">>> Reporting error to",
Constants.ErrorReportServer, Constants.ErrorReportServer,
message message,
) )
if (isTesting) { if (isTesting) {
return return

View file

@ -50,7 +50,7 @@
export let color: string | undefined = undefined export let color: string | undefined = undefined
export let clss: string | undefined = "" export let clss: string | undefined = ""
clss ??= "" clss ??= ""
export let emojiHeight = 40 export let emojiHeight = "40px"
</script> </script>
{#if icon} {#if icon}
@ -147,7 +147,7 @@
{:else if icon === "user_circle"} {:else if icon === "user_circle"}
<UserCircleIcon class={clss} {color} /> <UserCircleIcon class={clss} {color} />
{:else if Utils.isEmoji(icon)} {:else if Utils.isEmoji(icon)}
<span style={`font-size: ${emojiHeight}px; line-height: ${emojiHeight}px`}> <span style= {`font-size: ${emojiHeight}; line-height: ${emojiHeight}`}>
{icon} {icon}
</span> </span>
{:else} {:else}

View file

@ -30,6 +30,7 @@
* Class which is applied onto the individual icons * Class which is applied onto the individual icons
*/ */
export let clss = "" export let clss = ""
export let emojiHeight : string = "40px"
/** /**
* Class applied onto the entire element * Class applied onto the entire element
@ -41,7 +42,7 @@
<div class={twMerge("relative", size)}> <div class={twMerge("relative", size)}>
{#each iconsParsed as icon} {#each iconsParsed as icon}
<div class="absolute top-0 left-0 flex h-full w-full items-center"> <div class="absolute top-0 left-0 flex h-full w-full items-center">
<Icon icon={icon.icon} color={icon.color} {clss} /> <Icon icon={icon.icon} color={icon.color} {clss} {emojiHeight} />
</div> </div>
{/each} {/each}
</div> </div>

View file

@ -23,7 +23,7 @@
let rasterLayerId = rasterLayer.sync( let rasterLayerId = rasterLayer.sync(
(l) => l?.properties?.id, (l) => l?.properties?.id,
[], [],
(id) => availableLayers.find((l) => l.properties.id === id) (id) => availableLayers.find((l) => l.properties.id === id),
) )
rasterLayer.setData(availableLayers[0]) rasterLayer.setData(availableLayers[0])
$: rasterLayer.setData(availableLayers[0]) $: rasterLayer.setData(availableLayers[0])
@ -36,13 +36,13 @@
return return
} }
rasterLayer.setData(fav) rasterLayer.setData(fav)
}) }),
) )
onDestroy( onDestroy(
rasterLayer.addCallbackAndRunD((selected) => { rasterLayer.addCallbackAndRunD((selected) => {
favourite?.setData(selected.properties.id) favourite?.setData(selected.properties.id)
}) }),
) )
} }
@ -56,7 +56,7 @@
} else { } else {
rasterLayerOnMap.setData(undefined) rasterLayerOnMap.setData(undefined)
} }
}) }),
) )
} }
@ -93,6 +93,15 @@
{#each availableLayers as availableLayer} {#each availableLayers as availableLayer}
<option value={availableLayer.properties.id}> <option value={availableLayer.properties.id}>
{availableLayer.properties.name} {availableLayer.properties.name}
{#if availableLayer.properties.category.startsWith("historic")}
⏱️
{/if}
{#if availableLayer.properties.category.endsWith("elevation")}
{/if}
{#if availableLayer.properties.best}
{/if}
</option> </option>
{/each} {/each}
</select> </select>

View file

@ -30,6 +30,12 @@
| "large-height" | "large-height"
| string | string
} }
const emojiHeights = {
"small":"2rem",
"medium":"3rem",
"large":"5rem"
}
</script> </script>
{#if mapping.icon !== undefined} {#if mapping.icon !== undefined}
@ -42,6 +48,9 @@
}-width`, }-width`,
"shrink-0" "shrink-0"
)} )}
emojiHeight={ emojiHeights[mapping.iconClass] ?? "2rem"}
clss={`mapping-icon-${mapping.iconClass ?? "small"}`} clss={`mapping-icon-${mapping.iconClass ?? "small"}`}
/> />
<SpecialTranslation t={mapping.then} {tags} {state} {layer} feature={selectedElement} {clss} /> <SpecialTranslation t={mapping.then} {tags} {state} {layer} feature={selectedElement} {clss} />

View file

@ -1727,11 +1727,12 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
} }
} }
private static emojiRegex = /^\p{Extended_Pictographic}+$/u private static emojiRegex = /[\p{Extended_Pictographic}🛰]$/u
/** /**
* Returns 'true' if the given string contains at least one and only emoji characters * Returns 'true' if the given string contains at least one and only emoji characters
* @param string *
* Utils.isEmoji("⛰\uFE0F") // => true
*/ */
public static isEmoji(string: string) { public static isEmoji(string: string) {
return Utils.emojiRegex.test(string) return Utils.emojiRegex.test(string)

View file

@ -1,5 +1,21 @@
{ {
"layers": [ "layers": [
{
"url": "pmtiles://https://api.protomaps.com/tiles/v3.json?key=2af8b969a9e8b692",
"style": "assets/sunny.json",
"connect-src": [
"https://protomaps.github.io"
],
"best": true,
"id": "protomaps.sunny",
"name": "Protomaps Sunny",
"type": "vector",
"category": "osmbasedmap",
"attribution": {
"text": "Protomaps",
"url": "https://protomaps.com/"
}
},
{ {
"name": "OpenStreetMap Carto", "name": "OpenStreetMap Carto",
"url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
@ -87,21 +103,6 @@
"url": "https://protomaps.com/" "url": "https://protomaps.com/"
} }
}, },
{
"url": "pmtiles://https://api.protomaps.com/tiles/v3.json?key=2af8b969a9e8b692",
"style": "assets/sunny.json",
"connect-src": [
"https://protomaps.github.io"
],
"id": "protomaps.sunny",
"name": "Protomaps Sunny",
"type": "vector",
"category": "osmbasedmap",
"attribution": {
"text": "Protomaps",
"url": "https://protomaps.com/"
}
},
{ {
"url": "pmtiles://https://api.protomaps.com/tiles/v3.json?key=2af8b969a9e8b692", "url": "pmtiles://https://api.protomaps.com/tiles/v3.json?key=2af8b969a9e8b692",
"style": "assets/sunny-unlabeled.json", "style": "assets/sunny-unlabeled.json",