forked from MapComplete/MapComplete
Fix various bugs; improve UK-addresses theme
This commit is contained in:
parent
e20cf0abfa
commit
8ca9e4f36c
20 changed files with 357 additions and 164 deletions
|
@ -1,7 +1,6 @@
|
|||
import {UIEventSource} from "../../UIEventSource";
|
||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||
import Hash from "../../Web/Hash";
|
||||
import {BBox} from "../../BBox";
|
||||
import {ElementStorage} from "../../ElementStorage";
|
||||
import {TagsFilter} from "../../Tags/TagsFilter";
|
||||
|
@ -20,7 +19,8 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
};
|
||||
private readonly _alreadyRegistered = new Set<UIEventSource<any>>();
|
||||
private readonly _is_dirty = new UIEventSource(false)
|
||||
|
||||
private previousFeatureSet : Set<any> = undefined;
|
||||
|
||||
constructor(
|
||||
state: {
|
||||
locationControl: UIEventSource<{ zoom: number }>,
|
||||
|
@ -65,18 +65,12 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
private update() {
|
||||
const self = this;
|
||||
const layer = this.upstream.layer;
|
||||
const features: { feature: any; freshness: Date }[] = this.upstream.features.data;
|
||||
const features: { feature: any; freshness: Date }[] = (this.upstream.features.data ?? []);
|
||||
const includedFeatureIds = new Set<string>();
|
||||
const newFeatures = features.filter((f) => {
|
||||
|
||||
self.registerCallback(f.feature)
|
||||
|
||||
if (
|
||||
(this.state.selectedElement !== undefined && this.state.selectedElement.data?.id === f.feature.properties.id) ||
|
||||
(Hash.hash.data !== undefined && f.feature.properties.id === Hash.hash.data)) {
|
||||
// This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away
|
||||
return true;
|
||||
}
|
||||
|
||||
const isShown = layer.layerDef.isShown;
|
||||
const tags = f.feature.properties;
|
||||
if (isShown.IsKnown(tags)) {
|
||||
|
@ -97,12 +91,30 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
includedFeatureIds.add(f.feature.properties.id)
|
||||
return true;
|
||||
});
|
||||
|
||||
this.features.setData(newFeatures);
|
||||
const previousSet = this.previousFeatureSet;
|
||||
this._is_dirty.setData(false)
|
||||
|
||||
// Is there any difference between the two sets?
|
||||
if(previousSet !== undefined && previousSet.size === includedFeatureIds.size){
|
||||
// The size of the sets is the same - they _might_ be identical
|
||||
const newItemFound = Array.from(includedFeatureIds).some(id => !previousSet.has(id))
|
||||
if(!newItemFound){
|
||||
// We know that:
|
||||
// - The sets have the same size
|
||||
// - Every item from the new set has been found in the old set
|
||||
// which means they are identical!
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Something new has been found!
|
||||
this.features.setData(newFeatures);
|
||||
|
||||
}
|
||||
|
||||
private registerCallback(feature: any) {
|
||||
|
@ -115,10 +127,11 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
}
|
||||
this._alreadyRegistered.add(src)
|
||||
|
||||
const self = this;
|
||||
src.addCallbackAndRunD(_ => {
|
||||
self._is_dirty.setData(true)
|
||||
})
|
||||
const self = this;
|
||||
// Add a callback as a changed tag migh change the filter
|
||||
src.addCallbackAndRunD(_ => {
|
||||
self._is_dirty.setData(true)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -89,6 +89,10 @@ export default class OsmFeatureSource {
|
|||
if (z > 20) {
|
||||
throw "This is an absurd high zoom level"
|
||||
}
|
||||
|
||||
if( z < 14){
|
||||
throw `Zoom ${z} is too much for OSM to handle! Use a higher zoom level!`
|
||||
}
|
||||
|
||||
const bbox = BBox.fromTile(z, x, y)
|
||||
const url = `${this._backend}/api/0.6/map?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}`
|
||||
|
|
|
@ -8,6 +8,7 @@ import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson";
|
|||
import LineRenderingConfigJson from "../Json/LineRenderingConfigJson";
|
||||
import {LayerConfigJson} from "../Json/LayerConfigJson";
|
||||
import Constants from "../../Constants";
|
||||
import {AllKnownLayouts} from "../../../Customizations/AllKnownLayouts";
|
||||
|
||||
export interface DesugaringContext {
|
||||
tagRenderings: Map<string, TagRenderingConfigJson>
|
||||
|
@ -15,19 +16,14 @@ export interface DesugaringContext {
|
|||
}
|
||||
|
||||
export abstract class Conversion<TIn, TOut> {
|
||||
protected readonly doc: string;
|
||||
public readonly modifiedAttributes: string[];
|
||||
protected readonly doc: string;
|
||||
|
||||
constructor(doc: string, modifiedAttributes: string[] = []) {
|
||||
this.modifiedAttributes = modifiedAttributes;
|
||||
this.doc = doc + "\n\nModified attributes are\n" + modifiedAttributes.join(", ");
|
||||
}
|
||||
|
||||
public convertStrict(state: DesugaringContext, json: TIn, context: string): TOut {
|
||||
const fixed = this.convert(state, json, context)
|
||||
return DesugaringStep.strict(fixed)
|
||||
}
|
||||
|
||||
public static strict<T>(fixed: { errors: string[], warnings: string[], result?: T }): T {
|
||||
if (fixed?.errors?.length > 0) {
|
||||
throw fixed.errors.join("\n");
|
||||
|
@ -36,6 +32,11 @@ export abstract class Conversion<TIn, TOut> {
|
|||
return fixed.result;
|
||||
}
|
||||
|
||||
public convertStrict(state: DesugaringContext, json: TIn, context: string): TOut {
|
||||
const fixed = this.convert(state, json, context)
|
||||
return DesugaringStep.strict(fixed)
|
||||
}
|
||||
|
||||
abstract convert(state: DesugaringContext, json: TIn, context: string): { result: TOut, errors: string[], warnings: string[] }
|
||||
|
||||
public convertAll(state: DesugaringContext, jsons: TIn[], context: string): { result: TOut[], errors: string[], warnings: string[] } {
|
||||
|
@ -133,9 +134,9 @@ class Fuse<T> extends DesugaringStep<T> {
|
|||
convert(state: DesugaringContext, json: T, context: string): { result: T; errors: string[]; warnings: string[] } {
|
||||
const errors = []
|
||||
const warnings = []
|
||||
for (let i = 0; i < this.steps.length; i++){
|
||||
for (let i = 0; i < this.steps.length; i++) {
|
||||
const step = this.steps[i];
|
||||
let r = step.convert(state, json, context + "(fusion "+this.constructor.name+"."+i+")")
|
||||
let r = step.convert(state, json, context + "(fusion " + this.constructor.name + "." + i + ")")
|
||||
errors.push(...r.errors)
|
||||
warnings.push(...r.warnings)
|
||||
json = r.result
|
||||
|
@ -157,6 +158,16 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
|
|||
super("Converts a tagRenderingSpec into the full tagRendering", []);
|
||||
}
|
||||
|
||||
convert(state: DesugaringContext, json: string | TagRenderingConfigJson | { builtin: string | string[]; override: any }, context: string): { result: TagRenderingConfigJson[]; errors: string[]; warnings: string[] } {
|
||||
const errors = []
|
||||
const warnings = []
|
||||
|
||||
return {
|
||||
result: this.convertUntilStable(state, json, warnings, errors, context),
|
||||
errors, warnings
|
||||
};
|
||||
}
|
||||
|
||||
private lookup(state: DesugaringContext, name: string): TagRenderingConfigJson[] {
|
||||
if (state.tagRenderings.has(name)) {
|
||||
return [state.tagRenderings.get(name)]
|
||||
|
@ -223,6 +234,14 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
|
|||
if (typeof names === "string") {
|
||||
names = [names]
|
||||
}
|
||||
|
||||
for (const key of Object.keys(tr)) {
|
||||
if (key === "builtin" || key === "override" || key === "id") {
|
||||
continue
|
||||
}
|
||||
errors.push("At " + ctx + ": an object calling a builtin can only have keys `builtin` or `override`, but a key with name `" + key + "` was found. This won't be picked up! The full object is: " + JSON.stringify(tr))
|
||||
}
|
||||
|
||||
const trs: TagRenderingConfigJson[] = []
|
||||
for (const name of names) {
|
||||
const lookup = this.lookup(state, name)
|
||||
|
@ -230,10 +249,10 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
|
|||
errors.push(ctx + ": The tagRendering with identifier " + name + " was not found.\n\tDid you mean one of " + Array.from(state.tagRenderings.keys()).join(", ") + "?")
|
||||
continue
|
||||
}
|
||||
for (let tr of lookup) {
|
||||
tr = Utils.Clone<any>(tr)
|
||||
Utils.Merge(tr["override"] ?? {}, tr)
|
||||
trs.push(tr)
|
||||
for (let foundTr of lookup) {
|
||||
foundTr = Utils.Clone<any>(foundTr)
|
||||
Utils.Merge(tr["override"] ?? {}, foundTr)
|
||||
trs.push(foundTr)
|
||||
}
|
||||
}
|
||||
return trs;
|
||||
|
@ -257,17 +276,6 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
convert(state: DesugaringContext, json: string | TagRenderingConfigJson | { builtin: string | string[]; override: any }, context: string): { result: TagRenderingConfigJson[]; errors: string[]; warnings: string[] } {
|
||||
const errors = []
|
||||
const warnings = []
|
||||
|
||||
return {
|
||||
result: this.convertUntilStable(state, json, warnings, errors, context),
|
||||
errors, warnings
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ExpandGroupRewrite extends Conversion<{
|
||||
|
@ -287,31 +295,6 @@ class ExpandGroupRewrite extends Conversion<{
|
|||
);
|
||||
}
|
||||
|
||||
/* Used for left|right group creation and replacement */
|
||||
private prepConfig(keyToRewrite: string, target: string, tr: TagRenderingConfigJson) {
|
||||
|
||||
function replaceRecursive(transl: string | any) {
|
||||
if (typeof transl === "string") {
|
||||
return transl.replace(keyToRewrite, target)
|
||||
}
|
||||
if (transl.map !== undefined) {
|
||||
return transl.map(o => replaceRecursive(o))
|
||||
}
|
||||
transl = {...transl}
|
||||
for (const key in transl) {
|
||||
transl[key] = replaceRecursive(transl[key])
|
||||
}
|
||||
return transl
|
||||
}
|
||||
|
||||
const orig = tr;
|
||||
tr = replaceRecursive(tr)
|
||||
|
||||
tr.id = target + "-" + orig.id
|
||||
tr.group = target
|
||||
return tr
|
||||
}
|
||||
|
||||
convert(state: DesugaringContext, json:
|
||||
{
|
||||
rewrite:
|
||||
|
@ -344,14 +327,14 @@ class ExpandGroupRewrite extends Conversion<{
|
|||
const trs: TagRenderingConfigJson[] = []
|
||||
|
||||
for (const tr of subRenderings) {
|
||||
trs.push( this.prepConfig(source, target, tr))
|
||||
trs.push(this.prepConfig(source, target, tr))
|
||||
}
|
||||
if(rewrittenPerGroup.has(groupName)){
|
||||
if (rewrittenPerGroup.has(groupName)) {
|
||||
rewrittenPerGroup.get(groupName).push(...trs)
|
||||
|
||||
}else{
|
||||
rewrittenPerGroup.set(groupName, trs)
|
||||
|
||||
} else {
|
||||
rewrittenPerGroup.set(groupName, trs)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -365,9 +348,9 @@ class ExpandGroupRewrite extends Conversion<{
|
|||
})
|
||||
|
||||
|
||||
rewrittenPerGroup.forEach((group, groupName) => {
|
||||
rewrittenPerGroup.forEach((group, _) => {
|
||||
group.forEach(tr => {
|
||||
if(tr.id === undefined || tr.id === ""){
|
||||
if (tr.id === undefined || tr.id === "") {
|
||||
errors.push("A tagrendering has an empty ID after expanding the tag")
|
||||
}
|
||||
})
|
||||
|
@ -378,6 +361,31 @@ class ExpandGroupRewrite extends Conversion<{
|
|||
errors, warnings
|
||||
};
|
||||
}
|
||||
|
||||
/* Used for left|right group creation and replacement */
|
||||
private prepConfig(keyToRewrite: string, target: string, tr: TagRenderingConfigJson) {
|
||||
|
||||
function replaceRecursive(transl: string | any) {
|
||||
if (typeof transl === "string") {
|
||||
return transl.replace(keyToRewrite, target)
|
||||
}
|
||||
if (transl.map !== undefined) {
|
||||
return transl.map(o => replaceRecursive(o))
|
||||
}
|
||||
transl = {...transl}
|
||||
for (const key in transl) {
|
||||
transl[key] = replaceRecursive(transl[key])
|
||||
}
|
||||
return transl
|
||||
}
|
||||
|
||||
const orig = tr;
|
||||
tr = replaceRecursive(tr)
|
||||
|
||||
tr.id = target + "-" + orig.id
|
||||
tr.group = target
|
||||
return tr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -406,10 +414,10 @@ export class UpdateLegacyLayer extends DesugaringStep<LayerConfigJson | string |
|
|||
}
|
||||
|
||||
if (config.tagRenderings !== undefined) {
|
||||
let i =0;
|
||||
let i = 0;
|
||||
for (const tagRendering of config.tagRenderings) {
|
||||
i++;
|
||||
if(typeof tagRendering === "string" || tagRendering["builtin"] !== undefined){
|
||||
if (typeof tagRendering === "string" || tagRendering["builtin"] !== undefined) {
|
||||
continue
|
||||
}
|
||||
if (tagRendering["id"] === undefined) {
|
||||
|
@ -419,8 +427,8 @@ export class UpdateLegacyLayer extends DesugaringStep<LayerConfigJson | string |
|
|||
delete tagRendering["#"]
|
||||
} else if (tagRendering["freeform"]?.key !== undefined) {
|
||||
tagRendering["id"] = config.id + "-" + tagRendering["freeform"]["key"]
|
||||
}else{
|
||||
tagRendering["id"] = "tr-"+i
|
||||
} else {
|
||||
tagRendering["id"] = "tr-" + i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -618,12 +626,12 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
|||
}
|
||||
|
||||
|
||||
if (this._isBuiltin ) {
|
||||
if (this._isBuiltin) {
|
||||
if (json.tagRenderings?.some(tr => tr["id"] === "")) {
|
||||
const emptyIndexes : number[] = []
|
||||
for (let i = 0; i < json.tagRenderings.length; i++){
|
||||
const emptyIndexes: number[] = []
|
||||
for (let i = 0; i < json.tagRenderings.length; i++) {
|
||||
const tagRendering = json.tagRenderings[i];
|
||||
if(tagRendering["id"] === ""){
|
||||
if (tagRendering["id"] === "") {
|
||||
emptyIndexes.push(i)
|
||||
}
|
||||
}
|
||||
|
@ -634,19 +642,20 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
|||
if (duplicateIds.length > 0 && !Utils.runningFromConsole) {
|
||||
errors.push(`Some tagRenderings have a duplicate id: ${duplicateIds} (at ${context}.tagRenderings)`)
|
||||
}
|
||||
|
||||
|
||||
if(json.description === undefined){
|
||||
|
||||
if (Constants.priviliged_layers.indexOf(json.id) >= 0) {
|
||||
errors.push(
|
||||
context + ": A priviliged layer must have a description"
|
||||
)
|
||||
} else {
|
||||
warnings.push(
|
||||
context + ": A builtin layer should have a description"
|
||||
)
|
||||
}}
|
||||
|
||||
|
||||
if (json.description === undefined) {
|
||||
|
||||
if (Constants.priviliged_layers.indexOf(json.id) >= 0) {
|
||||
errors.push(
|
||||
context + ": A priviliged layer must have a description"
|
||||
)
|
||||
} else {
|
||||
warnings.push(
|
||||
context + ": A builtin layer should have a description"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
errors.push(e)
|
||||
|
@ -774,7 +783,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
|
|||
private static CalculateDependencies(alreadyLoaded: LayerConfigJson[], allKnownLayers: Map<string, LayerConfigJson>, themeId: string): LayerConfigJson[] {
|
||||
const dependenciesToAdd: LayerConfigJson[] = []
|
||||
const loadedLayerIds: Set<string> = new Set<string>(alreadyLoaded.map(l => l.id));
|
||||
|
||||
|
||||
// Verify cross-dependencies
|
||||
let unmetDependencies: { neededLayer: string, neededBy: string, reason: string, context?: string }[] = []
|
||||
do {
|
||||
|
@ -789,7 +798,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
|
|||
// Their existance is checked elsewhere, so this is fine
|
||||
unmetDependencies = dependencies.filter(dep => !loadedLayerIds.has(dep.neededLayer))
|
||||
for (const unmetDependency of unmetDependencies) {
|
||||
if(loadedLayerIds.has(unmetDependency.neededLayer)){
|
||||
if (loadedLayerIds.has(unmetDependency.neededLayer)) {
|
||||
continue
|
||||
}
|
||||
const dep = allKnownLayers.get(unmetDependency.neededLayer)
|
||||
|
@ -825,9 +834,9 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
|
|||
})
|
||||
|
||||
const dependencies = AddDependencyLayersToTheme.CalculateDependencies(layers, allKnownLayers, theme.id);
|
||||
if(dependencies.length > 0){
|
||||
|
||||
warnings.push(context+": added "+dependencies.map(d => d.id).join(", ")+" to the theme as they are needed")
|
||||
if (dependencies.length > 0) {
|
||||
|
||||
warnings.push(context + ": added " + dependencies.map(d => d.id).join(", ") + " to the theme as they are needed")
|
||||
}
|
||||
layers.unshift(...dependencies);
|
||||
|
||||
|
@ -842,12 +851,36 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
|
|||
}
|
||||
}
|
||||
|
||||
class SetDefault extends DesugaringStep<LayerConfigJson> {
|
||||
private readonly value: object;
|
||||
private readonly key: string;
|
||||
|
||||
constructor(key: string, value: object) {
|
||||
super("Sets " + key + " to a default value if undefined");
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
convert(state: DesugaringContext, json: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings: string[] } {
|
||||
if (json[this.key] === undefined) {
|
||||
json = {...json}
|
||||
json[this.key] = this.value
|
||||
}
|
||||
|
||||
return {
|
||||
errors: [], warnings: [],
|
||||
result: json
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class PrepareLayer extends Fuse<LayerConfigJson> {
|
||||
constructor() {
|
||||
super(
|
||||
"Fully prepares and expands a layer for the LayerConfig.",
|
||||
new OnEveryConcat("tagRenderings", new ExpandGroupRewrite()),
|
||||
new OnEveryConcat("tagRenderings", new ExpandTagRendering()),
|
||||
new SetDefault("titleIcons", ["defaults"]),
|
||||
new OnEveryConcat("titleIcons", new ExpandTagRendering())
|
||||
);
|
||||
}
|
||||
|
@ -888,8 +921,15 @@ class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfig
|
|||
errors.push(context + ": The layer with name " + json + " was not found as a builtin layer")
|
||||
continue
|
||||
}
|
||||
Utils.Merge(json["override"], found);
|
||||
layers.push(found)
|
||||
if (json["override"]["tagRenderings"] !== undefined && (found["tagRenderings"] ?? []).length > 0) {
|
||||
errors.push(`At ${context}: when overriding a layer, an override is not allowed to override into tagRenderings. Use "+tagRenderings" or "tagRenderings+" instead to prepend or append some questions.`)
|
||||
}
|
||||
try {
|
||||
Utils.Merge(json["override"], found);
|
||||
layers.push(found)
|
||||
} catch (e) {
|
||||
errors.push(`At ${context}: could not apply an override due to: ${e}.\nThe override is: ${JSON.stringify(json["override"],)}`)
|
||||
}
|
||||
}
|
||||
return {
|
||||
result: layers,
|
||||
|
@ -906,19 +946,25 @@ class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfig
|
|||
|
||||
}
|
||||
|
||||
class AddDefaultLayers extends DesugaringStep<LayoutConfigJson>{
|
||||
|
||||
class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
|
||||
|
||||
constructor() {
|
||||
super("Adds the default layers, namely: "+Constants.added_by_default.join(", "),["layers"]);
|
||||
super("Adds the default layers, namely: " + Constants.added_by_default.join(", "), ["layers"]);
|
||||
}
|
||||
|
||||
|
||||
convert(state: DesugaringContext, json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
|
||||
const errors = []
|
||||
json.layers = [...json.layers]
|
||||
|
||||
if (json.id === "personal") {
|
||||
const publicIds = AllKnownLayouts.AllPublicLayers().map(l => l.id)
|
||||
json.layers = publicIds.map(id => state.sharedLayers.get(id))
|
||||
}
|
||||
|
||||
for (const layerName of Constants.added_by_default) {
|
||||
const v = state.sharedLayers.get(layerName)
|
||||
if(v === undefined){
|
||||
errors.push("Default layer "+layerName+" not found")
|
||||
if (v === undefined) {
|
||||
errors.push("Default layer " + layerName + " not found")
|
||||
}
|
||||
json.layers.push(v)
|
||||
}
|
||||
|
@ -928,7 +974,7 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson>{
|
|||
warnings: []
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class PrepareTheme extends Fuse<LayoutConfigJson> {
|
||||
|
@ -939,7 +985,6 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
|
|||
new AddDefaultLayers(),
|
||||
new AddDependencyLayersToTheme(),
|
||||
new OnEvery("layers", new PrepareLayer()),
|
||||
|
||||
);
|
||||
}
|
||||
}
|
|
@ -140,9 +140,9 @@ export interface LayerConfigJson {
|
|||
* Small icons shown next to the title.
|
||||
* If not specified, the OsmLink and wikipedia links will be used by default.
|
||||
* Use an empty array to hide them.
|
||||
* Note that "defaults" will insert all the default titleIcons
|
||||
* Note that "defaults" will insert all the default titleIcons (which are added automatically)
|
||||
*/
|
||||
titleIcons?: (string | TagRenderingConfigJson)[];
|
||||
titleIcons?: (string | TagRenderingConfigJson)[] | ["defaults"];
|
||||
|
||||
|
||||
mapRendering: null | (PointRenderingConfigJson | LineRenderingConfigJson)[]
|
||||
|
|
|
@ -67,6 +67,10 @@ export default class LayerConfig extends WithContextLoader {
|
|||
context = context + "." + json.id;
|
||||
super(json, context)
|
||||
this.id = json.id;
|
||||
|
||||
if(json.id === undefined){
|
||||
throw "Not a valid layer: id is undefined: "+JSON.stringify(json)
|
||||
}
|
||||
|
||||
if (json.source === undefined) {
|
||||
throw "Layer " + this.id + " does not define a source section (" + context + ")"
|
||||
|
|
|
@ -23,8 +23,8 @@ export default class EditableTagRendering extends Toggle {
|
|||
) {
|
||||
|
||||
// The tagrendering is hidden if:
|
||||
// The answer is unknown. The questionbox will then show the question
|
||||
// There is a condition hiding the answer
|
||||
// - The answer is unknown. The questionbox will then show the question
|
||||
// - There is a condition hiding the answer
|
||||
const renderingIsShown = tags.map(tags =>
|
||||
configuration.IsKnown(tags) &&
|
||||
(configuration?.condition?.matchesProperties(tags) ?? true))
|
||||
|
|
|
@ -216,7 +216,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
|
||||
/**
|
||||
* Returns true if this tag rendering has a minimap in some language.
|
||||
* Note: this might be hidden by conditions
|
||||
* Note: this minimap can be hidden by conditions
|
||||
*/
|
||||
private static hasMinimap(renderingConfig: TagRenderingConfig): boolean {
|
||||
const translations: Translation[] = Utils.NoNull([renderingConfig.render, ...(renderingConfig.mappings ?? []).map(m => m.then)]);
|
||||
|
|
|
@ -68,10 +68,11 @@ export default class QuestionBox extends VariableUiElement {
|
|||
if (tagRendering.IsKnown(tags)) {
|
||||
continue;
|
||||
}
|
||||
if (tagRendering.condition &&
|
||||
!tagRendering.condition.matchesProperties(tags)) {
|
||||
// Filtered away by the condition, so it is kindof known
|
||||
continue;
|
||||
if (tagRendering.condition) {
|
||||
if (!tagRendering.condition.matchesProperties(tags)) {
|
||||
// Filtered away by the condition, so it is kindof known
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// this value is NOT known - this is the question we have to show!
|
||||
|
@ -79,7 +80,7 @@ export default class QuestionBox extends VariableUiElement {
|
|||
}
|
||||
return undefined; // The questions are depleted
|
||||
}, [skippedQuestions]);
|
||||
|
||||
|
||||
const questionsToAsk: UIEventSource<BaseUIElement[]> = tagsSource.map(tags => {
|
||||
if (tags === undefined) {
|
||||
return [];
|
||||
|
|
|
@ -148,11 +148,12 @@ export default class ShowDataLayer {
|
|||
const mp = options.leafletMap.data;
|
||||
|
||||
if(mp === null){
|
||||
return true; // Unregister as the map is destroyed
|
||||
return true; // Unregister as the map has been destroyed
|
||||
}
|
||||
if (mp === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._cleanCount++
|
||||
// clean all the old stuff away, if any
|
||||
if (this.geoLayer !== undefined) {
|
||||
|
|
3
Utils.ts
3
Utils.ts
|
@ -269,6 +269,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
v = "" + v
|
||||
}
|
||||
v = v.replace(/\n/g, "<br/>")
|
||||
}else{
|
||||
// v === undefined
|
||||
v = ""
|
||||
}
|
||||
txt = txt.replace("{" + key + "}", v)
|
||||
match = txt.match(regex)
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
"startZoom": 1,
|
||||
"widenFactor": 1,
|
||||
"layers": [
|
||||
"public_bookcase",
|
||||
"note_import"
|
||||
"public_bookcase"
|
||||
]
|
||||
}
|
|
@ -30,28 +30,6 @@
|
|||
"minzoom": 19
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"builtin": "current_view",
|
||||
"override": {
|
||||
"+mapRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point"
|
||||
],
|
||||
"icon": {
|
||||
"render": "./assets/svg/robot.svg"
|
||||
},
|
||||
"iconSize": "15,15,center"
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
{
|
||||
"id": "hw",
|
||||
"render": "Beep boop! I'm a bot!"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"builtin": "type_node",
|
||||
"override": {
|
||||
|
|
|
@ -304,7 +304,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"iconSize": "30,30,bottom"
|
||||
"iconSize": "30,30,center"
|
||||
}
|
||||
],
|
||||
"filter": [
|
||||
|
|
|
@ -189,15 +189,46 @@
|
|||
"description": "Alamat"
|
||||
},
|
||||
{
|
||||
"builtin": "address",
|
||||
"override": {
|
||||
"calculatedTags": [
|
||||
"_closest_3_street_names=feat.closestn('named_streets',3, 'name').map(f => f.feat.properties.name)",
|
||||
"_closest_street:0:name=JSON.parse(feat.properties._closest_3_street_names)[0]",
|
||||
"_closest_street:1:name=JSON.parse(feat.properties._closest_3_street_names)[1]",
|
||||
"_closest_street:2:name=JSON.parse(feat.properties._closest_3_street_names)[2]"
|
||||
]
|
||||
"id": "address",
|
||||
"name": {
|
||||
"en": "Known addresses in OSM",
|
||||
"de": "Bekannte Adressen in OSM",
|
||||
"zh_Hant": "OSM 上已知的地址",
|
||||
"hu": "Ismert címek az OSM-en",
|
||||
"nl": "Bekende adressen in OSM"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"or": [
|
||||
"addr:housenumber~*",
|
||||
"addr:street~*",
|
||||
"ref:inspireid~*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Known address",
|
||||
"de": "Bekannte Adresse",
|
||||
"hu": "Ismert cím",
|
||||
"nl": "Bekend adres"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"en": "Addresses",
|
||||
"nl": "Adressen",
|
||||
"de": "Adressen",
|
||||
"ru": "Адреса",
|
||||
"zh_Hant": "地址",
|
||||
"hu": "Címek"
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_closest_3_street_names=feat.closestn('named_streets',3, 'name').map(f => f.feat.properties.name)",
|
||||
"_closest_street:0:name=JSON.parse(feat.properties._closest_3_street_names)[0]",
|
||||
"_closest_street:1:name=JSON.parse(feat.properties._closest_3_street_names)[1]",
|
||||
"_closest_street:2:name=JSON.parse(feat.properties._closest_3_street_names)[2]"
|
||||
],
|
||||
"tagRenderings": [
|
||||
{
|
||||
"id": "uk_addresses_explanation_osm",
|
||||
|
@ -265,6 +296,24 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "uk_addresses_unit",
|
||||
"render": "The sub-part of this address is {addr:unit}",
|
||||
"question": {
|
||||
"en": "What is the unit indication of this address? <div class='subtle'>This is the letter or number of the letterbox here, if multiple letterboxes share the same street and housenumber. If there are multiple at the same location, add them here with a <b>;</b> between them</div>"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "addr:unit"
|
||||
},
|
||||
"mappings": [{
|
||||
"if": "addr:unit=",
|
||||
"then": "This address has no subparts. <div class='subtle'>Subparts are e.g. appartment numbers, extra letters or numbers if there are multiple letterboxes, ...</div>"
|
||||
}
|
||||
],
|
||||
"condition": {
|
||||
"or": ["addr:housenumber~*","addr:housename~*"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "uk_addresses_street",
|
||||
"render": {
|
||||
|
@ -281,31 +330,27 @@
|
|||
"mappings": [
|
||||
{
|
||||
"if": "addr:street:={_closest_street:0:name}",
|
||||
"then": "Located in <b>{_closest_street:0:name}</b>",
|
||||
"then": "This address is in street <b>{_closest_street:0:name}</b>",
|
||||
"hideInAnswer": "_closest_street:0:name="
|
||||
},
|
||||
{
|
||||
"if": "addr:street:={_closest_street:1:name}",
|
||||
"then": "Located in <b>{_closest_street:1:name}</b>",
|
||||
"then": "This address is in street <b>{_closest_street:1:name}</b>",
|
||||
"hideInAnswer": "_closest_street:1:name="
|
||||
},
|
||||
{
|
||||
"if": "addr:street:={_closest_street:2:name}",
|
||||
"then": "Located in <b>{_closest_street:2:name}</b>",
|
||||
"then": "This address is in street <b>{_closest_street:2:name}</b>",
|
||||
"hideInAnswer": "_closest_street:2:name="
|
||||
}
|
||||
],
|
||||
"condition": {
|
||||
"and": [
|
||||
"nohousenumber!~yes"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
"id": "fixme",
|
||||
"render": "<b>Fixme description</b>{fixme}",
|
||||
"render": "<b>This address is complicated - therefore someone has given a description of what should be fixed:</b>{fixme}",
|
||||
"question": {
|
||||
"en": "What should be fixed here? Please explain"
|
||||
"en": "What should be fixed here? Please explain what the address is"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "fixme"
|
||||
|
@ -313,7 +358,7 @@
|
|||
"mappings": [
|
||||
{
|
||||
"if": "fixme=",
|
||||
"then": "No fixme - write something here to explain complicated cases"
|
||||
"then": "Is this address very complicated? - write something here to explain complicated cases"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -324,6 +369,62 @@
|
|||
"en": "{image_carousel(image:address)}<br/>{image_upload(image:address, Add image of the address)}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"mapRendering": [
|
||||
{
|
||||
"label": {
|
||||
"render": "<div style='margin-top: -42px; color: white' class='rounded-full p-1 font-bold relative'>{addr:housenumber}</div>",
|
||||
"condition": "addr:housenumber~*"
|
||||
},
|
||||
"iconSize": "50,50,center",
|
||||
"icon": {
|
||||
"render": "./assets/layers/address/housenumber_blank.svg",
|
||||
"mappings": [
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
{
|
||||
"and": [
|
||||
"addr:housenumber=",
|
||||
"nohousenumber!=yes"
|
||||
]
|
||||
},
|
||||
"addr:street="
|
||||
]
|
||||
},
|
||||
"then": "./assets/themes/uk_addresses/housenumber_unknown.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
]
|
||||
},
|
||||
{
|
||||
"color": {
|
||||
"render": "#00f",
|
||||
"mappings": [
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
{
|
||||
"and": [
|
||||
"addr:housenumber=",
|
||||
"nohousenumber!=yes"
|
||||
]
|
||||
},
|
||||
"addr:street="
|
||||
]
|
||||
},
|
||||
"then": "#ff0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"width": {
|
||||
"render": "8"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"named_streets"
|
||||
|
|
|
@ -1083,6 +1083,8 @@
|
|||
"description": "Tragen Sie zu OpenStreetMap bei, indem Sie Adressinformationen ausfüllen",
|
||||
"layers": {
|
||||
"2": {
|
||||
"description": "Adressen",
|
||||
"name": "Bekannte Adressen in OSM",
|
||||
"tagRenderings": {
|
||||
"uk_addresses_explanation_osm": {
|
||||
"render": "Diese Adresse ist in OpenStreetMap gespeichert"
|
||||
|
@ -1100,6 +1102,9 @@
|
|||
"question": "In welcher Straße befindet sich diese Adresse?",
|
||||
"render": "Diese Adresse befindet sich in der Straße <b>{addr:street}</b>"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Bekannte Adresse"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -967,6 +967,9 @@
|
|||
"contributor": {
|
||||
"render": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>"
|
||||
},
|
||||
"render_id": {
|
||||
"render": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
|
||||
},
|
||||
"theme": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
|
@ -1289,12 +1292,14 @@
|
|||
}
|
||||
},
|
||||
"2": {
|
||||
"description": "Addresses",
|
||||
"name": "Known addresses in OSM",
|
||||
"tagRenderings": {
|
||||
"address-sign-image": {
|
||||
"render": "{image_carousel(image:address)}<br/>{image_upload(image:address, Add image of the address)}"
|
||||
},
|
||||
"fixme": {
|
||||
"question": "What should be fixed here? Please explain"
|
||||
"question": "What should be fixed here? Please explain what the address is"
|
||||
},
|
||||
"uk_addresses_explanation_osm": {
|
||||
"render": "This address is saved in OpenStreetMap"
|
||||
|
@ -1312,6 +1317,9 @@
|
|||
"question": "What street is this address located in?",
|
||||
"render": "This address is in street <b>{addr:street}</b>"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Known address"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -8,5 +8,16 @@
|
|||
},
|
||||
"ghostbikes": {
|
||||
"title": "Emlékkerékpárok"
|
||||
},
|
||||
"uk_addresses": {
|
||||
"layers": {
|
||||
"2": {
|
||||
"description": "Címek",
|
||||
"name": "Ismert címek az OSM-en",
|
||||
"title": {
|
||||
"render": "Ismert cím"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1060,6 +1060,8 @@
|
|||
"description": "Draag bij aan OpenStreetMap door adresinformatie in te vullen",
|
||||
"layers": {
|
||||
"2": {
|
||||
"description": "Adressen",
|
||||
"name": "Bekende adressen in OSM",
|
||||
"tagRenderings": {
|
||||
"uk_addresses_housenumber": {
|
||||
"mappings": {
|
||||
|
@ -1069,6 +1071,9 @@
|
|||
},
|
||||
"render": "Het huisnummer is <b>{addr:housenumber}</b>"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Bekend adres"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -530,5 +530,12 @@
|
|||
"description": "Нанесите все деревья на карту!",
|
||||
"shortDescription": "Карта деревьев",
|
||||
"title": "Деревья"
|
||||
},
|
||||
"uk_addresses": {
|
||||
"layers": {
|
||||
"2": {
|
||||
"description": "Адреса"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -655,6 +655,14 @@
|
|||
"shortDescription": "所有樹木的地圖",
|
||||
"title": "樹木"
|
||||
},
|
||||
"uk_addresses": {
|
||||
"layers": {
|
||||
"2": {
|
||||
"description": "地址",
|
||||
"name": "OSM 上已知的地址"
|
||||
}
|
||||
}
|
||||
},
|
||||
"waste_basket": {
|
||||
"description": "在這份地圖當中,你可以找到你附近的垃圾筒。如果地圖有遺漏垃圾筒,你可以自己加上去",
|
||||
"shortDescription": "垃圾筒的地圖",
|
||||
|
|
Loading…
Reference in a new issue