chore: automated housekeeping...

This commit is contained in:
Pieter Vander Vennet 2024-08-14 13:53:56 +02:00
parent f77570589d
commit 9b8a9337fd
111 changed files with 2911 additions and 1280 deletions

View file

@ -8,26 +8,26 @@ import { GeoOperations } from "../Logic/GeoOperations"
import { RasterLayerProperties } from "./RasterLayerProperties"
import { Utils } from "../Utils"
export type EditorLayerIndex = (Feature<Polygon, EditorLayerIndexProperties> &
RasterLayerPolygon)[]
export type EditorLayerIndex = (Feature<Polygon, EditorLayerIndexProperties> & RasterLayerPolygon)[]
export class AvailableRasterLayers {
private static _editorLayerIndex: EditorLayerIndex = undefined
private static _editorLayerIndexStore: UIEventSource<EditorLayerIndex> = new UIEventSource<EditorLayerIndex>(undefined)
private static _editorLayerIndexStore: UIEventSource<EditorLayerIndex> =
new UIEventSource<EditorLayerIndex>(undefined)
public static async editorLayerIndex(): Promise<EditorLayerIndex> {
if(AvailableRasterLayers._editorLayerIndex !== undefined){
if (AvailableRasterLayers._editorLayerIndex !== undefined) {
return AvailableRasterLayers._editorLayerIndex
}
console.debug("Downloading ELI")
const eli = await Utils.downloadJson<{ features: EditorLayerIndex }>("./assets/data/editor-layer-index.json")
this._editorLayerIndex = eli.features.filter(l => l.properties.id !== "Bing")
const eli = await Utils.downloadJson<{ features: EditorLayerIndex }>(
"./assets/data/editor-layer-index.json"
)
this._editorLayerIndex = eli.features.filter((l) => l.properties.id !== "Bing")
this._editorLayerIndexStore.set(this._editorLayerIndex)
return this._editorLayerIndex
}
public static globalLayers: RasterLayerPolygon[] = globallayers.layers
.filter(
(properties) =>
@ -41,7 +41,7 @@ export class AvailableRasterLayers {
geometry: BBox.global.asGeometry(),
}
)
public static bing = <RasterLayerPolygon> bingJson
public static bing = <RasterLayerPolygon>bingJson
public static readonly osmCartoProperties: RasterLayerProperties = {
id: "osm",
name: "OpenStreetMap",
@ -70,10 +70,14 @@ export class AvailableRasterLayers {
return l.properties.id === "protomaps.sunny"
})
public static layersAvailableAt( location: Store<{ lon: number; lat: number }>,
enableBing?: Store<boolean>): {store: Store<RasterLayerPolygon[]> } {
const store = {store: undefined}
Utils.AddLazyProperty(store, "store", () => AvailableRasterLayers._layersAvailableAt(location, enableBing))
public static layersAvailableAt(
location: Store<{ lon: number; lat: number }>,
enableBing?: Store<boolean>
): { store: Store<RasterLayerPolygon[]> } {
const store = { store: undefined }
Utils.AddLazyProperty(store, "store", () =>
AvailableRasterLayers._layersAvailableAt(location, enableBing)
)
return store
}
@ -81,19 +85,19 @@ export class AvailableRasterLayers {
location: Store<{ lon: number; lat: number }>,
enableBing?: Store<boolean>
): Store<RasterLayerPolygon[]> {
this.editorLayerIndex() // start the download
const availableLayersBboxes = Stores.ListStabilized(
location.mapD((loc) => {
const eli = AvailableRasterLayers._editorLayerIndexStore.data
if(!eli){
return []
}
const lonlat: [number, number] = [loc.lon, loc.lat]
return eli.filter((eliPolygon) =>
BBox.get(eliPolygon).contains(lonlat)
)
}, [AvailableRasterLayers._editorLayerIndexStore])
location.mapD(
(loc) => {
const eli = AvailableRasterLayers._editorLayerIndexStore.data
if (!eli) {
return []
}
const lonlat: [number, number] = [loc.lon, loc.lat]
return eli.filter((eliPolygon) => BBox.get(eliPolygon).contains(lonlat))
},
[AvailableRasterLayers._editorLayerIndexStore]
)
)
return Stores.ListStabilized(
availableLayersBboxes.map(
@ -126,7 +130,6 @@ export class AvailableRasterLayers {
)
)
}
}
export class RasterLayerUtils {

View file

@ -84,7 +84,7 @@ export class Pipe<TIn, TInter, TOut> extends Conversion<TIn, TOut> {
convert(json: TIn, context: ConversionContext): TOut {
const r0 = this._step0.convert(json, context.inOperation(this._step0.name))
if(context.hasErrors() && this._failfast){
if (context.hasErrors() && this._failfast) {
return undefined
}
return this._step1.convert(r0, context.inOperation(this._step1.name))

View file

@ -12,7 +12,7 @@ export class DetectMappingsWithImages extends DesugaringStep<TagRenderingConfigJ
super(
"Checks that 'then'clauses in mappings don't have images, but use 'icon' instead",
[],
"DetectMappingsWithImages",
"DetectMappingsWithImages"
)
this._doesImageExist = doesImageExist
}
@ -52,14 +52,14 @@ export class DetectMappingsWithImages extends DesugaringStep<TagRenderingConfigJ
if (!ignore) {
ctx.err(
`A mapping has an image in the 'then'-clause. Remove the image there and use \`"icon": <your-image>\` instead. The images found are ${images.join(
", ",
)}. (This check can be turned of by adding "#": "${ignoreToken}" in the mapping, but this is discouraged`,
", "
)}. (This check can be turned of by adding "#": "${ignoreToken}" in the mapping, but this is discouraged`
)
} else {
ctx.info(
`Ignored image ${images.join(
", ",
)} in 'then'-clause of a mapping as this check has been disabled`,
", "
)} in 'then'-clause of a mapping as this check has been disabled`
)
for (const image of images) {

View file

@ -1,7 +1,10 @@
import { DesugaringStep } from "./Conversion"
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
import { LayerConfigJson } from "../Json/LayerConfigJson"
import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
import {
MappingConfigJson,
QuestionableTagRenderingConfigJson,
} from "../Json/QuestionableTagRenderingConfigJson"
import { ConversionContext } from "./ConversionContext"
import { Translation } from "../../../UI/i18n/Translation"
import NameSuggestionIndex from "../../../Logic/Web/NameSuggestionIndex"
@ -20,17 +23,17 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
convert(
json: TagRenderingConfigJson | QuestionableTagRenderingConfigJson,
context: ConversionContext,
context: ConversionContext
): TagRenderingConfigJson {
if (json["special"] !== undefined) {
context.err(
"Detected `special` on the top level. Did you mean `{\"render\":{ \"special\": ... }}`",
'Detected `special` on the top level. Did you mean `{"render":{ "special": ... }}`'
)
}
if (Object.keys(json).length === 1 && typeof json["render"] === "string") {
context.warn(
`use the content directly instead of {render: ${JSON.stringify(json["render"])}}`,
`use the content directly instead of {render: ${JSON.stringify(json["render"])}}`
)
}
@ -42,7 +45,7 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
const mapping: MappingConfigJson = json.mappings[i]
CheckTranslation.noUndefined.convert(
mapping.then,
context.enters("mappings", i, "then"),
context.enters("mappings", i, "then")
)
if (!mapping.if) {
console.log(
@ -51,7 +54,7 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
"if",
mapping.if,
context.path.join("."),
mapping.then,
mapping.then
)
context.enters("mappings", i, "if").err("No `if` is defined")
}
@ -61,7 +64,7 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
context
.enters("mappings", i, "addExtraTags", j)
.err(
"Detected a 'null' or 'undefined' value. Either specify a tag or delete this item",
"Detected a 'null' or 'undefined' value. Either specify a tag or delete this item"
)
}
}
@ -72,18 +75,18 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
context
.enters("mappings", i, "then")
.warn(
"A mapping should not start with 'yes' or 'no'. If the attribute is known, it will only show 'yes' or 'no' <i>without</i> the question, resulting in a weird phrasing in the information box",
"A mapping should not start with 'yes' or 'no'. If the attribute is known, it will only show 'yes' or 'no' <i>without</i> the question, resulting in a weird phrasing in the information box"
)
}
}
}
if (json["group"]) {
context.err("Groups are deprecated, use `\"label\": [\"" + json["group"] + "\"]` instead")
context.err('Groups are deprecated, use `"label": ["' + json["group"] + '"]` instead')
}
if (json["question"] && json.freeform?.key === undefined && json.mappings === undefined) {
context.err(
"A question is defined, but no mappings nor freeform (key) are. Add at least one of them",
"A question is defined, but no mappings nor freeform (key) are. Add at least one of them"
)
}
if (json["question"] && !json.freeform && (json.mappings?.length ?? 0) == 1) {
@ -93,7 +96,7 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
context
.enter("questionHint")
.err(
"A questionHint is defined, but no question is given. As such, the questionHint will never be shown",
"A questionHint is defined, but no question is given. As such, the questionHint will never be shown"
)
}
@ -101,7 +104,7 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
context
.enters("icon", "size")
.err(
"size is not a valid attribute. Did you mean 'class'? Class can be one of `small`, `medium` or `large`",
"size is not a valid attribute. Did you mean 'class'? Class can be one of `small`, `medium` or `large`"
)
}
@ -111,10 +114,10 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
.enter("render")
.err(
"This tagRendering allows to set a value to key " +
json.freeform.key +
", but does not define a `render`. Please, add a value here which contains `{" +
json.freeform.key +
"}`",
json.freeform.key +
", but does not define a `render`. Please, add a value here which contains `{" +
json.freeform.key +
"}`"
)
} else {
const render = new Translation(<any>json.render)
@ -145,7 +148,7 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
const keyFirstArg = ["canonical", "fediverse_link", "translated"]
if (
keyFirstArg.some(
(funcName) => txt.indexOf(`{${funcName}(${json.freeform.key}`) >= 0,
(funcName) => txt.indexOf(`{${funcName}(${json.freeform.key}`) >= 0
)
) {
continue
@ -169,7 +172,7 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
context
.enter("render")
.err(
`The rendering for language ${ln} does not contain \`{${json.freeform.key}}\`. Did you perhaps forget to set "freeform.type: 'wikidata'"?`,
`The rendering for language ${ln} does not contain \`{${json.freeform.key}}\`. Did you perhaps forget to set "freeform.type: 'wikidata'"?`
)
continue
}
@ -181,7 +184,7 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
context
.enter("render")
.err(
`The rendering for language ${ln} does not contain \`{${json.freeform.key}}\`. However, it does contain ${json.freeform.key} without braces. Did you forget the braces?\n\tThe current text is ${txt}`,
`The rendering for language ${ln} does not contain \`{${json.freeform.key}}\`. However, it does contain ${json.freeform.key} without braces. Did you forget the braces?\n\tThe current text is ${txt}`
)
continue
}
@ -189,7 +192,7 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
context
.enter("render")
.err(
`The rendering for language ${ln} does not contain \`{${json.freeform.key}}\`. This is a bug, as this rendering should show exactly this freeform key!\n\tThe current text is ${txt}`,
`The rendering for language ${ln} does not contain \`{${json.freeform.key}}\`. This is a bug, as this rendering should show exactly this freeform key!\n\tThe current text is ${txt}`
)
}
}
@ -204,22 +207,22 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
.enters("freeform", "type")
.err(
"No entry found in the 'Name Suggestion Index'. None of the 'osmSource'-tags match an entry in the NSI.\n\tOsmSource-tags are " +
tags.map((t) => new Tag(t.key, t.value).asHumanString()).join(" ; "),
tags.map((t) => new Tag(t.key, t.value).asHumanString()).join(" ; ")
)
}
} else if (json.freeform.type === "nsi") {
context
.enters("freeform", "type")
.warn(
"No need to explicitly set type to 'NSI', autodetected based on freeform type",
"No need to explicitly set type to 'NSI', autodetected based on freeform type"
)
}
}
if (json.render && json["question"] && json.freeform === undefined) {
context.err(
`Detected a tagrendering which takes input without freeform key in ${context}; the question is ${new Translation(
json["question"],
).textFor("en")}`,
json["question"]
).textFor("en")}`
)
}
@ -230,9 +233,9 @@ export class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJso
.enters("freeform", "type")
.err(
"Unknown type: " +
freeformType +
"; try one of " +
Validators.availableTypes.join(", "),
freeformType +
"; try one of " +
Validators.availableTypes.join(", ")
)
}
}

View file

@ -24,7 +24,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
@ -50,7 +50,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
context
.enter("source")
.err(
"No source section is defined; please define one as data is not loaded otherwise",
"No source section is defined; please define one as data is not loaded otherwise"
)
} else {
if (json.source === "special" || json.source === "special:library") {
@ -58,7 +58,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
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 +67,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 +93,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 +115,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 +132,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 priviliged layer"
)
}
}
@ -148,19 +148,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 +190,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 +198,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 +235,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 +256,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 +273,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 +293,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 +323,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 +350,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."
)
}
}
@ -388,9 +388,9 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
.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, {}),
tags.asHumanString(false, false, {}) +
"\n The required tags are: " +
baseTags.asHumanString(false, false, {})
)
}
}

View file

@ -26,7 +26,7 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> {
new On("question", new ValidatePossibleLinks()),
new On("questionHint", new ValidatePossibleLinks()),
new On("mappings", new Each(new On("then", new ValidatePossibleLinks()))),
new MiscTagRenderingChecks(layerConfig),
new MiscTagRenderingChecks(layerConfig)
)
}
}

View file

@ -22,7 +22,7 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
doesImageExist: DoesImageExist,
path: string,
isBuiltin: boolean,
sharedTagRenderings?: Set<string>,
sharedTagRenderings?: Set<string>
) {
super("Doesn't change anything, but emits warnings and errors", [], "ValidateTheme")
this._validateImage = doesImageExist
@ -41,15 +41,15 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
if (json["units"] !== undefined) {
context.err(
"The theme " +
json.id +
" has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) ",
json.id +
" has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) "
)
}
if (json["roamingRenderings"] !== undefined) {
context.err(
"Theme " +
json.id +
" contains an old 'roamingRenderings'. Use an 'overrideAll' instead",
json.id +
" contains an old 'roamingRenderings'. Use an 'overrideAll' instead"
)
}
}
@ -67,10 +67,10 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
for (const remoteImage of remoteImages) {
context.err(
"Found a remote image: " +
remoteImage.path +
" in theme " +
json.id +
", please download it.",
remoteImage.path +
" in theme " +
json.id +
", please download it."
)
}
for (const image of images) {
@ -86,17 +86,17 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
const filename = this._path.substring(
this._path.lastIndexOf("/") + 1,
this._path.length - 5,
this._path.length - 5
)
if (theme.id !== filename) {
context.err(
"Theme ids should be the same as the name.json, but we got id: " +
theme.id +
" and filename " +
filename +
" (" +
this._path +
")",
theme.id +
" and filename " +
filename +
" (" +
this._path +
")"
)
}
this._validateImage.convert(theme.icon, context.enter("icon"))
@ -104,13 +104,13 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
const dups = Utils.Duplicates(json.layers.map((layer) => layer["id"]))
if (dups.length > 0) {
context.err(
`The theme ${json.id} defines multiple layers with id ${dups.join(", ")}`,
`The theme ${json.id} defines multiple layers with id ${dups.join(", ")}`
)
}
if (json["mustHaveLanguage"] !== undefined) {
new ValidateLanguageCompleteness(...json["mustHaveLanguage"]).convert(
theme,
context,
context
)
}
if (!json.hideFromOverview && theme.id !== "personal" && this._isBuiltin) {
@ -118,7 +118,7 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
const targetLanguage = theme.title.SupportedLanguages()[0]
if (targetLanguage !== "en") {
context.err(
`TargetLanguage is not 'en' for public theme ${theme.id}, it is ${targetLanguage}. Move 'en' up in the title of the theme and set it as the first key`,
`TargetLanguage is not 'en' for public theme ${theme.id}, it is ${targetLanguage}. Move 'en' up in the title of the theme and set it as the first key`
)
}

View file

@ -9,7 +9,7 @@ export class ValidateThemeAndLayers extends Fuse<LayoutConfigJson> {
doesImageExist: DoesImageExist,
path: string,
isBuiltin: boolean,
sharedTagRenderings?: Set<string>,
sharedTagRenderings?: Set<string>
) {
super(
"Validates a theme and the contained layers",
@ -19,10 +19,10 @@ export class ValidateThemeAndLayers extends Fuse<LayoutConfigJson> {
new Each(
new Bypass(
(layer) => Constants.added_by_default.indexOf(<any>layer.id) < 0,
new ValidateLayerConfig(undefined, isBuiltin, doesImageExist, false, true),
),
),
),
new ValidateLayerConfig(undefined, isBuiltin, doesImageExist, false, true)
)
)
)
)
}
}

View file

@ -28,7 +28,7 @@ export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
super(
"Checks that the given object is fully translated in the specified languages",
[],
"ValidateLanguageCompleteness",
"ValidateLanguageCompleteness"
)
this._languages = languages ?? ["en"]
}
@ -42,18 +42,18 @@ export class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> {
.filter(
(t) =>
t.tr.translations[neededLanguage] === undefined &&
t.tr.translations["*"] === undefined,
t.tr.translations["*"] === undefined
)
.forEach((missing) => {
context
.enter(missing.context.split("."))
.err(
`The theme ${obj.id} should be translation-complete for ` +
neededLanguage +
", but it lacks a translation for " +
missing.context +
".\n\tThe known translation is " +
missing.tr.textFor("en"),
neededLanguage +
", but it lacks a translation for " +
missing.context +
".\n\tThe known translation is " +
missing.tr.textFor("en")
)
})
}
@ -70,7 +70,7 @@ export class DoesImageExist extends DesugaringStep<string> {
constructor(
knownImagePaths: Set<string>,
checkExistsSync: (path: string) => boolean = undefined,
ignore?: Set<string>,
ignore?: Set<string>
) {
super("Checks if an image exists", [], "DoesImageExist")
this._ignore = ignore
@ -106,15 +106,15 @@ export class DoesImageExist extends DesugaringStep<string> {
if (!this._knownImagePaths.has(image)) {
if (this.doesPathExist === undefined) {
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)) {
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 {
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`
)
}
}
@ -127,7 +127,7 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> {
super(
"Checks that an 'overrideAll' does not override a single override",
[],
"OverrideShadowingCheck",
"OverrideShadowingCheck"
)
}
@ -174,7 +174,11 @@ class MiscThemeChecks extends DesugaringStep<LayoutConfigJson> {
context.err("The theme " + json.id + " has no 'layers' defined")
}
if (!Array.isArray(json.layers)) {
context.enter("layers").err("The 'layers'-field should be an array, but it is not. Did you pase a layer identifier and forget to add the '[' and ']'?")
context
.enter("layers")
.err(
"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 === "") {
context.warn("Social image for theme " + json.id + " is the emtpy string")
@ -183,9 +187,9 @@ class MiscThemeChecks extends DesugaringStep<LayoutConfigJson> {
context.warn("Obsolete field `clustering` is still around")
}
if(json.layers === undefined){
if (json.layers === undefined) {
context.err("This theme has no layers defined")
}else{
} else {
for (let i = 0; i < json.layers.length; i++) {
const l = json.layers[i]
if (l["override"]?.["source"] === undefined) {
@ -207,7 +211,7 @@ class MiscThemeChecks extends DesugaringStep<LayoutConfigJson> {
context
.enter("overideAll")
.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."
)
}
return json
@ -219,7 +223,7 @@ export class PrevalidateTheme extends Fuse<LayoutConfigJson> {
super(
"Various consistency checks on the raw JSON",
new MiscThemeChecks(),
new OverrideShadowingCheck(),
new OverrideShadowingCheck()
)
}
}
@ -229,7 +233,7 @@ export class DetectConflictingAddExtraTags extends DesugaringStep<TagRenderingCo
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",
[],
"DetectConflictingAddExtraTags",
"DetectConflictingAddExtraTags"
)
}
@ -256,7 +260,7 @@ export class DetectConflictingAddExtraTags extends DesugaringStep<TagRenderingCo
.enters("mappings", i)
.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 " +
duplicateKeys.join(", "),
duplicateKeys.join(", ")
)
}
}
@ -274,13 +278,13 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
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",
[],
"DetectNonErasedKeysInMappings",
"DetectNonErasedKeysInMappings"
)
}
convert(
json: QuestionableTagRenderingConfigJson,
context: ConversionContext,
context: ConversionContext
): QuestionableTagRenderingConfigJson {
if (json.multiAnswer) {
// No need to check this here, this has its own validation
@ -334,8 +338,8 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
.enters("freeform")
.warn(
"The freeform block does not modify the key `" +
neededKey +
"` which is set in a mapping. Use `addExtraTags` to overwrite it",
neededKey +
"` which is set in a mapping. Use `addExtraTags` to overwrite it"
)
}
}
@ -353,8 +357,8 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
.enters("mappings", i)
.warn(
"This mapping does not modify the key `" +
neededKey +
"` which is set in a mapping or by the freeform block. Use `addExtraTags` to overwrite it",
neededKey +
"` which is set in a mapping or by the freeform block. Use `addExtraTags` to overwrite it"
)
}
}
@ -371,7 +375,7 @@ export class DetectMappingsShadowedByCondition extends DesugaringStep<TagRenderi
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",
[],
"DetectMappingsShadowedByCondition",
"DetectMappingsShadowedByCondition"
)
this._forceError = forceError
}
@ -443,7 +447,7 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
* DetectShadowedMappings.extractCalculatedTagNames({calculatedTags: ["_abc=js()"]}) // => ["_abc"]
*/
private static extractCalculatedTagNames(
layerConfig?: LayerConfigJson | { calculatedTags: string[] },
layerConfig?: LayerConfigJson | { calculatedTags: string[] }
) {
return (
layerConfig?.calculatedTags?.map((ct) => {
@ -529,16 +533,16 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
json.mappings[i]["hideInAnswer"] !== true
) {
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) {
// The current mapping is shadowed!
context.err(`Mapping ${i} is shadowed by mapping ${j} and will thus never be shown:
The mapping ${parsedConditions[i].asHumanString(
false,
false,
{},
)} is fully matched by a previous mapping (namely ${j}), which matches:
false,
false,
{}
)} is fully matched by a previous mapping (namely ${j}), which matches:
${parsedConditions[j].asHumanString(false, false, {})}.
To fix this problem, you can try to:
@ -563,7 +567,7 @@ export class ValidatePossibleLinks extends DesugaringStep<string | Record<string
super(
"Given a possible set of translations, validates that <a href=... target='_blank'> does have `rel='noopener'` set",
[],
"ValidatePossibleLinks",
"ValidatePossibleLinks"
)
}
@ -593,21 +597,21 @@ export class ValidatePossibleLinks extends DesugaringStep<string | Record<string
convert(
json: string | Record<string, string>,
context: ConversionContext,
context: ConversionContext
): string | Record<string, string> {
if (typeof json === "string") {
if (this.isTabnabbingProne(json)) {
context.err(
"The string " +
json +
" has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping",
json +
" has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping"
)
}
} else {
for (const k in json) {
if (this.isTabnabbingProne(json[k])) {
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`
)
}
}
@ -625,7 +629,7 @@ export class CheckTranslation extends DesugaringStep<Translatable> {
super(
"Checks that a translation is valid and internally consistent",
["*"],
"CheckTranslation",
"CheckTranslation"
)
this._allowUndefined = allowUndefined
}
@ -672,7 +676,7 @@ export class ValidateLayerConfig extends DesugaringStep<LayerConfigJson> {
isBuiltin: boolean,
doesImageExist: DoesImageExist,
studioValidations: boolean = false,
skipDefaultLayers: boolean = false,
skipDefaultLayers: boolean = false
) {
super("Thin wrapper around 'ValidateLayer", [], "ValidateLayerConfig")
this.validator = new ValidateLayer(
@ -680,7 +684,7 @@ export class ValidateLayerConfig extends DesugaringStep<LayerConfigJson> {
isBuiltin,
doesImageExist,
studioValidations,
skipDefaultLayers,
skipDefaultLayers
)
}
@ -708,7 +712,7 @@ export class ValidatePointRendering extends DesugaringStep<PointRenderingConfigJ
context
.enter("markers")
.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)) {
@ -718,7 +722,7 @@ export class ValidatePointRendering extends DesugaringStep<PointRenderingConfigJ
context
.enter("location")
.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
@ -737,26 +741,26 @@ export class ValidateLayer extends Conversion<
isBuiltin: boolean,
doesImageExist: DoesImageExist,
studioValidations: boolean = false,
skipDefaultLayers: boolean = false,
skipDefaultLayers: boolean = false
) {
super("Doesn't change anything, but emits warnings and errors", [], "ValidateLayer")
this._prevalidation = new PrevalidateLayer(
path,
isBuiltin,
doesImageExist,
studioValidations,
studioValidations
)
this._skipDefaultLayers = skipDefaultLayers
}
convert(
json: LayerConfigJson,
context: ConversionContext,
context: ConversionContext
): { parsed: LayerConfig; raw: LayerConfigJson } {
context = context.inOperation(this.name)
if (typeof json === "string") {
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
}
@ -787,7 +791,7 @@ export class ValidateLayer extends Conversion<
context
.enters("calculatedTags", i)
.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}`
)
}
}
@ -835,7 +839,7 @@ export class ValidateLayer extends Conversion<
context
.enters("allowMove", "enableAccuracy")
.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"
)
}
@ -866,8 +870,8 @@ export class ValidateFilter extends DesugaringStep<FilterConfigJson> {
.enters("fields", i)
.err(
`Invalid filter: ${type} is not a valid textfield type.\n\tTry one of ${Array.from(
Validators.availableTypes,
).join(",")}`,
Validators.availableTypes
).join(",")}`
)
}
}
@ -884,13 +888,13 @@ export class DetectDuplicateFilters extends DesugaringStep<{
super(
"Tries to detect layers where a shared filter can be used (or where similar filters occur)",
[],
"DetectDuplicateFilters",
"DetectDuplicateFilters"
)
}
convert(
json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] },
context: ConversionContext,
context: ConversionContext
): { layers: LayerConfigJson[]; themes: LayoutConfigJson[] } {
const { layers, themes } = json
const perOsmTag = new Map<
@ -954,7 +958,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{
filter: FilterConfigJson
}[]
>,
layout?: LayoutConfigJson | undefined,
layout?: LayoutConfigJson | undefined
): void {
if (layer.filter === undefined || layer.filter === null) {
return
@ -994,7 +998,7 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
super(
"Detects mappings which have identical (english) names or identical mappings.",
["presets"],
"DetectDuplicatePresets",
"DetectDuplicatePresets"
)
}
@ -1005,13 +1009,13 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
if (new Set(enNames).size != enNames.length) {
const dups = Utils.Duplicates(enNames)
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)
context.err(
`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`
)
}
@ -1026,17 +1030,17 @@ export class DetectDuplicatePresets extends DesugaringStep<LayoutConfig> {
Utils.SameObject(presetATags, presetBTags) &&
Utils.sameList(
presetA.preciseInput.snapToLayers,
presetB.preciseInput.snapToLayers,
presetB.preciseInput.snapToLayers
)
) {
context.err(
`This theme has multiple presets with the same tags: ${presetATags.asHumanString(
false,
false,
{},
{}
)}, namely the preset '${presets[i].title.textFor("en")}' and '${presets[
j
].title.textFor("en")}'`,
].title.textFor("en")}'`
)
}
}
@ -1061,13 +1065,13 @@ export class ValidateThemeEnsemble extends Conversion<
super(
"Validates that all themes together are logical, i.e. no duplicate ids exists within (overriden) themes",
[],
"ValidateThemeEnsemble",
"ValidateThemeEnsemble"
)
}
convert(
json: LayoutConfig[],
context: ConversionContext,
context: ConversionContext
): Map<
string,
{
@ -1118,11 +1122,11 @@ export class ValidateThemeEnsemble extends Conversion<
context.err(
[
"The layer with id '" +
id +
"' is found in multiple themes with different tag definitions:",
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"),
].join("\n")
)
}
}

