forked from MapComplete/MapComplete
UX: fix #2089, improve background selection hotkeys and add emoji to indicate categories of background layers
This commit is contained in:
parent
5b67ccc9e2
commit
2dc386fd9a
15 changed files with 600 additions and 525 deletions
|
@ -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",
|
||||||
|
|
|
@ -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 |
|
@ -74,7 +74,6 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"cycle_highways"
|
"cycle_highways"
|
||||||
],
|
],
|
||||||
"overpassTimeout": 60,
|
"overpassTimeout": 60,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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} />
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue