Merge master

This commit is contained in:
Pieter Vander Vennet 2024-02-26 16:11:41 +01:00
commit 89a0be8903
150 changed files with 4201 additions and 9581 deletions

View file

@ -92,7 +92,6 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
this._includeClosedNotesDays +
"&bbox={x_min},{y_min},{x_max},{y_max}",
geoJsonZoomLevel: 10,
maxCacheAge: 0,
},
/* We need to set 'pass_all_features'
There are probably many note_import-layers, and we don't want the first one to gobble up all notes and then discard them...

View file

@ -135,6 +135,10 @@ export class UpdateLegacyLayer extends DesugaringStep<
delete config["rotation"]
delete config["wayHandling"]
delete config["hideUnderlayingFeaturesMinPercentage"]
const src = config.source
delete src["isOsmCache"]
delete src["maxCacheAge"]
delete src["widenFactor"]
for (const mapRenderingElement of config["mapRendering"] ?? []) {
if (mapRenderingElement["iconOverlays"] !== undefined) {
@ -269,6 +273,7 @@ class UpdateLegacyTheme extends DesugaringStep<LayoutConfigJson> {
oldThemeConfig.layers = Utils.NoNull(oldThemeConfig.layers)
delete oldThemeConfig["language"]
delete oldThemeConfig["version"]
delete oldThemeConfig["clustering"]
if (oldThemeConfig.startLat === 0) {
delete oldThemeConfig.startLat

View file

@ -194,7 +194,6 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
if (v === undefined) {
const msg = `Default layer ${layerName} not found. ${state.sharedLayers.size} layers are available`
if (layerName === "favourite") {
// context.warn(msg)
continue
}
context.err(msg)

View file

@ -283,6 +283,15 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
}
}
for (let i = 0; i < theme.layers.length; i++) {
const layer = theme.layers[i]
if (!layer.id.match("[a-z][a-z0-9_]*")) {
context
.enters("layers", i, "id")
.err("Invalid ID:" + layer.id + "should match [a-z][a-z0-9_]*")
}
}
return json
}
}
@ -364,6 +373,34 @@ class MiscThemeChecks extends DesugaringStep<LayoutConfigJson> {
if (json.socialImage === "") {
context.warn("Social image for theme " + json.id + " is the emtpy string")
}
if (json["clustering"]) {
context.warn("Obsolete field `clustering` is still around")
}
{
for (let i = 0; i < json.layers.length; i++) {
const l = json.layers[i]
if (l["override"]?.["source"] === undefined) {
continue
}
if (l["override"]?.["source"]?.["geoJson"]) {
continue // We don't care about external data as we won't cache it anyway
}
if (l["override"]["id"] !== undefined) {
continue
}
context
.enters("layers", i)
.err("A layer which changes the source-tags must also change the ID")
}
}
if (json["overideAll"]) {
context
.enter("overideAll")
.err(
"'overrideAll' is spelled with _two_ `r`s. You only wrote a single one of them."
)
}
return json
}
}
@ -1035,7 +1072,8 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> {
new On("render", new ValidatePossibleLinks()),
new On("question", new ValidatePossibleLinks()),
new On("questionHint", new ValidatePossibleLinks()),
new On("mappings", new Each(new On("then", new ValidatePossibleLinks())))
new On("mappings", new Each(new On("then", new ValidatePossibleLinks()))),
new MiscTagRenderingChecks()
)
}
}
@ -1070,8 +1108,9 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
if (json.id?.toLowerCase() !== json.id) {
context.enter("id").err(`The id of a layer should be lowercase: ${json.id}`)
}
if (json.id?.match(/[a-z0-9-_]/) == null) {
context.enter("id").err(`The id of a layer should match [a-z0-9-_]*: ${json.id}`)
const layerRegex = /[a-zA-Z][a-zA-Z_0-9]+/
if (json.id.match(layerRegex) === null) {
context.enter("id").err("Invalid ID. A layer ID should match " + layerRegex.source)
}
}
@ -1572,6 +1611,22 @@ export class ValidateLayer extends Conversion<
}
}
if (json["doCount"]) {
context.enters("doCount").err("Use `isCounted` instead of `doCount`")
}
if (json.source) {
const src = json.source
if (src["isOsmCache"] !== undefined) {
context.enters("source").err("isOsmCache is deprecated")
}
if (src["maxCacheAge"] !== undefined) {
context
.enters("source")
.err("maxCacheAge is deprecated; it is " + src["maxCacheAge"])
}
}
return { raw: json, parsed: layerConfig }
}
}
@ -1774,3 +1829,79 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
return json
}
}
export class ValidateThemeEnsemble extends Conversion<
LayoutConfig[],
Map<
string,
{
tags: TagsFilter
foundInTheme: string[]
}
>
> {
constructor() {
super(
"Validates that all themes together are logical, i.e. no duplicate ids exists within (overriden) themes",
[],
"ValidateThemeEnsemble"
)
}
convert(
json: LayoutConfig[],
context: ConversionContext
): Map<
string,
{
tags: TagsFilter
foundInTheme: string[]
}
> {
const idToSource = new Map<string, { tags: TagsFilter; foundInTheme: string[] }>()
for (const theme of json) {
for (const layer of theme.layers) {
if (typeof layer.source === "string") {
continue
}
if (Constants.priviliged_layers.indexOf(<any>layer.id) >= 0) {
continue
}
if (!layer.source) {
console.log(theme, layer, layer.source)
context.enters(theme.id, "layers", "source", layer.id).err("No source defined")
continue
}
if (layer.source.geojsonSource) {
continue
}
const id = layer.id
const tags = layer.source.osmTags
if (!idToSource.has(id)) {
idToSource.set(id, { tags, foundInTheme: [theme.id] })
continue
}
const oldTags = idToSource.get(id).tags
const oldTheme = idToSource.get(id).foundInTheme
if (oldTags.shadows(tags) && tags.shadows(oldTags)) {
// All is good, all is well
oldTheme.push(theme.id)
continue
}
context.err(
[
"The layer with id '" +
id +
"' is found in multiple themes with different tag definitions:",
"\t In theme " + oldTheme + ":\t" + oldTags.asHumanString(false, false, {}),
"\tIn theme " + theme.id + ":\t" + tags.asHumanString(false, false, {}),
].join("\n")
)
}
}
return idToSource
}
}

View file

@ -74,15 +74,6 @@ export interface LayerConfigJson {
* Every source must set which tags have to be present in order to load the given layer.
*/
osmTags: TagConfigJson
/**
* question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?
* The maximum amount of seconds that a tile is allowed to linger in the cache
*
* type: nat
* default: 30 days
* group: expert
*/
maxCacheAge?: number
}
| {
/**
@ -109,17 +100,6 @@ export interface LayerConfigJson {
* ifunset: This is not a tiled geojson
*/
geoJsonZoomLevel?: number
/**
* Indicates that the upstream geojson data is OSM-derived.
* Useful for e.g. merging or for scripts generating this cache.
* This also indicates that making changes on this data is possible
*
* question: Is this geojson a cache of OpenStreetMap data?
* ifunset: This is not an OpenStreetMap cache
* iftrue: this is based on OpenStreetMap and can thus be edited
* group: expert
*/
isOsmCache?: boolean
/**
* Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this
*
@ -176,6 +156,18 @@ export interface LayerConfigJson {
*/
isShown?: TagConfigJson
/**
* question: should this layer be included in the summary counts?
*
* The layer server can give summary counts for a tile.
* This should however be disabled for some layers, e.g. because there are too many features (walls_and_buildings) or because the count is irrelevant.
*
* ifunset: Do count
* iffalse: Do not include the counts
* iftrue: Do include the count
*/
isCounted?: true | boolean
/**
* The minimum needed zoomlevel required to start loading and displaying the data.
* This can be used to only show common features (e.g. a bicycle parking) only when the map is zoomed in very much (17).

View file

@ -417,14 +417,6 @@ export interface LayoutConfigJson {
*/
overpassTimeout?: number
/**
* When a query is run, the data within bounds of the visible map is loaded.
* However, users tend to pan and zoom a lot. It is pretty annoying if every single pan means a reloading of the data.
* For this, the bounds are widened in order to make a small pan still within bounds of the loaded data.
*
* IF widenfactor is 1, this feature is disabled. A recommended value is between 1 and 3
*/
widenFactor?: number
/**
* At low zoom levels, overpass is used to query features.
* At high zoom level, the OSM api is used to fetch one or more BBOX aligning with a slippy tile.

View file

@ -28,8 +28,6 @@ import { ImmutableStore } from "../../Logic/UIEventSource"
import { OsmTags } from "../OsmFeature"
import Constants from "../Constants"
import { QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRenderingConfigJson"
import SvelteUIElement from "../../UI/Base/SvelteUIElement"
import Statistics from "../../assets/svg/Statistics.svelte"
export default class LayerConfig extends WithContextLoader {
public static readonly syncSelectionAllowed = ["no", "local", "theme-only", "global"] as const
@ -46,7 +44,6 @@ export default class LayerConfig extends WithContextLoader {
public readonly isShown: TagsFilter
public minzoom: number
public minzoomVisible: number
public readonly maxzoom: number
public readonly title?: TagRenderingConfig
public readonly titleIcons: TagRenderingConfig[]
public readonly mapRendering: PointRenderingConfig[]
@ -56,6 +53,7 @@ export default class LayerConfig extends WithContextLoader {
public readonly allowMove: MoveConfig | null
public readonly allowSplit: boolean
public readonly shownByDefault: boolean
public readonly doCount: boolean
/**
* In seconds
*/
@ -161,6 +159,7 @@ export default class LayerConfig extends WithContextLoader {
}
this.minzoomVisible = json.minzoomVisible ?? this.minzoom
this.shownByDefault = json.shownByDefault ?? true
this.doCount = json.isCounted ?? true
this.forceLoad = json.forceLoad ?? false
if (json.presets === null) json.presets = undefined
if (json.presets !== undefined && json.presets?.map === undefined) {
@ -465,9 +464,7 @@ export default class LayerConfig extends WithContextLoader {
return [
new Combine([
new Link(
Utils.runningFromConsole
? "<img src='https://mapcomplete.org/assets/svg/statistics.svg' height='18px'>"
: new SvelteUIElement(Statistics, { class: "w-4 h-4 mr-2" }),
"<img src='https://mapcomplete.org/assets/svg/statistics.svg' height='18px'>",
"https://taginfo.openstreetmap.org/keys/" + values.key + "#values",
true
),

View file

@ -247,6 +247,14 @@ export default class LayoutConfig implements LayoutInformation {
return this.layers.some((l) => l.isLeftRightSensitive())
}
public hasNoteLayer() {
return this.layers.some((l) => l.id === "note")
}
public hasPresets() {
return this.layers.some((l) => l.presets?.length > 0)
}
public missingTranslations(extraInspection: any): {
untranslated: Map<string, string[]>
total: number

View file

@ -314,7 +314,7 @@ export default class PointRenderingConfig extends WithContextLoader {
const label = self.label
?.GetRenderValue(tags)
?.Subs(tags)
?.SetClass("block center absolute text-center marker-label")
?.SetClass("flex items-center justify-center absolute marker-label")
?.SetClass(cssClassesLabel)
if (cssLabel) {
label.SetStyle(cssLabel)