View file

@ -338,7 +338,7 @@ export default class LayoutConfig implements LayoutInformation {
...json,
layers: json.layers.filter((l) => l["id"] !== "favourite"),
}
const usedImages =json._usedImages
const usedImages = json._usedImages
usedImages.sort()
this.usedImages = Utils.Dedup(usedImages)

View file

@ -963,7 +963,7 @@ export class TagRenderingConfigUtils {
tags,
country.split(";"),
center,
{sortByFrequency: true}
{ sortByFrequency: true }
)
)
})

View file

@ -2,7 +2,11 @@ import LayoutConfig from "./ThemeConfig/LayoutConfig"
import { SpecialVisualizationState } from "../UI/SpecialVisualization"
import { Changes } from "../Logic/Osm/Changes"
import { Store, UIEventSource } from "../Logic/UIEventSource"
import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
import {
FeatureSource,
IndexedFeatureSource,
WritableFeatureSource,
} from "../Logic/FeatureSource/FeatureSource"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
import { ExportableMap, MapProperties } from "./MapProperties"
import LayerState from "../Logic/State/LayerState"
@ -46,7 +50,9 @@ import BackgroundLayerResetter from "../Logic/Actors/BackgroundLayerResetter"
import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage"
import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
import NoElementsInViewDetector, { FeatureViewState } from "../Logic/Actors/NoElementsInViewDetector"
import NoElementsInViewDetector, {
FeatureViewState,
} from "../Logic/Actors/NoElementsInViewDetector"
import FilteredLayer from "./FilteredLayer"
import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector"
import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager"
@ -155,7 +161,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.featureSwitches = new FeatureSwitchState(layout)
this.guistate = new MenuState(
this.featureSwitches.featureSwitchWelcomeMessage.data,
layout.id,
layout.id
)
this.map = new UIEventSource<MlMap>(undefined)
const geolocationState = new GeoLocationState()
@ -171,14 +177,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
oauth_token: QueryParameters.GetQueryParameter(
"oauth_token",
undefined,
"Used to complete the login",
"Used to complete the login"
),
})
this.userRelatedState = new UserRelatedState(
this.osmConnection,
layout,
this.featureSwitches,
this.mapProperties,
this.mapProperties
)
this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => {
this.mapProperties.allowRotating.setData(fixated !== "yes")
@ -189,20 +195,20 @@ export default class ThemeViewState implements SpecialVisualizationState {
geolocationState,
this.selectedElement,
this.mapProperties,
this.userRelatedState.gpsLocationHistoryRetentionTime,
this.userRelatedState.gpsLocationHistoryRetentionTime
)
this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties)
this.availableLayers = AvailableRasterLayers.layersAvailableAt(
this.mapProperties.location,
this.osmConnection.isLoggedIn,
this.osmConnection.isLoggedIn
)
this.layerState = new LayerState(
this.osmConnection,
layout.layers,
layout.id,
this.featureSwitches.featureSwitchLayerDefault,
this.featureSwitches.featureSwitchLayerDefault
)
{
@ -211,7 +217,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const isDisplayed = QueryParameters.GetBooleanQueryParameter(
"overlay-" + rasterInfo.id,
rasterInfo.defaultState ?? true,
"Whether or not overlay layer " + rasterInfo.id + " is shown",
"Whether or not overlay layer " + rasterInfo.id + " is shown"
)
const state = { isDisplayed }
overlayLayerStates.set(rasterInfo.id, state)
@ -236,7 +242,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.osmConnection.Backend(),
(id) => this.layerState.filteredLayers.get(id).isDisplayed,
mvtAvailableLayers,
this.fullNodeDatabase,
this.fullNodeDatabase
)
let currentViewIndex = 0
@ -254,7 +260,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
id: "current_view_" + currentViewIndex,
}),
]
}),
})
)
this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds)
@ -272,19 +278,19 @@ export default class ThemeViewState implements SpecialVisualizationState {
featureSwitches: this.featureSwitches,
},
layout?.isLeftRightSensitive() ?? false,
(e) => this.reportError(e),
(e) => this.reportError(e)
)
this.historicalUserLocations = this.geolocation.historicalUserLocations
this.newFeatures = new NewGeometryFromChangesFeatureSource(
this.changes,
layoutSource,
this.featureProperties,
this.featureProperties
)
layoutSource.addSource(this.newFeatures)
const perLayer = new PerLayerFeatureSourceSplitter(
Array.from(this.layerState.filteredLayers.values()).filter(
(l) => l.layerDef?.source !== null,
(l) => l.layerDef?.source !== null
),
new ChangeGeometryApplicator(this.indexedFeatures, this.changes),
{
@ -295,10 +301,10 @@ export default class ThemeViewState implements SpecialVisualizationState {
"Got ",
features.length,
"leftover features, such as",
features[0].properties,
features[0].properties
)
},
},
}
)
this.perLayer = perLayer.perLayer
}
@ -338,12 +344,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.lastClickObject = new LastClickFeatureSource(
this.layout,
this.mapProperties.lastClickLocation,
this.userRelatedState.addNewFeatureMode,
this.userRelatedState.addNewFeatureMode
)
this.osmObjectDownloader = new OsmObjectDownloader(
this.osmConnection.Backend(),
this.changes,
this.changes
)
this.perLayerFiltered = this.showNormalDataOn(this.map)
@ -354,7 +360,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
currentZoom: this.mapProperties.zoom,
layerState: this.layerState,
bounds: this.visualFeedbackViewportBounds,
},
}
)
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
this.imageUploadManager = new ImageUploadManager(
@ -362,7 +368,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
Imgur.singleton,
this.featureProperties,
this.osmConnection,
this.changes,
this.changes
)
this.favourites = new FavouritesFeatureSource(this)
const longAgo = new Date()
@ -408,7 +414,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
LayoutSource.fromCacheZoomLevel,
fs,
this.featureProperties,
fs.layer.layerDef.maxAgeOfCache,
fs.layer.layerDef.maxAgeOfCache
)
toLocalStorage.set(layerId, storage)
})
@ -421,7 +427,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const doShowLayer = this.mapProperties.zoom.map(
(z) =>
(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) {
@ -438,7 +444,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
fs.layer,
fs,
(id) => this.featureProperties.getStore(id),
this.layerState.globalFilters,
this.layerState.globalFilters
)
filteringFeatureSource.set(layerName, filtered)
@ -582,7 +588,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
return
}
this.selectClosestAtCenter(0)
},
}
)
for (let i = 1; i < 9; i++) {
@ -600,7 +606,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
onUp: true,
},
doc,
() => this.selectClosestAtCenter(i - 1),
() => this.selectClosestAtCenter(i - 1)
)
}
@ -617,7 +623,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
if (this.featureSwitches.featureSwitchBackgroundSelection.data) {
this.guistate.backgroundLayerSelectionIsOpened.setData(true)
}
},
}
)
Hotkeys.RegisterHotkey(
{
@ -629,60 +635,57 @@ export default class ThemeViewState implements SpecialVisualizationState {
if (this.featureSwitches.featureSwitchFilter.data) {
this.guistate.openFilterView()
}
},
}
)
Hotkeys.RegisterHotkey(
{ shift: "O" },
Translations.t.hotkeyDocumentation.selectMapnik,
() => {
this.mapProperties.rasterLayer.setData(AvailableRasterLayers.osmCarto)
},
}
)
const setLayerCategory = (category: EliCategory) => {
const timeOfCall = new Date()
const available = this.availableLayers.store.addCallbackAndRunD(
available => {
const now = new Date()
const timeDiff= (now.getTime() - timeOfCall.getTime()) / 1000
if(timeDiff > 3){
return true // unregister
}
const current = this.mapProperties.rasterLayer
const best = RasterLayerUtils.SelectBestLayerAccordingTo(
available,
category,
current.data,
)
console.log("Best layer for category", category, "is", best.properties.id)
current.setData(best)
},
)
const available = this.availableLayers.store.addCallbackAndRunD((available) => {
const now = new Date()
const timeDiff = (now.getTime() - timeOfCall.getTime()) / 1000
if (timeDiff > 3) {
return true // unregister
}
const current = this.mapProperties.rasterLayer
const best = RasterLayerUtils.SelectBestLayerAccordingTo(
available,
category,
current.data
)
console.log("Best layer for category", category, "is", best.properties.id)
current.setData(best)
})
}
Hotkeys.RegisterHotkey(
{ nomod: "O" },
Translations.t.hotkeyDocumentation.selectOsmbasedmap,
() => setLayerCategory("osmbasedmap"),
() => setLayerCategory("osmbasedmap")
)
Hotkeys.RegisterHotkey(
{ nomod: "M" },
Translations.t.hotkeyDocumentation.selectMap,
() => setLayerCategory("map"),
() => setLayerCategory("map")
)
Hotkeys.RegisterHotkey(
{ nomod: "P" },
Translations.t.hotkeyDocumentation.selectAerial,
() => setLayerCategory("photo"),
() => setLayerCategory("photo")
)
Hotkeys.RegisterHotkey(
{ nomod: "L" },
Translations.t.hotkeyDocumentation.geolocate,
() => {
this.geolocationControl.handleClick()
},
}
)
return true
})
@ -694,7 +697,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
Translations.t.hotkeyDocumentation.translationMode,
() => {
Locale.showLinkToWeblate.setData(!Locale.showLinkToWeblate.data)
},
}
)
}
@ -705,7 +708,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const normalLayers = this.layout.layers.filter(
(l) =>
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))
@ -713,7 +716,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
(l) =>
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
l.source.geojsonSource === undefined &&
l.doCount,
l.doCount
)
const summaryTileSource = new SummaryTileSource(
Constants.SummaryServer,
@ -722,7 +725,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.mapProperties,
{
isActive: this.mapProperties.zoom.map((z) => z < maxzoom),
},
}
)
return new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers)
@ -743,12 +746,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
gps_location_history: this.geolocation.historicalUserLocations,
gps_track: this.geolocation.historicalUserLocationsTrack,
selected_element: new StaticFeatureSource(
this.selectedElement.map((f) => (f === undefined ? empty : [f])),
this.selectedElement.map((f) => (f === undefined ? empty : [f]))
),
range: new StaticFeatureSource(
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,
favourite: this.favourites,
@ -763,7 +766,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
ShowDataLayer.showRange(
this.map,
new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]),
this.featureSwitches.featureSwitchIsTesting,
this.featureSwitches.featureSwitchIsTesting
)
}
const currentViewLayer = this.layout.layers.find((l) => l.id === "current_view")
@ -777,7 +780,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
currentViewLayer,
this.layout,
this.osmObjectDownloader,
this.featureProperties,
this.featureProperties
)
})
}
@ -821,20 +824,20 @@ export default class ThemeViewState implements SpecialVisualizationState {
const lastClickLayerConfig = new LayerConfig(
<LayerConfigJson>last_click_layerconfig,
"last_click",
"last_click"
)
const lastClickFiltered =
lastClickLayerConfig.isShown === undefined
? specialLayers.last_click
: specialLayers.last_click.features.mapD((fs) =>
fs.filter((f) => {
const matches = lastClickLayerConfig.isShown.matchesProperties(
f.properties,
)
console.debug("LastClick ", f, "matches", matches)
return matches
}),
)
fs.filter((f) => {
const matches = lastClickLayerConfig.isShown.matchesProperties(
f.properties
)
console.debug("LastClick ", f, "matches", matches)
return matches
})
)
new ShowDataLayer(this.map, {
features: new StaticFeatureSource(lastClickFiltered),
layer: lastClickLayerConfig,
@ -881,7 +884,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.mapProperties.rasterLayer,
this.availableLayers,
this.featureSwitches.backgroundLayerId,
this.userRelatedState.preferredBackgroundLayer,
this.userRelatedState.preferredBackgroundLayer
)
}
@ -897,7 +900,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
? ">>> _Not_ reporting error to report server as testmode is on"
: ">>> Reporting error to",
Constants.ErrorReportServer,
message,
message
)
if (isTesting) {
return