forked from MapComplete/MapComplete
Refactoring: add metatagging, add 'last edited by' element, add 'metacondition'
This commit is contained in:
parent
771783a31c
commit
105120060d
31 changed files with 217 additions and 142 deletions
Logic
Models
UI
BigComponents
Map
Popup
SpecialVisualization.tsSpecialVisualizations.tsi18n
assets
langs
scripts
test/Logic/OSM/Actions
|
@ -62,7 +62,11 @@ export default class DetermineLayout {
|
|||
layoutId,
|
||||
"The layout to load into MapComplete"
|
||||
).data
|
||||
return AllKnownLayouts.allKnownLayouts.get(layoutId?.toLowerCase())
|
||||
const layout = AllKnownLayouts.allKnownLayouts.get(layoutId?.toLowerCase())
|
||||
if (layout === undefined) {
|
||||
throw "No layout with name " + layoutId + " exists"
|
||||
}
|
||||
return layout
|
||||
}
|
||||
|
||||
public static LoadLayoutFromHash(userLayoutParam: UIEventSource<string>): LayoutConfig | null {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { UIEventSource } from "../../UIEventSource"
|
|||
*/
|
||||
export default class FeaturePropertiesStore {
|
||||
private readonly _source: FeatureSource & IndexedFeatureSource
|
||||
private readonly _elements = new Map<string, UIEventSource<any>>()
|
||||
private readonly _elements = new Map<string, UIEventSource<Record<string, string>>>()
|
||||
|
||||
constructor(source: FeatureSource & IndexedFeatureSource) {
|
||||
this._source = source
|
||||
|
@ -83,7 +83,9 @@ export default class FeaturePropertiesStore {
|
|||
return changeMade
|
||||
}
|
||||
|
||||
addAlias(oldId: string, newId: string): void {
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
public addAlias(oldId: string, newId: string): void {
|
||||
console.log("FeaturePropertiesStore: adding alias for", oldId, newId)
|
||||
if (newId === undefined) {
|
||||
// We removed the node/way/relation with type 'type' and id 'oldId' on openstreetmap!
|
||||
const element = this._elements.get(oldId)
|
||||
|
|
|
@ -94,8 +94,9 @@ export default class MetaTagging {
|
|||
let definedTags = new Set(Object.getOwnPropertyNames(feature.properties))
|
||||
for (const metatag of metatagsToApply) {
|
||||
try {
|
||||
if (!metatag.keys.some((key) => feature.properties[key] === undefined)) {
|
||||
if (!metatag.keys.some((key) => !(key in feature.properties))) {
|
||||
// All keys are already defined, we probably already ran this one
|
||||
// Note that we use 'key in properties', not 'properties[key] === undefined'. The latter will cause evaluation of lazy properties
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -96,16 +96,11 @@ export class ReferencingWaysMetaTagger extends SimpleMetaTagger {
|
|||
return false
|
||||
}
|
||||
|
||||
console.trace("Downloading referencing ways for", feature.properties.id)
|
||||
OsmObject.DownloadReferencingWays(id).then((referencingWays) => {
|
||||
const currentTagsSource = state.allElements?.getEventSourceById(id) ?? []
|
||||
Utils.AddLazyPropertyAsync(feature.properties, "_referencing_ways", async () => {
|
||||
const referencingWays = await OsmObject.DownloadReferencingWays(id)
|
||||
const wayIds = referencingWays.map((w) => "way/" + w.id)
|
||||
wayIds.sort()
|
||||
const wayIdsStr = wayIds.join(";")
|
||||
if (wayIdsStr !== "" && currentTagsSource.data["_referencing_ways"] !== wayIdsStr) {
|
||||
currentTagsSource.data["_referencing_ways"] = wayIdsStr
|
||||
currentTagsSource.ping()
|
||||
}
|
||||
return wayIds.join(";")
|
||||
})
|
||||
|
||||
return true
|
||||
|
@ -221,6 +216,7 @@ class RewriteMetaInfoTags extends SimpleMetaTagger {
|
|||
return movedSomething
|
||||
}
|
||||
}
|
||||
|
||||
export default class SimpleMetaTaggers {
|
||||
/**
|
||||
* A simple metatagger which rewrites various metatags as needed
|
||||
|
|
|
@ -575,12 +575,14 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
|||
}
|
||||
|
||||
export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
|
||||
constructor() {
|
||||
private readonly _desugaring: DesugaringContext
|
||||
constructor(desugaring: DesugaringContext) {
|
||||
super(
|
||||
"Add some editing elements, such as the delete button or the move button if they are configured. These used to be handled by the feature info box, but this has been replaced by special visualisation elements",
|
||||
[],
|
||||
"AddEditingElements"
|
||||
)
|
||||
this._desugaring = desugaring
|
||||
}
|
||||
|
||||
convert(
|
||||
|
@ -609,6 +611,30 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
|
|||
})
|
||||
}
|
||||
|
||||
if (json.deletion && !ValidationUtils.hasSpecialVisualisation(json, "all_tags")) {
|
||||
const trc: TagRenderingConfigJson = {
|
||||
id: "all-tags",
|
||||
render: { "*": "{all_tags()}" },
|
||||
metacondition: {
|
||||
or: [
|
||||
"__featureSwitchIsTesting=true",
|
||||
"__featureSwitchIsDebugging=true",
|
||||
"mapcomplete-show_debug=yes",
|
||||
],
|
||||
},
|
||||
}
|
||||
json.tagRenderings.push(trc)
|
||||
}
|
||||
|
||||
if (
|
||||
json.source !== "special" &&
|
||||
json.source !== "special:library" &&
|
||||
json.tagRenderings &&
|
||||
!json.tagRenderings.some((tr) => tr["id"] === "last_edit")
|
||||
) {
|
||||
json.tagRenderings.push(this._desugaring.tagRenderings.get("last_edit"))
|
||||
}
|
||||
|
||||
return { result: json }
|
||||
}
|
||||
}
|
||||
|
@ -1145,7 +1171,7 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
|
|||
new On("tagRenderings", new Each(new DetectInline())),
|
||||
new AddQuestionBox(),
|
||||
new AddMiniMap(state),
|
||||
new AddEditingElements(),
|
||||
new AddEditingElements(state),
|
||||
new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
|
||||
new On<(PointRenderingConfigJson | LineRenderingConfigJson)[], LayerConfigJson>(
|
||||
"mapRendering",
|
||||
|
|
|
@ -47,6 +47,10 @@ export default class TagRenderingConfig {
|
|||
public readonly question?: TypedTranslation<object>
|
||||
public readonly questionhint?: TypedTranslation<object>
|
||||
public readonly condition?: TagsFilter
|
||||
/**
|
||||
* Evaluated against the current 'usersettings'-state
|
||||
*/
|
||||
public readonly metacondition?: TagsFilter
|
||||
public readonly description?: Translation
|
||||
|
||||
public readonly configuration_warnings: string[] = []
|
||||
|
@ -70,14 +74,6 @@ export default class TagRenderingConfig {
|
|||
if (json === undefined) {
|
||||
throw "Initing a TagRenderingConfig with undefined in " + context
|
||||
}
|
||||
if (json === "questions") {
|
||||
// Very special value
|
||||
this.render = null
|
||||
this.question = null
|
||||
this.condition = null
|
||||
this.id = "questions"
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof json === "number") {
|
||||
json = "" + json
|
||||
|
@ -114,11 +110,15 @@ export default class TagRenderingConfig {
|
|||
}
|
||||
|
||||
this.labels = json.labels ?? []
|
||||
this.render = Translations.T(json.render, translationKey + ".render")
|
||||
this.render = Translations.T(<any>json.render, translationKey + ".render")
|
||||
this.question = Translations.T(json.question, translationKey + ".question")
|
||||
this.questionhint = Translations.T(json.questionHint, translationKey + ".questionHint")
|
||||
this.description = Translations.T(json.description, translationKey + ".description")
|
||||
this.condition = TagUtils.Tag(json.condition ?? { and: [] }, `${context}.condition`)
|
||||
this.metacondition = TagUtils.Tag(
|
||||
json.metacondition ?? { and: [] },
|
||||
`${context}.metacondition`
|
||||
)
|
||||
if (json.freeform) {
|
||||
if (
|
||||
json.freeform.addExtraTags !== undefined &&
|
||||
|
|
|
@ -205,6 +205,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
*/
|
||||
private miscSetup() {
|
||||
this.userRelatedState.markLayoutAsVisited(this.layout)
|
||||
|
||||
this.selectedElement.addCallbackAndRunD(() => {
|
||||
// As soon as we have a selected element, we clear it
|
||||
// This is to work around maplibre, which'll _first_ register the click on the map and only _then_ on the feature
|
||||
this.lastClickObject.features.setData([])
|
||||
})
|
||||
}
|
||||
|
||||
private initHotkeys() {
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
onDestroy(tags.addCallbackAndRun(tags => {
|
||||
_tags = tags;
|
||||
}));
|
||||
|
||||
let _metatags: Record<string, string>
|
||||
onDestroy(state.userRelatedState.preferencesAsTags .addCallbackAndRun(tags => {
|
||||
_metatags = tags;
|
||||
}));
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
@ -40,7 +45,7 @@
|
|||
|
||||
<div class="flex flex-col">
|
||||
{#each layer.tagRenderings as config (config.id)}
|
||||
{#if config.condition === undefined || config.condition.matchesProperties(_tags)}
|
||||
{#if (config.condition === undefined || config.condition.matchesProperties(_tags)) && (config.metacondition === undefined || config.metacondition.matchesProperties(_metatags))}
|
||||
{#if config.IsKnown(_tags)}
|
||||
<TagRenderingEditable {tags} {config} {state} {selectedElement} {layer} {highlightedRendering}></TagRenderingEditable>
|
||||
{/if}
|
||||
|
|
|
@ -91,6 +91,7 @@ export class MapLibreAdaptor implements MapProperties {
|
|||
// Workaround, 'ShowPointLayer' sets this flag
|
||||
return
|
||||
}
|
||||
console.log(e)
|
||||
const lon = e.lngLat.lng
|
||||
const lat = e.lngLat.lat
|
||||
lastClickLocation.setData({ lon, lat })
|
||||
|
|
|
@ -96,14 +96,15 @@
|
|||
}
|
||||
});
|
||||
state.newFeatures.features.ping();
|
||||
const tagsStore = state.featureProperties.getStore(newId);
|
||||
{
|
||||
// Set some metainfo
|
||||
const tagsStore = state.featureProperties.getStore(newId);
|
||||
const properties = tagsStore.data;
|
||||
if (snapTo) {
|
||||
// metatags (starting with underscore) are not uploaded, so we can safely mark this
|
||||
properties["_referencing_ways"] = `["${snapTo}"]`;
|
||||
}
|
||||
properties["_backend"] = state.osmConnection.Backend()
|
||||
properties["_last_edit:timestamp"] = new Date().toISOString();
|
||||
const userdetails = state.osmConnection.userDetails.data;
|
||||
properties["_last_edit:contributor"] = userdetails.name;
|
||||
|
@ -112,8 +113,9 @@
|
|||
}
|
||||
const feature = state.indexedFeatures.featuresById.data.get(newId);
|
||||
abort();
|
||||
state.selectedElement.setData(feature);
|
||||
state.selectedLayer.setData(selectedPreset.layer);
|
||||
state.selectedElement.setData(feature);
|
||||
tagsStore.ping()
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,46 +1,63 @@
|
|||
<script lang="ts">
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import Table from "../Base/Table"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||
import Table from "../Base/Table";
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import SimpleMetaTaggers from "../../Logic/SimpleMetaTagger";
|
||||
import { FixedUiElement } from "../Base/FixedUiElement";
|
||||
import { onDestroy } from "svelte";
|
||||
import Toggle, { ClickableToggle } from "../Input/Toggle";
|
||||
import Lazy from "../Base/Lazy";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
|
||||
//Svelte props
|
||||
export let tags: UIEventSource<any>
|
||||
export let state: any
|
||||
export let tags: UIEventSource<any>;
|
||||
export let state: any;
|
||||
|
||||
const calculatedTags = [].concat(
|
||||
// SimpleMetaTagger.lazyTags,
|
||||
...(state?.layoutToUse?.layers?.map((l) => l.calculatedTags?.map((c) => c[0]) ?? []) ?? [])
|
||||
)
|
||||
);
|
||||
|
||||
const allTags = tags.map((tags) => {
|
||||
const parts = []
|
||||
const parts: (string | BaseUIElement)[][] = [];
|
||||
for (const key in tags) {
|
||||
if (!tags.hasOwnProperty(key)) {
|
||||
continue
|
||||
}
|
||||
let v = tags[key]
|
||||
let v = tags[key];
|
||||
if (v === "") {
|
||||
v = "<b>empty string</b>"
|
||||
v = "<b>empty string</b>";
|
||||
}
|
||||
parts.push([key, v ?? "<b>undefined</b>"])
|
||||
parts.push([key, v ?? "<b>undefined</b>"]);
|
||||
}
|
||||
|
||||
for (const key of calculatedTags) {
|
||||
const value = tags[key]
|
||||
const value = tags[key];
|
||||
if (value === undefined) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
let type = ""
|
||||
let type = "";
|
||||
if (typeof value !== "string") {
|
||||
type = " <i>" + typeof value + "</i>"
|
||||
type = " <i>" + typeof value + "</i>";
|
||||
}
|
||||
parts.push(["<i>" + key + "</i>", value])
|
||||
parts.push(["<i>" + key + "</i>", value]);
|
||||
}
|
||||
|
||||
return parts
|
||||
})
|
||||
for (const metatag of SimpleMetaTaggers.metatags.filter(mt => mt.isLazy)) {
|
||||
const title = "<i>" + metatag.keys.join(";") + "</i> (lazy)";
|
||||
const toggleState = new UIEventSource(false)
|
||||
const toggle: BaseUIElement = new Toggle(
|
||||
new Lazy(() => new FixedUiElement(metatag.keys.map(key => tags[key]).join(";"))),
|
||||
new FixedUiElement("Evaluate").onClick(() => toggleState.setData(true)) ,
|
||||
toggleState
|
||||
);
|
||||
parts.push([title, toggle]);
|
||||
}
|
||||
|
||||
const tagsTable = new Table(["Key", "Value"], $allTags).SetClass("zebra-table")
|
||||
return parts;
|
||||
});
|
||||
|
||||
let _allTags = [];
|
||||
onDestroy(allTags.addCallbackAndRunD(allTags => {
|
||||
_allTags = allTags;
|
||||
}));
|
||||
const tagsTable = new Table(["Key", "Value"], _allTags).SetClass("zebra-table");
|
||||
</script>
|
||||
|
||||
<section>
|
||||
|
|
|
@ -99,63 +99,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
})
|
||||
),
|
||||
]
|
||||
allRenderings.push(
|
||||
new Toggle(
|
||||
new Lazy(() =>
|
||||
FeatureInfoBox.createEditElements(questionBoxes, layerConfig, tags, state)
|
||||
),
|
||||
undefined,
|
||||
state.featureSwitchUserbadge
|
||||
)
|
||||
)
|
||||
|
||||
return new Combine(allRenderings).SetClass("block")
|
||||
}
|
||||
|
||||
/**
|
||||
* All the edit elements, together (note that the question boxes are passed though)
|
||||
* @param questionBoxes
|
||||
* @param layerConfig
|
||||
* @param tags
|
||||
* @param state
|
||||
* @private
|
||||
*/
|
||||
private static createEditElements(
|
||||
questionBoxes: Map<string, QuestionBox>,
|
||||
layerConfig: LayerConfig,
|
||||
tags: UIEventSource<any>,
|
||||
state: FeaturePipelineState
|
||||
): BaseUIElement {
|
||||
let editElements: BaseUIElement[] = []
|
||||
questionBoxes.forEach((questionBox) => {
|
||||
editElements.push(questionBox)
|
||||
})
|
||||
|
||||
editElements.push(
|
||||
new VariableUiElement(
|
||||
state.osmConnection.userDetails
|
||||
.map((ud) => ud.csCount)
|
||||
.map(
|
||||
(csCount) => {
|
||||
if (
|
||||
csCount <= Constants.userJourney.historyLinkVisible &&
|
||||
state.featureSwitchIsDebugging.data == false &&
|
||||
state.featureSwitchIsTesting.data === false
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return new TagRenderingAnswer(
|
||||
tags,
|
||||
SharedTagRenderings.SharedTagRendering.get("last_edit"),
|
||||
state
|
||||
)
|
||||
},
|
||||
[state.featureSwitchIsDebugging, state.featureSwitchIsTesting]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return new Combine(editElements).SetClass("flex flex-col")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,10 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
const baseQuestions = (layer.tagRenderings ?? [])?.filter(tr => allowed(tr.labels) && tr.question !== undefined);
|
||||
let baseQuestions = []
|
||||
$: {
|
||||
baseQuestions = (layer.tagRenderings ?? [])?.filter(tr => allowed(tr.labels) && tr.question !== undefined);
|
||||
}
|
||||
let skippedQuestions = new UIEventSource<Set<string>>(new Set<string>());
|
||||
|
||||
let questionsToAsk = tags.map(tags => {
|
||||
|
@ -80,6 +83,7 @@
|
|||
skipped++;
|
||||
}
|
||||
}
|
||||
$: console.log("Current questionbox state:", {answered, skipped, questionsToAsk, layer, selectedElement, tags})
|
||||
</script>
|
||||
|
||||
{#if _questionsToAsk.length === 0}
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
checkedMappings = [...config.mappings.map(_ => false), false /*One element extra in case a freeform value is added*/];
|
||||
}
|
||||
}
|
||||
$: console.log("Checked mappings:", checkedMappings)
|
||||
let selectedTags: TagsFilter = undefined;
|
||||
|
||||
function mappingIsHidden(mapping: Mapping): boolean {
|
||||
|
|
|
@ -63,6 +63,7 @@ export interface SpecialVisualizationState {
|
|||
readonly userRelatedState: {
|
||||
readonly mangroveIdentity: MangroveIdentity
|
||||
readonly showAllQuestionsAtOnce: UIEventSource<boolean>
|
||||
readonly preferencesAsTags: Store<Record<string, string>>
|
||||
}
|
||||
readonly lastClickObject: WritableFeatureSource
|
||||
}
|
||||
|
|
|
@ -1265,21 +1265,24 @@ export default class SpecialVisualizations {
|
|||
doc: "The URL to link to",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "class",
|
||||
doc: "CSS-classes to add to the element",
|
||||
},
|
||||
],
|
||||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
args: string[]
|
||||
): BaseUIElement {
|
||||
const [text, href] = args
|
||||
const [text, href, classnames] = args
|
||||
return new VariableUiElement(
|
||||
tagSource.map(
|
||||
(tags) =>
|
||||
new Link(
|
||||
Utils.SubstituteKeys(text, tags),
|
||||
Utils.SubstituteKeys(href, tags),
|
||||
true
|
||||
)
|
||||
tagSource.map((tags) =>
|
||||
new Link(
|
||||
Utils.SubstituteKeys(text, tags),
|
||||
Utils.SubstituteKeys(href, tags),
|
||||
true
|
||||
).SetClass(classnames)
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
@ -29,7 +29,14 @@ export class Translation extends BaseUIElement {
|
|||
}
|
||||
count++
|
||||
if (typeof translations[translationsKey] != "string") {
|
||||
console.error("Non-string object in translation: ", translations[translationsKey])
|
||||
console.error(
|
||||
"Non-string object at",
|
||||
context,
|
||||
"in translation: ",
|
||||
translations[translationsKey],
|
||||
"\n current translations are: ",
|
||||
translations
|
||||
)
|
||||
throw (
|
||||
"Error in an object depicting a translation: a non-string object was found. (" +
|
||||
context +
|
||||
|
|
11
Utils.ts
11
Utils.ts
|
@ -307,13 +307,21 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
* @param init
|
||||
* @constructor
|
||||
*/
|
||||
public static AddLazyProperty(object: any, name: string, init: () => any) {
|
||||
public static AddLazyProperty(
|
||||
object: any,
|
||||
name: string,
|
||||
init: () => any,
|
||||
whenDone?: () => void
|
||||
) {
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
get: () => {
|
||||
delete object[name]
|
||||
object[name] = init()
|
||||
if (whenDone) {
|
||||
whenDone()
|
||||
}
|
||||
return object[name]
|
||||
},
|
||||
})
|
||||
|
@ -332,6 +340,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
enumerable: false,
|
||||
configurable: true,
|
||||
get: () => {
|
||||
console.trace("Property", name, "got requested")
|
||||
init().then((r) => {
|
||||
delete object[name]
|
||||
object[name] = r
|
||||
|
|
|
@ -19,4 +19,4 @@
|
|||
"https://commons.wikimedia.org/wiki/File:ISO_7010_P018.svg"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
|
@ -55,7 +55,16 @@
|
|||
"*": "{open_note()}"
|
||||
}
|
||||
},
|
||||
"all_tags"
|
||||
{
|
||||
"metacondition": {
|
||||
"or": [
|
||||
"__featureSwitchDebugging=true"
|
||||
]
|
||||
},
|
||||
"render": {
|
||||
"*": "{all_tags()}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"mapRendering": [
|
||||
{
|
||||
|
|
|
@ -1365,10 +1365,26 @@
|
|||
]
|
||||
},
|
||||
"last_edit": {
|
||||
"#": "Gives some metainfo about the last edit and who did edit it - rendering only",
|
||||
"description": "Gives some metainfo about the last edit and who did edit it - rendering only",
|
||||
"condition": "_last_edit:contributor~*",
|
||||
"metacondition": {
|
||||
"or": [
|
||||
"__featureSwitchIsTesting=true",
|
||||
"__featureSwitchIsDebugging=true",
|
||||
"mapcomplete-show_debug=yes",
|
||||
"_csCount>=10"
|
||||
]
|
||||
},
|
||||
"render": {
|
||||
"*": "<div class='subtle' style='font-size: small; margin-top: 2em; margin-bottom: 0.5em;'><a href='https://www.openStreetMap.org/changeset/{_last_edit:changeset}' target='_blank'>Last edited on {_last_edit:timestamp}</a> by <a href='https://www.openStreetMap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a></div>"
|
||||
"special": {
|
||||
"type": "link",
|
||||
"href": "{_backend}/changeset/{_last_edit:changeset}",
|
||||
"text": {
|
||||
"en": "Last edited on {_last_edit:timestamp} by {_last_edit:contributor}",
|
||||
"nl": "Laatst gewijzigd op {_last_edit:timestamp} door {_last_edit:contributor}"
|
||||
},
|
||||
"class": "subtle font-small"
|
||||
}
|
||||
}
|
||||
},
|
||||
"all_tags": {
|
||||
|
|
|
@ -3560,6 +3560,9 @@
|
|||
"19": {
|
||||
"then": "Aquí es poden reciclar sabates"
|
||||
},
|
||||
"20": {
|
||||
"then": "Aquí es poden reciclar petits aparells elèctrics"
|
||||
},
|
||||
"21": {
|
||||
"then": "Aquí es poden reciclar petits aparells elèctrics"
|
||||
},
|
||||
|
@ -4554,4 +4557,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6927,13 +6927,13 @@
|
|||
"16": {
|
||||
"question": "Recycling von Kunststoffen"
|
||||
},
|
||||
"18": {
|
||||
"17": {
|
||||
"question": "Recycling von Metallschrott"
|
||||
},
|
||||
"19": {
|
||||
"18": {
|
||||
"question": "Recycling von Elektrokleingeräten"
|
||||
},
|
||||
"20": {
|
||||
"19": {
|
||||
"question": "Recycling von Restabfällen"
|
||||
},
|
||||
"20": {
|
||||
|
|
|
@ -6946,15 +6946,12 @@
|
|||
"question": "Recycling of plastic"
|
||||
},
|
||||
"17": {
|
||||
"question": "Recycling of printer cartridges"
|
||||
},
|
||||
"18": {
|
||||
"question": "Recycling of scrap metal"
|
||||
},
|
||||
"19": {
|
||||
"18": {
|
||||
"question": "Recycling of small electrical appliances"
|
||||
},
|
||||
"20": {
|
||||
"19": {
|
||||
"question": "Recycling of residual waste"
|
||||
},
|
||||
"20": {
|
||||
|
@ -9182,4 +9179,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3448,7 +3448,7 @@
|
|||
"16": {
|
||||
"question": "Reciclaje de plástico"
|
||||
},
|
||||
"18": {
|
||||
"17": {
|
||||
"question": "Reciclaje de chatarra"
|
||||
},
|
||||
"18": {
|
||||
|
|
|
@ -1799,13 +1799,13 @@
|
|||
"16": {
|
||||
"question": "Riciclo di plastica"
|
||||
},
|
||||
"18": {
|
||||
"17": {
|
||||
"question": "Riciclo di rottami metallici"
|
||||
},
|
||||
"19": {
|
||||
"18": {
|
||||
"question": "Riciclo di piccoli elettrodomestici"
|
||||
},
|
||||
"20": {
|
||||
"19": {
|
||||
"question": "Riciclo di secco"
|
||||
},
|
||||
"20": {
|
||||
|
|
|
@ -6512,14 +6512,14 @@
|
|||
"question": "Recycling van plastic"
|
||||
},
|
||||
"17": {
|
||||
"question": "Recycling van printer cartridges"
|
||||
},
|
||||
"18": {
|
||||
"question": "Recycling van oud metaal"
|
||||
},
|
||||
"19": {
|
||||
"18": {
|
||||
"question": "Recycling van kleine elektrische apparaten"
|
||||
},
|
||||
"19": {
|
||||
"question": "Recycling van restafval"
|
||||
},
|
||||
"20": {
|
||||
"question": "Recycling van restafval"
|
||||
}
|
||||
|
@ -8652,4 +8652,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -131,6 +131,13 @@
|
|||
"question": "What is the network name for the wireless internet access?",
|
||||
"render": "The network name is <b>{internet_access:ssid}</b>"
|
||||
},
|
||||
"last_edit": {
|
||||
"render": {
|
||||
"special": {
|
||||
"text": "Last edited on {_last_edit:timestamp} by {_last_edit:contributor}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"level": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
|
|
|
@ -131,6 +131,13 @@
|
|||
"question": "Wat is de netwerknaam voor de draadloze internettoegang?",
|
||||
"render": "De netwerknaam is <b>{internet_access:ssid}</b>"
|
||||
},
|
||||
"last_edit": {
|
||||
"render": {
|
||||
"special": {
|
||||
"text": "Laatst gewijzigd op {_last_edit:timestamp} door {_last_edit:contributor} "
|
||||
}
|
||||
}
|
||||
},
|
||||
"level": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Translation } from "../UI/i18n/Translation"
|
|||
import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson"
|
||||
import questions from "../assets/tagRenderings/questions.json"
|
||||
import PointRenderingConfigJson from "../Models/ThemeConfig/Json/PointRenderingConfigJson"
|
||||
import { PrepareLayer } from "../Models/ThemeConfig/Conversion/PrepareLayer"
|
||||
import { PrepareLayer, RewriteSpecial } from "../Models/ThemeConfig/Conversion/PrepareLayer"
|
||||
import { PrepareTheme } from "../Models/ThemeConfig/Conversion/PrepareTheme"
|
||||
import { DesugaringContext } from "../Models/ThemeConfig/Conversion/Conversion"
|
||||
import { Utils } from "../Utils"
|
||||
|
@ -156,6 +156,7 @@ class LayerOverviewUtils extends Script {
|
|||
getSharedTagRenderings(doesImageExist: DoesImageExist): Map<string, TagRenderingConfigJson> {
|
||||
const dict = new Map<string, TagRenderingConfigJson>()
|
||||
|
||||
const prep = new RewriteSpecial()
|
||||
const validator = new ValidateTagRenderings(undefined, doesImageExist)
|
||||
for (const key in questions) {
|
||||
if (key === "id") {
|
||||
|
@ -163,7 +164,12 @@ class LayerOverviewUtils extends Script {
|
|||
}
|
||||
questions[key].id = key
|
||||
questions[key]["source"] = "shared-questions"
|
||||
const config = <TagRenderingConfigJson>questions[key]
|
||||
const config = prep.convertStrict(
|
||||
<TagRenderingConfigJson>questions[key],
|
||||
"questions.json:" + key
|
||||
)
|
||||
delete config.description
|
||||
delete config["#"]
|
||||
validator.convertStrict(
|
||||
config,
|
||||
"generate-layer-overview:tagRenderings/questions.json:" + key
|
||||
|
|
|
@ -883,7 +883,10 @@ describe("ReplaceGeometryAction", () => {
|
|||
const data = await Utils.downloadJson(url)
|
||||
const fullNodeDatabase = undefined // TODO new FullNodeDatabaseSource(undefined)
|
||||
// TODO fullNodeDatabase.handleOsmJson(data, 0)
|
||||
const changes = new Changes()
|
||||
const changes = new Changes({
|
||||
dryRun: new ImmutableStore(true),
|
||||
osmConnection: new OsmConnection()
|
||||
})
|
||||
const osmConnection = new OsmConnection({
|
||||
dryRun: new ImmutableStore(true),
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue