forked from MapComplete/MapComplete
Fix: fix #2207
This commit is contained in:
parent
4794032e49
commit
ca17d3da1b
7 changed files with 136 additions and 91 deletions
|
@ -13,10 +13,13 @@ class DownloadEli extends Script {
|
||||||
const url = "https://osmlab.github.io/editor-layer-index/imagery.geojson"
|
const url = "https://osmlab.github.io/editor-layer-index/imagery.geojson"
|
||||||
// Target should use '.json' instead of '.geojson', as the latter cannot be imported by the build systems
|
// Target should use '.json' instead of '.geojson', as the latter cannot be imported by the build systems
|
||||||
const target = args[0] ?? "public/assets/data/editor-layer-index.json"
|
const target = args[0] ?? "public/assets/data/editor-layer-index.json"
|
||||||
|
const targetGlobal = args[1] ?? "src/assets/generated/editor-layer-index-global.json"
|
||||||
|
|
||||||
const targetBing = args[0] ?? "src/assets/bing.json"
|
const targetBing = args[0] ?? "src/assets/bing.json"
|
||||||
|
|
||||||
const eli: Eli = await Utils.downloadJson(url)
|
const eli: Eli = await Utils.downloadJson(url)
|
||||||
const keptLayers: EliEntry[] = []
|
const keptLayers: EliEntry[] = []
|
||||||
|
const keptGlobalLayers: EliEntry[] = []
|
||||||
console.log("Got", eli.features.length, "ELI-entries")
|
console.log("Got", eli.features.length, "ELI-entries")
|
||||||
for (let layer of eli.features) {
|
for (let layer of eli.features) {
|
||||||
const props = layer.properties
|
const props = layer.properties
|
||||||
|
@ -95,18 +98,26 @@ class DownloadEli extends Script {
|
||||||
}
|
}
|
||||||
|
|
||||||
layer = { properties: layer.properties, type: layer.type, geometry: layer.geometry }
|
layer = { properties: layer.properties, type: layer.type, geometry: layer.geometry }
|
||||||
|
if(layer.geometry === null){
|
||||||
|
keptGlobalLayers.push(layer)
|
||||||
|
}else{
|
||||||
keptLayers.push(layer)
|
keptLayers.push(layer)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const contents =
|
const contents =
|
||||||
'{"type":"FeatureCollection",\n "features": [\n' +
|
'{"type":"FeatureCollection",\n "features": [\n' +
|
||||||
keptLayers
|
keptLayers
|
||||||
.filter((l) => l.properties.id !== "Bing")
|
|
||||||
.map((l) => JSON.stringify(l))
|
.map((l) => JSON.stringify(l))
|
||||||
.join(",\n") +
|
.join(",\n") +
|
||||||
"\n]}"
|
"\n]}"
|
||||||
|
|
||||||
const bing = keptLayers.find((l) => l.properties.id === "Bing")
|
const contentsGlobal =
|
||||||
|
keptGlobalLayers
|
||||||
|
.filter((l) => l.properties.id !== "Bing")
|
||||||
|
.map(l => l.properties)
|
||||||
|
|
||||||
|
const bing = keptGlobalLayers.find((l) => l.properties.id === "Bing")
|
||||||
if (bing) {
|
if (bing) {
|
||||||
fs.writeFileSync(targetBing, JSON.stringify(bing), { encoding: "utf8" })
|
fs.writeFileSync(targetBing, JSON.stringify(bing), { encoding: "utf8" })
|
||||||
console.log("Written", targetBing)
|
console.log("Written", targetBing)
|
||||||
|
@ -115,6 +126,9 @@ class DownloadEli extends Script {
|
||||||
}
|
}
|
||||||
fs.writeFileSync(target, contents, { encoding: "utf8" })
|
fs.writeFileSync(target, contents, { encoding: "utf8" })
|
||||||
console.log("Written", keptLayers.length + ", entries to the ELI")
|
console.log("Written", keptLayers.length + ", entries to the ELI")
|
||||||
|
fs.writeFileSync(targetGlobal, JSON.stringify(contentsGlobal,null, " "), { encoding: "utf8" })
|
||||||
|
console.log("Written", keptGlobalLayers.length + ", entries to the global ELI")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { Feature, Polygon } from "geojson"
|
import { Feature, Polygon } from "geojson"
|
||||||
import * as globallayers from "../assets/global-raster-layers.json"
|
import * as globallayers from "../assets/global-raster-layers.json"
|
||||||
|
import * as globallayersEli from "../assets/generated/editor-layer-index-global.json"
|
||||||
|
|
||||||
import * as bingJson from "../assets/bing.json"
|
import * as bingJson from "../assets/bing.json"
|
||||||
|
|
||||||
import { BBox } from "../Logic/BBox"
|
import { BBox } from "../Logic/BBox"
|
||||||
|
@ -21,26 +23,37 @@ export class AvailableRasterLayers {
|
||||||
}
|
}
|
||||||
console.debug("Downloading ELI")
|
console.debug("Downloading ELI")
|
||||||
const eli = await Utils.downloadJson<{ features: EditorLayerIndex }>(
|
const eli = await Utils.downloadJson<{ features: EditorLayerIndex }>(
|
||||||
"./assets/data/editor-layer-index.json"
|
"./assets/data/editor-layer-index.json",
|
||||||
)
|
)
|
||||||
this._editorLayerIndex = eli.features?.filter((l) => l.properties.id !== "Bing") ?? []
|
this._editorLayerIndex = eli.features?.filter((l) => l.properties.id !== "Bing") ?? []
|
||||||
this._editorLayerIndexStore.set(this._editorLayerIndex)
|
this._editorLayerIndexStore.set(this._editorLayerIndex)
|
||||||
return this._editorLayerIndex
|
return this._editorLayerIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
public static globalLayers: ReadonlyArray<RasterLayerPolygon> = globallayers.layers
|
public static readonly globalLayers: ReadonlyArray<RasterLayerPolygon> = AvailableRasterLayers.initGlobalLayers()
|
||||||
|
|
||||||
|
private static initGlobalLayers(): RasterLayerPolygon[] {
|
||||||
|
const gl: RasterLayerProperties[] = (globallayers["default"] ?? globallayers ).layers
|
||||||
.filter(
|
.filter(
|
||||||
(properties) =>
|
(properties) =>
|
||||||
properties.id !== "osm.carto" && properties.id !== "Bing" /*Added separately*/
|
properties.id !== "osm.carto" && properties.id !== "Bing", /*Added separately*/
|
||||||
)
|
)
|
||||||
.map(
|
const glEli: RasterLayerProperties[] = globallayersEli["default"] ?? globallayersEli
|
||||||
|
const joined = gl.concat(glEli)
|
||||||
|
if (joined.some(j => !j.id)) {
|
||||||
|
console.log("Invalid layers:", JSON.stringify(joined .filter(l => !l.id)))
|
||||||
|
throw "Detected invalid global layer with invalid id"
|
||||||
|
}
|
||||||
|
return joined.map(
|
||||||
(properties) =>
|
(properties) =>
|
||||||
<RasterLayerPolygon>{
|
<RasterLayerPolygon>{
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties,
|
properties,
|
||||||
geometry: BBox.global.asGeometry(),
|
geometry: BBox.global.asGeometry(),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public static bing = <RasterLayerPolygon>bingJson
|
public static bing = <RasterLayerPolygon>bingJson
|
||||||
public static readonly osmCartoProperties: RasterLayerProperties = {
|
public static readonly osmCartoProperties: RasterLayerProperties = {
|
||||||
id: "osm",
|
id: "osm",
|
||||||
|
@ -72,18 +85,18 @@ export class AvailableRasterLayers {
|
||||||
|
|
||||||
public static layersAvailableAt(
|
public static layersAvailableAt(
|
||||||
location: Store<{ lon: number; lat: number }>,
|
location: Store<{ lon: number; lat: number }>,
|
||||||
enableBing?: Store<boolean>
|
enableBing?: Store<boolean>,
|
||||||
): { store: Store<RasterLayerPolygon[]> } {
|
): { store: Store<RasterLayerPolygon[]> } {
|
||||||
const store = { store: undefined }
|
const store = { store: undefined }
|
||||||
Utils.AddLazyProperty(store, "store", () =>
|
Utils.AddLazyProperty(store, "store", () =>
|
||||||
AvailableRasterLayers._layersAvailableAt(location, enableBing)
|
AvailableRasterLayers._layersAvailableAt(location, enableBing),
|
||||||
)
|
)
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _layersAvailableAt(
|
private static _layersAvailableAt(
|
||||||
location: Store<{ lon: number; lat: number }>,
|
location: Store<{ lon: number; lat: number }>,
|
||||||
enableBing?: Store<boolean>
|
enableBing?: Store<boolean>,
|
||||||
): Store<RasterLayerPolygon[]> {
|
): Store<RasterLayerPolygon[]> {
|
||||||
this.editorLayerIndex() // start the download
|
this.editorLayerIndex() // start the download
|
||||||
const availableLayersBboxes = Stores.ListStabilized(
|
const availableLayersBboxes = Stores.ListStabilized(
|
||||||
|
@ -96,8 +109,8 @@ export class AvailableRasterLayers {
|
||||||
const lonlat: [number, number] = [loc.lon, loc.lat]
|
const lonlat: [number, number] = [loc.lon, loc.lat]
|
||||||
return eli.filter((eliPolygon) => BBox.get(eliPolygon).contains(lonlat))
|
return eli.filter((eliPolygon) => BBox.get(eliPolygon).contains(lonlat))
|
||||||
},
|
},
|
||||||
[AvailableRasterLayers._editorLayerIndexStore]
|
[AvailableRasterLayers._editorLayerIndexStore],
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
return Stores.ListStabilized(
|
return Stores.ListStabilized(
|
||||||
availableLayersBboxes.map(
|
availableLayersBboxes.map(
|
||||||
|
@ -119,15 +132,15 @@ export class AvailableRasterLayers {
|
||||||
if (
|
if (
|
||||||
!matching.some(
|
!matching.some(
|
||||||
(l) =>
|
(l) =>
|
||||||
l.id === AvailableRasterLayers.defaultBackgroundLayer.properties.id
|
l.id === AvailableRasterLayers.defaultBackgroundLayer.properties.id,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
matching.push(AvailableRasterLayers.defaultBackgroundLayer)
|
matching.push(AvailableRasterLayers.defaultBackgroundLayer)
|
||||||
}
|
}
|
||||||
return matching
|
return matching
|
||||||
},
|
},
|
||||||
[enableBing]
|
[enableBing],
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +159,7 @@ export class RasterLayerUtils {
|
||||||
available: RasterLayerPolygon[],
|
available: RasterLayerPolygon[],
|
||||||
preferredCategory: string,
|
preferredCategory: string,
|
||||||
ignoreLayer?: RasterLayerPolygon,
|
ignoreLayer?: RasterLayerPolygon,
|
||||||
skipLayers: number = 0
|
skipLayers: number = 0,
|
||||||
): RasterLayerPolygon {
|
): RasterLayerPolygon {
|
||||||
const inCategory = available.filter((l) => l.properties.category === preferredCategory)
|
const inCategory = available.filter((l) => l.properties.category === preferredCategory)
|
||||||
const best: RasterLayerPolygon[] = inCategory.filter((l) => l.properties.best)
|
const best: RasterLayerPolygon[] = inCategory.filter((l) => l.properties.best)
|
||||||
|
@ -154,7 +167,7 @@ export class RasterLayerUtils {
|
||||||
let all = best.concat(others)
|
let all = best.concat(others)
|
||||||
console.log(
|
console.log(
|
||||||
"Selected layers are:",
|
"Selected layers are:",
|
||||||
all.map((l) => l.properties.id)
|
all.map((l) => l.properties.id),
|
||||||
)
|
)
|
||||||
if (others.length > skipLayers) {
|
if (others.length > skipLayers) {
|
||||||
all = all.slice(skipLayers)
|
all = all.slice(skipLayers)
|
||||||
|
|
|
@ -120,11 +120,11 @@ export class ConversionContext {
|
||||||
return new ConversionContext(this.messages, this.path, [...this.operation, key])
|
return new ConversionContext(this.messages, this.path, [...this.operation, key])
|
||||||
}
|
}
|
||||||
|
|
||||||
warn(message: string) {
|
warn(...message: (string | number)[]) {
|
||||||
this.messages.push({ context: this, level: "warning", message })
|
this.messages.push({ context: this, level: "warning", message: message.join(" ") })
|
||||||
}
|
}
|
||||||
|
|
||||||
err(...message: string[]) {
|
err(...message: (string | number)[]) {
|
||||||
this._hasErrors = true
|
this._hasErrors = true
|
||||||
this.messages.push({ context: this, level: "error", message: message.join(" ") })
|
this.messages.push({ context: this, level: "error", message: message.join(" ") })
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import { Translatable } from "../Json/Translatable"
|
||||||
import { ConversionContext } from "./ConversionContext"
|
import { ConversionContext } from "./ConversionContext"
|
||||||
import PointRenderingConfigJson from "../Json/PointRenderingConfigJson"
|
import PointRenderingConfigJson from "../Json/PointRenderingConfigJson"
|
||||||
import { PrevalidateLayer } from "./PrevalidateLayer"
|
import { PrevalidateLayer } from "./PrevalidateLayer"
|
||||||
|
import { AvailableRasterLayers } from "../../RasterLayers"
|
||||||
|
import { eliCategory } from "../../RasterLayerProperties"
|
||||||
|
|
||||||
export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
|
export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
|
||||||
private readonly _languages: string[]
|
private readonly _languages: string[]
|
||||||
|
@ -28,7 +30,7 @@ export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
|
||||||
super(
|
super(
|
||||||
"Checks that the given object is fully translated in the specified languages",
|
"Checks that the given object is fully translated in the specified languages",
|
||||||
[],
|
[],
|
||||||
"ValidateLanguageCompleteness"
|
"ValidateLanguageCompleteness",
|
||||||
)
|
)
|
||||||
this._languages = languages ?? ["en"]
|
this._languages = languages ?? ["en"]
|
||||||
}
|
}
|
||||||
|
@ -42,7 +44,7 @@ export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
|
||||||
.filter(
|
.filter(
|
||||||
(t) =>
|
(t) =>
|
||||||
t.tr.translations[neededLanguage] === undefined &&
|
t.tr.translations[neededLanguage] === undefined &&
|
||||||
t.tr.translations["*"] === undefined
|
t.tr.translations["*"] === undefined,
|
||||||
)
|
)
|
||||||
.forEach((missing) => {
|
.forEach((missing) => {
|
||||||
context
|
context
|
||||||
|
@ -53,7 +55,7 @@ export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
|
||||||
", but it lacks a translation for " +
|
", but it lacks a translation for " +
|
||||||
missing.context +
|
missing.context +
|
||||||
".\n\tThe known translation is " +
|
".\n\tThe known translation is " +
|
||||||
missing.tr.textFor("en")
|
missing.tr.textFor("en"),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -70,7 +72,7 @@ export class DoesImageExist extends DesugaringStep<string> {
|
||||||
constructor(
|
constructor(
|
||||||
knownImagePaths: Set<string>,
|
knownImagePaths: Set<string>,
|
||||||
checkExistsSync: (path: string) => boolean = undefined,
|
checkExistsSync: (path: string) => boolean = undefined,
|
||||||
ignore?: Set<string>
|
ignore?: Set<string>,
|
||||||
) {
|
) {
|
||||||
super("Checks if an image exists", [], "DoesImageExist")
|
super("Checks if an image exists", [], "DoesImageExist")
|
||||||
this._ignore = ignore
|
this._ignore = ignore
|
||||||
|
@ -103,22 +105,22 @@ export class DoesImageExist extends DesugaringStep<string> {
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Utils.isEmoji(image)){
|
if (Utils.isEmoji(image)) {
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._knownImagePaths.has(image)) {
|
if (!this._knownImagePaths.has(image)) {
|
||||||
if (this.doesPathExist === undefined) {
|
if (this.doesPathExist === undefined) {
|
||||||
context.err(
|
context.err(
|
||||||
`Image with path ${image} not found or not attributed; it is used in ${context}`
|
`Image with path ${image} not found or not attributed; it is used in ${context}`,
|
||||||
)
|
)
|
||||||
} else if (!this.doesPathExist(image)) {
|
} else if (!this.doesPathExist(image)) {
|
||||||
context.err(
|
context.err(
|
||||||
`Image with path ${image} does not exist.\n Check for typo's and missing directories in the path.`
|
`Image with path ${image} does not exist.\n Check for typo's and missing directories in the path.`,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
context.err(
|
context.err(
|
||||||
`Image with path ${image} is not attributed (but it exists); execute 'npm run query:licenses' to add the license information and/or run 'npm run generate:licenses' to compile all the license info`
|
`Image with path ${image} is not attributed (but it exists); execute 'npm run query:licenses' to add the license information and/or run 'npm run generate:licenses' to compile all the license info`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +133,7 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> {
|
||||||
super(
|
super(
|
||||||
"Checks that an 'overrideAll' does not override a single override",
|
"Checks that an 'overrideAll' does not override a single override",
|
||||||
[],
|
[],
|
||||||
"OverrideShadowingCheck"
|
"OverrideShadowingCheck",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +183,7 @@ class MiscThemeChecks extends DesugaringStep<LayoutConfigJson> {
|
||||||
context
|
context
|
||||||
.enter("layers")
|
.enter("layers")
|
||||||
.err(
|
.err(
|
||||||
"The 'layers'-field should be an array, but it is not. Did you pase a layer identifier and forget to add the '[' and ']'?"
|
"The 'layers'-field should be an array, but it is not. Did you pase a layer identifier and forget to add the '[' and ']'?",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (json.socialImage === "") {
|
if (json.socialImage === "") {
|
||||||
|
@ -215,9 +217,25 @@ class MiscThemeChecks extends DesugaringStep<LayoutConfigJson> {
|
||||||
context
|
context
|
||||||
.enter("overideAll")
|
.enter("overideAll")
|
||||||
.err(
|
.err(
|
||||||
"'overrideAll' is spelled with _two_ `r`s. You only wrote a single one of them."
|
"'overrideAll' is spelled with _two_ `r`s. You only wrote a single one of them.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json.defaultBackgroundId
|
||||||
|
&& ![AvailableRasterLayers.osmCartoProperties.id, ...eliCategory ]
|
||||||
|
.find(l => l === json.defaultBackgroundId) ) {
|
||||||
|
const background = json.defaultBackgroundId
|
||||||
|
const match = AvailableRasterLayers.globalLayers.find(l => l.properties.id === background)
|
||||||
|
if (!match) {
|
||||||
|
const suggestions = Utils.sortedByLevenshteinDistance(background,
|
||||||
|
AvailableRasterLayers.globalLayers, l => l.properties.id)
|
||||||
|
context.enter("defaultBackgroundId")
|
||||||
|
.warn("The default background layer with id", background, "does not exist or is not a global layer. Perhaps you meant one of:",
|
||||||
|
suggestions.slice(0, 5).map(l => l.properties.id).join(", "),
|
||||||
|
"If you want to use a certain category of background image, use", AvailableRasterLayers.globalLayers.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,7 +245,7 @@ export class PrevalidateTheme extends Fuse<LayoutConfigJson> {
|
||||||
super(
|
super(
|
||||||
"Various consistency checks on the raw JSON",
|
"Various consistency checks on the raw JSON",
|
||||||
new MiscThemeChecks(),
|
new MiscThemeChecks(),
|
||||||
new OverrideShadowingCheck()
|
new OverrideShadowingCheck(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,7 +255,7 @@ export class DetectConflictingAddExtraTags extends DesugaringStep<TagRenderingCo
|
||||||
super(
|
super(
|
||||||
"The `if`-part in a mapping might set some keys. Those keys are not allowed to be set in the `addExtraTags`, as this might result in conflicting values",
|
"The `if`-part in a mapping might set some keys. Those keys are not allowed to be set in the `addExtraTags`, as this might result in conflicting values",
|
||||||
[],
|
[],
|
||||||
"DetectConflictingAddExtraTags"
|
"DetectConflictingAddExtraTags",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +282,7 @@ export class DetectConflictingAddExtraTags extends DesugaringStep<TagRenderingCo
|
||||||
.enters("mappings", i)
|
.enters("mappings", i)
|
||||||
.err(
|
.err(
|
||||||
"AddExtraTags overrides a key that is set in the `if`-clause of this mapping. Selecting this answer might thus first set one value (needed to match as answer) and then override it with a different value, resulting in an unsaveable question. The offending `addExtraTags` is " +
|
"AddExtraTags overrides a key that is set in the `if`-clause of this mapping. Selecting this answer might thus first set one value (needed to match as answer) and then override it with a different value, resulting in an unsaveable question. The offending `addExtraTags` is " +
|
||||||
duplicateKeys.join(", ")
|
duplicateKeys.join(", "),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,13 +300,13 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
|
||||||
super(
|
super(
|
||||||
"A tagRendering might set a freeform key (e.g. `name` and have an option that _should_ erase this name, e.g. `noname=yes`). Under normal circumstances, every mapping/freeform should affect all touched keys",
|
"A tagRendering might set a freeform key (e.g. `name` and have an option that _should_ erase this name, e.g. `noname=yes`). Under normal circumstances, every mapping/freeform should affect all touched keys",
|
||||||
[],
|
[],
|
||||||
"DetectNonErasedKeysInMappings"
|
"DetectNonErasedKeysInMappings",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
convert(
|
convert(
|
||||||
json: QuestionableTagRenderingConfigJson,
|
json: QuestionableTagRenderingConfigJson,
|
||||||
context: ConversionContext
|
context: ConversionContext,
|
||||||
): QuestionableTagRenderingConfigJson {
|
): QuestionableTagRenderingConfigJson {
|
||||||
if (json.multiAnswer) {
|
if (json.multiAnswer) {
|
||||||
// No need to check this here, this has its own validation
|
// No need to check this here, this has its own validation
|
||||||
|
@ -343,7 +361,7 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
|
||||||
.warn(
|
.warn(
|
||||||
"The freeform block does not modify the key `" +
|
"The freeform block does not modify the key `" +
|
||||||
neededKey +
|
neededKey +
|
||||||
"` which is set in a mapping. Use `addExtraTags` to overwrite it"
|
"` which is set in a mapping. Use `addExtraTags` to overwrite it",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -362,7 +380,7 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
|
||||||
.warn(
|
.warn(
|
||||||
"This mapping does not modify the key `" +
|
"This mapping does not modify the key `" +
|
||||||
neededKey +
|
neededKey +
|
||||||
"` which is set in a mapping or by the freeform block. Use `addExtraTags` to overwrite it"
|
"` which is set in a mapping or by the freeform block. Use `addExtraTags` to overwrite it",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,7 +397,7 @@ export class DetectMappingsShadowedByCondition extends DesugaringStep<TagRenderi
|
||||||
super(
|
super(
|
||||||
"Checks that, if the tagrendering has a condition, that a mapping is not contradictory to it, i.e. that there are no dead mappings",
|
"Checks that, if the tagrendering has a condition, that a mapping is not contradictory to it, i.e. that there are no dead mappings",
|
||||||
[],
|
[],
|
||||||
"DetectMappingsShadowedByCondition"
|
"DetectMappingsShadowedByCondition",
|
||||||
)
|
)
|
||||||
this._forceError = forceError
|
this._forceError = forceError
|
||||||
}
|
}
|
||||||
|
@ -451,7 +469,7 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
|
||||||
* DetectShadowedMappings.extractCalculatedTagNames({calculatedTags: ["_abc=js()"]}) // => ["_abc"]
|
* DetectShadowedMappings.extractCalculatedTagNames({calculatedTags: ["_abc=js()"]}) // => ["_abc"]
|
||||||
*/
|
*/
|
||||||
private static extractCalculatedTagNames(
|
private static extractCalculatedTagNames(
|
||||||
layerConfig?: LayerConfigJson | { calculatedTags: string[] }
|
layerConfig?: LayerConfigJson | { calculatedTags: string[] },
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
layerConfig?.calculatedTags?.map((ct) => {
|
layerConfig?.calculatedTags?.map((ct) => {
|
||||||
|
@ -537,7 +555,7 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
|
||||||
json.mappings[i]["hideInAnswer"] !== true
|
json.mappings[i]["hideInAnswer"] !== true
|
||||||
) {
|
) {
|
||||||
context.warn(
|
context.warn(
|
||||||
`Mapping ${i} is shadowed by mapping ${j}. However, mapping ${j} has 'hideInAnswer' set, which will result in a different rendering in question-mode.`
|
`Mapping ${i} is shadowed by mapping ${j}. However, mapping ${j} has 'hideInAnswer' set, which will result in a different rendering in question-mode.`,
|
||||||
)
|
)
|
||||||
} else if (doesMatch) {
|
} else if (doesMatch) {
|
||||||
// The current mapping is shadowed!
|
// The current mapping is shadowed!
|
||||||
|
@ -545,7 +563,7 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
|
||||||
The mapping ${parsedConditions[i].asHumanString(
|
The mapping ${parsedConditions[i].asHumanString(
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
{}
|
{},
|
||||||
)} is fully matched by a previous mapping (namely ${j}), which matches:
|
)} is fully matched by a previous mapping (namely ${j}), which matches:
|
||||||
${parsedConditions[j].asHumanString(false, false, {})}.
|
${parsedConditions[j].asHumanString(false, false, {})}.
|
||||||
|
|
||||||
|
@ -571,7 +589,7 @@ export class ValidatePossibleLinks extends DesugaringStep<string | Record<string
|
||||||
super(
|
super(
|
||||||
"Given a possible set of translations, validates that <a href=... target='_blank'> does have `rel='noopener'` set",
|
"Given a possible set of translations, validates that <a href=... target='_blank'> does have `rel='noopener'` set",
|
||||||
[],
|
[],
|
||||||
"ValidatePossibleLinks"
|
"ValidatePossibleLinks",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,21 +619,21 @@ export class ValidatePossibleLinks extends DesugaringStep<string | Record<string
|
||||||
|
|
||||||
convert(
|
convert(
|
||||||
json: string | Record<string, string>,
|
json: string | Record<string, string>,
|
||||||
context: ConversionContext
|
context: ConversionContext,
|
||||||
): string | Record<string, string> {
|
): string | Record<string, string> {
|
||||||
if (typeof json === "string") {
|
if (typeof json === "string") {
|
||||||
if (this.isTabnabbingProne(json)) {
|
if (this.isTabnabbingProne(json)) {
|
||||||
context.err(
|
context.err(
|
||||||
"The string " +
|
"The string " +
|
||||||
json +
|
json +
|
||||||
" has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping"
|
" has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const k in json) {
|
for (const k in json) {
|
||||||
if (this.isTabnabbingProne(json[k])) {
|
if (this.isTabnabbingProne(json[k])) {
|
||||||
context.err(
|
context.err(
|
||||||
`The translation for ${k} '${json[k]}' has a link targeting \`_blank\`, but it doesn't have \`rel='noopener'\` set. This gives rise to reverse tabnapping`
|
`The translation for ${k} '${json[k]}' has a link targeting \`_blank\`, but it doesn't have \`rel='noopener'\` set. This gives rise to reverse tabnapping`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -633,7 +651,7 @@ export class CheckTranslation extends DesugaringStep<Translatable> {
|
||||||
super(
|
super(
|
||||||
"Checks that a translation is valid and internally consistent",
|
"Checks that a translation is valid and internally consistent",
|
||||||
["*"],
|
["*"],
|
||||||
"CheckTranslation"
|
"CheckTranslation",
|
||||||
)
|
)
|
||||||
this._allowUndefined = allowUndefined
|
this._allowUndefined = allowUndefined
|
||||||
}
|
}
|
||||||
|
@ -680,7 +698,7 @@ export class ValidateLayerConfig extends DesugaringStep<LayerConfigJson> {
|
||||||
isBuiltin: boolean,
|
isBuiltin: boolean,
|
||||||
doesImageExist: DoesImageExist,
|
doesImageExist: DoesImageExist,
|
||||||
studioValidations: boolean = false,
|
studioValidations: boolean = false,
|
||||||
skipDefaultLayers: boolean = false
|
skipDefaultLayers: boolean = false,
|
||||||
) {
|
) {
|
||||||
super("Thin wrapper around 'ValidateLayer", [], "ValidateLayerConfig")
|
super("Thin wrapper around 'ValidateLayer", [], "ValidateLayerConfig")
|
||||||
this.validator = new ValidateLayer(
|
this.validator = new ValidateLayer(
|
||||||
|
@ -688,7 +706,7 @@ export class ValidateLayerConfig extends DesugaringStep<LayerConfigJson> {
|
||||||
isBuiltin,
|
isBuiltin,
|
||||||
doesImageExist,
|
doesImageExist,
|
||||||
studioValidations,
|
studioValidations,
|
||||||
skipDefaultLayers
|
skipDefaultLayers,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +734,7 @@ export class ValidatePointRendering extends DesugaringStep<PointRenderingConfigJ
|
||||||
context
|
context
|
||||||
.enter("markers")
|
.enter("markers")
|
||||||
.err(
|
.err(
|
||||||
`Detected a field 'markerS' in pointRendering. It is written as a singular case`
|
`Detected a field 'markerS' in pointRendering. It is written as a singular case`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (json.marker && !Array.isArray(json.marker)) {
|
if (json.marker && !Array.isArray(json.marker)) {
|
||||||
|
@ -726,7 +744,7 @@ export class ValidatePointRendering extends DesugaringStep<PointRenderingConfigJ
|
||||||
context
|
context
|
||||||
.enter("location")
|
.enter("location")
|
||||||
.err(
|
.err(
|
||||||
"A pointRendering should have at least one 'location' to defined where it should be rendered. "
|
"A pointRendering should have at least one 'location' to defined where it should be rendered. ",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return json
|
return json
|
||||||
|
@ -745,26 +763,26 @@ export class ValidateLayer extends Conversion<
|
||||||
isBuiltin: boolean,
|
isBuiltin: boolean,
|
||||||
doesImageExist: DoesImageExist,
|
doesImageExist: DoesImageExist,
|
||||||
studioValidations: boolean = false,
|
studioValidations: boolean = false,
|
||||||
skipDefaultLayers: boolean = false
|
skipDefaultLayers: boolean = false,
|
||||||
) {
|
) {
|
||||||
super("Doesn't change anything, but emits warnings and errors", [], "ValidateLayer")
|
super("Doesn't change anything, but emits warnings and errors", [], "ValidateLayer")
|
||||||
this._prevalidation = new PrevalidateLayer(
|
this._prevalidation = new PrevalidateLayer(
|
||||||
path,
|
path,
|
||||||
isBuiltin,
|
isBuiltin,
|
||||||
doesImageExist,
|
doesImageExist,
|
||||||
studioValidations
|
studioValidations,
|
||||||
)
|
)
|
||||||
this._skipDefaultLayers = skipDefaultLayers
|
this._skipDefaultLayers = skipDefaultLayers
|
||||||
}
|
}
|
||||||
|
|
||||||
convert(
|
convert(
|
||||||
json: LayerConfigJson,
|
json: LayerConfigJson,
|
||||||
context: ConversionContext
|
context: ConversionContext,
|
||||||
): { parsed: LayerConfig; raw: LayerConfigJson } {
|
): { parsed: LayerConfig; raw: LayerConfigJson } {
|
||||||
context = context.inOperation(this.name)
|
context = context.inOperation(this.name)
|
||||||
if (typeof json === "string") {
|
if (typeof json === "string") {
|
||||||
context.err(
|
context.err(
|
||||||
`Not a valid layer: the layerConfig is a string. 'npm run generate:layeroverview' might be needed`
|
`Not a valid layer: the layerConfig is a string. 'npm run generate:layeroverview' might be needed`,
|
||||||
)
|
)
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -796,7 +814,7 @@ export class ValidateLayer extends Conversion<
|
||||||
context
|
context
|
||||||
.enters("calculatedTags", i)
|
.enters("calculatedTags", i)
|
||||||
.err(
|
.err(
|
||||||
`Invalid function definition: the custom javascript is invalid:${e}. The offending javascript code is:\n ${code}`
|
`Invalid function definition: the custom javascript is invalid:${e}. The offending javascript code is:\n ${code}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -844,7 +862,7 @@ export class ValidateLayer extends Conversion<
|
||||||
context
|
context
|
||||||
.enters("allowMove", "enableAccuracy")
|
.enters("allowMove", "enableAccuracy")
|
||||||
.err(
|
.err(
|
||||||
"`enableAccuracy` is written with two C in the first occurrence and only one in the last"
|
"`enableAccuracy` is written with two C in the first occurrence and only one in the last",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,8 +893,8 @@ export class ValidateFilter extends DesugaringStep<FilterConfigJson> {
|
||||||
.enters("fields", i)
|
.enters("fields", i)
|
||||||
.err(
|
.err(
|
||||||
`Invalid filter: ${type} is not a valid textfield type.\n\tTry one of ${Array.from(
|
`Invalid filter: ${type} is not a valid textfield type.\n\tTry one of ${Array.from(
|
||||||
Validators.availableTypes
|
Validators.availableTypes,
|
||||||
).join(",")}`
|
).join(",")}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -893,13 +911,13 @@ export class DetectDuplicateFilters extends DesugaringStep<{
|
||||||
super(
|
super(
|
||||||
"Tries to detect layers where a shared filter can be used (or where similar filters occur)",
|
"Tries to detect layers where a shared filter can be used (or where similar filters occur)",
|
||||||
[],
|
[],
|
||||||
"DetectDuplicateFilters"
|
"DetectDuplicateFilters",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
convert(
|
convert(
|
||||||
json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] },
|
json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] },
|
||||||
context: ConversionContext
|
context: ConversionContext,
|
||||||
): { layers: LayerConfigJson[]; themes: LayoutConfigJson[] } {
|
): { layers: LayerConfigJson[]; themes: LayoutConfigJson[] } {
|
||||||
const { layers, themes } = json
|
const { layers, themes } = json
|
||||||
const perOsmTag = new Map<
|
const perOsmTag = new Map<
|
||||||
|
@ -963,7 +981,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{
|
||||||
filter: FilterConfigJson
|
filter: FilterConfigJson
|
||||||
}[]
|
}[]
|
||||||
>,
|
>,
|
||||||
layout?: LayoutConfigJson | undefined
|
layout?: LayoutConfigJson | undefined,
|
||||||
): void {
|
): void {
|
||||||
if (layer.filter === undefined || layer.filter === null) {
|
if (layer.filter === undefined || layer.filter === null) {
|
||||||
return
|
return
|
||||||
|
@ -1003,7 +1021,7 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
|
||||||
super(
|
super(
|
||||||
"Detects mappings which have identical (english) names or identical mappings.",
|
"Detects mappings which have identical (english) names or identical mappings.",
|
||||||
["presets"],
|
["presets"],
|
||||||
"DetectDuplicatePresets"
|
"DetectDuplicatePresets",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1014,13 +1032,13 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
|
||||||
if (new Set(enNames).size != enNames.length) {
|
if (new Set(enNames).size != enNames.length) {
|
||||||
const dups = Utils.Duplicates(enNames)
|
const dups = Utils.Duplicates(enNames)
|
||||||
const layersWithDup = json.layers.filter((l) =>
|
const layersWithDup = json.layers.filter((l) =>
|
||||||
l.presets.some((p) => dups.indexOf(p.title.textFor("en")) >= 0)
|
l.presets.some((p) => dups.indexOf(p.title.textFor("en")) >= 0),
|
||||||
)
|
)
|
||||||
const layerIds = layersWithDup.map((l) => l.id)
|
const layerIds = layersWithDup.map((l) => l.id)
|
||||||
context.err(
|
context.err(
|
||||||
`This theme has multiple presets which are named:${dups}, namely layers ${layerIds.join(
|
`This theme has multiple presets which are named:${dups}, namely layers ${layerIds.join(
|
||||||
", "
|
", ",
|
||||||
)} this is confusing for contributors and is probably the result of reusing the same layer multiple times. Use \`{"override": {"=presets": []}}\` to remove some presets`
|
)} this is confusing for contributors and is probably the result of reusing the same layer multiple times. Use \`{"override": {"=presets": []}}\` to remove some presets`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1035,17 +1053,17 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
|
||||||
Utils.SameObject(presetATags, presetBTags) &&
|
Utils.SameObject(presetATags, presetBTags) &&
|
||||||
Utils.sameList(
|
Utils.sameList(
|
||||||
presetA.preciseInput.snapToLayers,
|
presetA.preciseInput.snapToLayers,
|
||||||
presetB.preciseInput.snapToLayers
|
presetB.preciseInput.snapToLayers,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
context.err(
|
context.err(
|
||||||
`This theme has multiple presets with the same tags: ${presetATags.asHumanString(
|
`This theme has multiple presets with the same tags: ${presetATags.asHumanString(
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
{}
|
{},
|
||||||
)}, namely the preset '${presets[i].title.textFor("en")}' and '${presets[
|
)}, namely the preset '${presets[i].title.textFor("en")}' and '${presets[
|
||||||
j
|
j
|
||||||
].title.textFor("en")}'`
|
].title.textFor("en")}'`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1070,13 +1088,13 @@ export class ValidateThemeEnsemble extends Conversion<
|
||||||
super(
|
super(
|
||||||
"Validates that all themes together are logical, i.e. no duplicate ids exists within (overriden) themes",
|
"Validates that all themes together are logical, i.e. no duplicate ids exists within (overriden) themes",
|
||||||
[],
|
[],
|
||||||
"ValidateThemeEnsemble"
|
"ValidateThemeEnsemble",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
convert(
|
convert(
|
||||||
json: LayoutConfig[],
|
json: LayoutConfig[],
|
||||||
context: ConversionContext
|
context: ConversionContext,
|
||||||
): Map<
|
): Map<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
|
@ -1131,7 +1149,7 @@ export class ValidateThemeEnsemble extends Conversion<
|
||||||
"' is found in multiple themes with different tag definitions:",
|
"' is found in multiple themes with different tag definitions:",
|
||||||
"\t In theme " + oldTheme + ":\t" + oldTags.asHumanString(false, false, {}),
|
"\t In theme " + oldTheme + ":\t" + oldTags.asHumanString(false, false, {}),
|
||||||
"\tIn theme " + theme.id + ":\t" + tags.asHumanString(false, false, {}),
|
"\tIn theme " + theme.id + ":\t" + tags.asHumanString(false, false, {}),
|
||||||
].join("\n")
|
].join("\n"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await state?.imageUploadManager.uploadImageAndApply(file, tags, targetKey)
|
await state?.imageUploadManager?.uploadImageAndApply(file, tags, targetKey)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
state.reportError(e, "Could not upload image")
|
state.reportError(e, "Could not upload image")
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"properties":{"name":"Bing Maps Aerial","id":"Bing","url":"https://ecn.t3.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=14634&pr=odbl&n=f","type":"bing","category":"photo","min_zoom":1,"max_zoom":22},"type":"Feature","geometry":null}
|
{"properties":{"name":"Bing Maps Aerial","id":"Bing","url":"https://ecn.t3.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=14738&pr=odbl&n=f","type":"bing","category":"photo","min_zoom":1,"max_zoom":22},"type":"Feature","geometry":null}
|
Loading…
Reference in a new issue