Themes: automatically derive source from presets if no source is given

This commit is contained in:
Pieter Vander Vennet 2024-08-16 02:09:54 +02:00
parent 662b5d0735
commit 06897588be
6 changed files with 243 additions and 184 deletions

View file

@ -9,6 +9,7 @@ import DeleteConfig from "../DeleteConfig"
import { And } from "../../../Logic/Tags/And"
import { DoesImageExist, ValidateFilter, ValidatePointRendering } from "./Validation"
import { ValidateTagRenderings } from "./ValidateTagRenderings"
import { TagsFilterClosed } from "../../../Logic/Tags/TagTypes"
export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
private readonly _isBuiltin: boolean
@ -24,7 +25,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
path: string,
isBuiltin: boolean,
doesImageExist: DoesImageExist,
studioValidations: boolean
studioValidations: boolean,
) {
super("Runs various checks against common mistakes for a layer", [], "PrevalidateLayer")
this._path = path
@ -47,18 +48,21 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
}
if (json.source === undefined) {
context
.enter("source")
.err(
"No source section is defined; please define one as data is not loaded otherwise"
)
if (json.presets?.length < 1) {
context
.enter("source")
.err(
"No source section is defined; please define one as data is not loaded otherwise",
)
}
} else {
if (json.source === "special" || json.source === "special:library") {
} else if (json.source && json.source["osmTags"] === undefined) {
context
.enters("source", "osmTags")
.err(
"No osmTags defined in the source section - these should always be present, even for geojson layer"
"No osmTags defined in the source section - these should always be present, even for geojson layer",
)
} else {
const osmTags = TagUtils.Tag(json.source["osmTags"], context + "source.osmTags")
@ -67,7 +71,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
.enters("source", "osmTags")
.err(
"The source states tags which give a very wide selection: it only uses negative expressions, which will result in too much and unexpected data. Add at least one required tag. The tags are:\n\t" +
osmTags.asHumanString(false, false, {})
osmTags.asHumanString(false, false, {}),
)
}
}
@ -93,10 +97,10 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
.enter("syncSelection")
.err(
"Invalid sync-selection: must be one of " +
LayerConfig.syncSelectionAllowed.map((v) => `'${v}'`).join(", ") +
" but got '" +
json.syncSelection +
"'"
LayerConfig.syncSelectionAllowed.map((v) => `'${v}'`).join(", ") +
" but got '" +
json.syncSelection +
"'",
)
}
if (json["pointRenderings"]?.length > 0) {
@ -115,7 +119,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
}
json.pointRendering?.forEach((pr, i) =>
this._validatePointRendering.convert(pr, context.enters("pointeRendering", i))
this._validatePointRendering.convert(pr, context.enters("pointeRendering", i)),
)
if (json["mapRendering"]) {
@ -132,8 +136,8 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
if (!Constants.priviliged_layers.find((x) => x == json.id)) {
context.err(
"Layer " +
json.id +
" uses 'special' as source.osmTags. However, this layer is not a priviliged layer"
json.id +
" uses 'special' as source.osmTags. However, this layer is not a privileged layer",
)
}
}
@ -148,19 +152,19 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
context
.enter("title")
.err(
"This layer does not have a title defined but it does have tagRenderings. Not having a title will disable the popups, resulting in an unclickable element. Please add a title. If not having a popup is intended and the tagrenderings need to be kept (e.g. in a library layer), set `title: null` to disable this error."
"This layer does not have a title defined but it does have tagRenderings. Not having a title will disable the popups, resulting in an unclickable element. Please add a title. If not having a popup is intended and the tagrenderings need to be kept (e.g. in a library layer), set `title: null` to disable this error.",
)
}
if (json.title === null) {
context.info(
"Title is `null`. This results in an element that cannot be clicked - even though tagRenderings is set."
"Title is `null`. This results in an element that cannot be clicked - even though tagRenderings is set.",
)
}
{
// Check for multiple, identical builtin questions - usability for studio users
const duplicates = Utils.Duplicates(
<string[]>json.tagRenderings.filter((tr) => typeof tr === "string")
<string[]>json.tagRenderings.filter((tr) => typeof tr === "string"),
)
for (let i = 0; i < json.tagRenderings.length; i++) {
const tagRendering = json.tagRenderings[i]
@ -190,7 +194,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
{
// duplicate ids in tagrenderings check
const duplicates = Utils.NoNull(
Utils.Duplicates(Utils.NoNull((json.tagRenderings ?? []).map((tr) => tr["id"])))
Utils.Duplicates(Utils.NoNull((json.tagRenderings ?? []).map((tr) => tr["id"]))),
)
if (duplicates.length > 0) {
// It is tempting to add an index to this warning; however, due to labels the indices here might be different from the index in the tagRendering list
@ -198,11 +202,11 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
.enter("tagRenderings")
.err(
"Some tagrenderings have a duplicate id: " +
duplicates.join(", ") +
"\n" +
JSON.stringify(
json.tagRenderings.filter((tr) => duplicates.indexOf(tr["id"]) >= 0)
)
duplicates.join(", ") +
"\n" +
JSON.stringify(
json.tagRenderings.filter((tr) => duplicates.indexOf(tr["id"]) >= 0),
),
)
}
}
@ -235,8 +239,8 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
if (json["overpassTags"] !== undefined) {
context.err(
"Layer " +
json.id +
'still uses the old \'overpassTags\'-format. Please use "source": {"osmTags": <tags>}\' instead of "overpassTags": <tags> (note: this isn\'t your fault, the custom theme generator still spits out the old format)'
json.id +
"still uses the old 'overpassTags'-format. Please use \"source\": {\"osmTags\": <tags>}' instead of \"overpassTags\": <tags> (note: this isn't your fault, the custom theme generator still spits out the old format)",
)
}
const forbiddenTopLevel = [
@ -256,7 +260,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
}
if (json["hideUnderlayingFeaturesMinPercentage"] !== undefined) {
context.err(
"Layer " + json.id + " contains an old 'hideUnderlayingFeaturesMinPercentage'"
"Layer " + json.id + " contains an old 'hideUnderlayingFeaturesMinPercentage'",
)
}
@ -273,9 +277,9 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
if (this._path != undefined && this._path.indexOf(expected) < 0) {
context.err(
"Layer is in an incorrect place. The path is " +
this._path +
", but expected " +
expected
this._path +
", but expected " +
expected,
)
}
}
@ -293,13 +297,13 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
.enter(["tagRenderings", ...emptyIndexes])
.err(
`Some tagrendering-ids are empty or have an emtpy string; this is not allowed (at ${emptyIndexes.join(
","
)}])`
",",
)}])`,
)
}
const duplicateIds = Utils.Duplicates(
(json.tagRenderings ?? [])?.map((f) => f["id"]).filter((id) => id !== "questions")
(json.tagRenderings ?? [])?.map((f) => f["id"]).filter((id) => id !== "questions"),
)
if (duplicateIds.length > 0 && !Utils.runningFromConsole) {
context
@ -323,7 +327,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
if (json.tagRenderings !== undefined) {
new On(
"tagRenderings",
new Each(new ValidateTagRenderings(json, this._doesImageExist))
new Each(new ValidateTagRenderings(json, this._doesImageExist)),
).convert(json, context)
}
@ -350,7 +354,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
context
.enters("pointRendering", i, "marker", indexM, "icon", "condition")
.err(
"Don't set a condition in a marker as this will result in an invisible but clickable element. Use extra filters in the source instead."
"Don't set a condition in a marker as this will result in an invisible but clickable element. Use extra filters in the source instead.",
)
}
}
@ -361,8 +365,11 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
if (typeof json.source === "string") {
context.enter("presets").err("A special layer cannot have presets")
}
// Check that a preset will be picked up by the layer itself
const baseTags = TagUtils.Tag(json.source["osmTags"])
let baseTags: TagsFilterClosed
if (json.source) {
// Check that a preset will be picked up by the layer itself
baseTags = TagUtils.Tag(json.source["osmTags"])
}
for (let i = 0; i < json.presets.length; i++) {
const preset = json.presets[i]
if (!preset) {
@ -382,16 +389,18 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
for (const tag of tags.asChange({ id: "node/-1" })) {
properties[tag.k] = tag.v
}
const doMatch = baseTags.matchesProperties(properties)
if (!doMatch) {
context
.enters("presets", i, "tags")
.err(
"This preset does not match the required tags of this layer. This implies that a newly added point will not show up.\n A newly created point will have properties: " +
if(baseTags) {
const doMatch = baseTags.matchesProperties(properties)
if (!doMatch) {
context
.enters("presets", i, "tags")
.err(
"This preset does not match the required tags of this layer. This implies that a newly added point will not show up.\n A newly created point will have properties: " +
tags.asHumanString(false, false, {}) +
"\n The required tags are: " +
baseTags.asHumanString(false, false, {})
)
baseTags.asHumanString(false, false, {}),
)
}
}
}
}