Merge branch 'develop' into feature/bicycle_road

This commit is contained in:
Robin van der Linde 2022-08-18 19:35:10 +02:00
commit 8e011d5b35
Signed by untrusted user: Robin-van-der-Linde
GPG key ID: 53956B3252478F0D
70 changed files with 1031 additions and 174 deletions

BIN
Docs/Misc/Climbing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
Docs/Misc/Kakampink.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

View file

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 274 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

View file

@ -0,0 +1,53 @@
# What do I want to tell at SOTM '22?
4 main topics:
The MapComplete Editor:
- What is MapComplete? The vision
+ Simple to use viewer
+ Simple to contribute
+ Packed with features under the hood (where applicable)
+ Pareto frontier
- What is a theme?
+ One or more layers + introduction text + some meta (such as icon)
+ Vision on what the target group is (who will use it? For what purpose?)
+ Written in a .json-file
- What is a .json-file
+ A flat text-file (create a .txt)
- What is a layer?
+ One logical type of feature
- Specifiy the tags that are needed to match
- Uses OSM as default datasource, but an online (sliced) geojson is an option too, e.g. to show/import
+ Specifies the elements to show in the popup
+ Specifies how it is rendered
+ Specifies if new elements can be added and with which tags
-> Don't mix, e.g. don't mix post-boxes with post offices
-> Don't use an adverb, e.g. "all shops ~~which accept cash~~". What if the shop is already there but the payment methods are not known yet? (use a filter instead)
- What is a TagRendering?
+ Converts the objects attributes into a text on the screen
+ If a question is added, it'll ask the user the right answer
-> Do use full sentences
-> Start with the step 'attributes to text', then add the question
-> Don't write "yes, this POI has ..." or even worse "yes"/"no"
- Deploying
+ copy paste into a base64-decoder
+ Go to "mapcomplete.osm.be/index?userlayout=true#<paste your encoded file here>"
+ Oh Noes! An error msg!
- sharing
+ Share often and early. I'd rather have a half-finished theme via chat/wiki that can be improved upon then no theme at all
+ When done: make a pull request via github (you'll get translations, documentation, integration with taginfo and a chatbot for free)
- A dash of Magic
+ Calculated tags
+ Twin layers (fritures)
+ Imports
- Future work:
+ Improve this flow

Binary file not shown.

View file

@ -235,7 +235,7 @@ export default class SimpleMetaTaggers {
private static canonicalize = new SimpleMetaTagger( private static canonicalize = new SimpleMetaTagger(
{ {
doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`)", doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`; `1` will be rewritten to `1m` as well)",
keys: ["Theme-defined keys"], keys: ["Theme-defined keys"],
}, },
@ -261,13 +261,14 @@ export default class SimpleMetaTaggers {
continue; continue;
} }
const value = feature.properties[key] const value = feature.properties[key]
const denom = unit.findDenomination(value) const denom = unit.findDenomination(value, () => feature.properties["_country"])
if (denom === undefined) { if (denom === undefined) {
// no valid value found // no valid value found
break; break;
} }
const [, denomination] = denom; const [, denomination] = denom;
let canonical = denomination?.canonicalValue(value) ?? undefined; const defaultDenom = unit.getDefaultDenomination(() => feature.properties["_country"])
let canonical = denomination?.canonicalValue(value, defaultDenom == denomination) ?? undefined;
if (canonical === value) { if (canonical === value) {
break; break;
} }

View file

@ -1,21 +1,22 @@
import {Translation} from "../UI/i18n/Translation"; import {Translation} from "../UI/i18n/Translation";
import {ApplicableUnitJson} from "./ThemeConfig/Json/UnitConfigJson"; import {DenominationConfigJson} from "./ThemeConfig/Json/UnitConfigJson";
import Translations from "../UI/i18n/Translations"; import Translations from "../UI/i18n/Translations";
import {Store, UIEventSource} from "../Logic/UIEventSource"; import {Store} from "../Logic/UIEventSource";
import BaseUIElement from "../UI/BaseUIElement"; import BaseUIElement from "../UI/BaseUIElement";
import Toggle from "../UI/Input/Toggle"; import Toggle from "../UI/Input/Toggle";
export class Denomination { export class Denomination {
public readonly canonical: string; public readonly canonical: string;
public readonly _canonicalSingular: string; public readonly _canonicalSingular: string;
public readonly default: boolean; public readonly useAsDefaultInput: boolean | string[]
public readonly useIfNoUnitGiven : boolean | string[]
public readonly prefix: boolean; public readonly prefix: boolean;
public readonly alternativeDenominations: string []; public readonly alternativeDenominations: string [];
private readonly _human: Translation; private readonly _human: Translation;
private readonly _humanSingular?: Translation; private readonly _humanSingular?: Translation;
constructor(json: ApplicableUnitJson, context: string) { constructor(json: DenominationConfigJson, context: string) {
context = `${context}.unit(${json.canonicalDenomination})` context = `${context}.unit(${json.canonicalDenomination})`
this.canonical = json.canonicalDenomination.trim() this.canonical = json.canonicalDenomination.trim()
if (this.canonical === undefined) { if (this.canonical === undefined) {
@ -32,7 +33,11 @@ export class Denomination {
this.alternativeDenominations = json.alternativeDenomination?.map(v => v.trim()) ?? [] this.alternativeDenominations = json.alternativeDenomination?.map(v => v.trim()) ?? []
this.default = json.default ?? false; if(json["default"] !== undefined) {
throw `${context} uses the old 'default'-key. Use "useIfNoUnitGiven" or "useAsDefaultInput" instead`
}
this.useIfNoUnitGiven = json.useIfNoUnitGiven
this.useAsDefaultInput = json.useAsDefaultInput ?? json.useIfNoUnitGiven
this._human = Translations.T(json.human, context + "human") this._human = Translations.T(json.human, context + "human")
this._humanSingular = Translations.T(json.humanSingular, context + "humanSingular") this._humanSingular = Translations.T(json.humanSingular, context + "humanSingular")
@ -68,32 +73,31 @@ export class Denomination {
* const unit = new Denomination({ * const unit = new Denomination({
* canonicalDenomination: "m", * canonicalDenomination: "m",
* alternativeDenomination: ["meter"], * alternativeDenomination: ["meter"],
* 'default': true,
* human: { * human: {
* en: "meter" * en: "meter"
* } * }
* }, "test") * }, "test")
* unit.canonicalValue("42m") // =>"42 m" * unit.canonicalValue("42m", true) // =>"42 m"
* unit.canonicalValue("42") // =>"42 m" * unit.canonicalValue("42", true) // =>"42 m"
* unit.canonicalValue("42 m") // =>"42 m" * unit.canonicalValue("42 m", true) // =>"42 m"
* unit.canonicalValue("42 meter") // =>"42 m" * unit.canonicalValue("42 meter", true) // =>"42 m"
* * unit.canonicalValue("42m", true) // =>"42 m"
* unit.canonicalValue("42", true) // =>"42 m"
* *
* // Should be trimmed if canonical is empty * // Should be trimmed if canonical is empty
* const unit = new Denomination({ * const unit = new Denomination({
* canonicalDenomination: "", * canonicalDenomination: "",
* alternativeDenomination: ["meter","m"], * alternativeDenomination: ["meter","m"],
* 'default': true,
* human: { * human: {
* en: "meter" * en: "meter"
* } * }
* }, "test") * }, "test")
* unit.canonicalValue("42m") // =>"42" * unit.canonicalValue("42m", true) // =>"42"
* unit.canonicalValue("42") // =>"42" * unit.canonicalValue("42", true) // =>"42"
* unit.canonicalValue("42 m") // =>"42" * unit.canonicalValue("42 m", true) // =>"42"
* unit.canonicalValue("42 meter") // =>"42" * unit.canonicalValue("42 meter", true) // =>"42"
*/ */
public canonicalValue(value: string, actAsDefault?: boolean) : string { public canonicalValue(value: string, actAsDefault: boolean) : string {
if (value === undefined) { if (value === undefined) {
return undefined; return undefined;
} }
@ -114,7 +118,7 @@ export class Denomination {
* *
* Returns null if it doesn't match this unit * Returns null if it doesn't match this unit
*/ */
public StrippedValue(value: string, actAsDefault?: boolean): string { public StrippedValue(value: string, actAsDefault: boolean): string {
if (value === undefined) { if (value === undefined) {
return undefined; return undefined;
@ -153,15 +157,26 @@ export class Denomination {
} }
} }
if (this.default || actAsDefault) { if (!actAsDefault) {
return null
}
const parsed = Number(value.trim()) const parsed = Number(value.trim())
if (!isNaN(parsed)) { if (!isNaN(parsed)) {
return value.trim(); return value.trim();
} }
}
return null; return null;
} }
isDefaultUnit(country: () => string) {
if(this.useIfNoUnitGiven === true){
return true
}
if(this.useIfNoUnitGiven === false){
return false
}
return this.useIfNoUnitGiven.indexOf(country()) >= 0
}
} }

View file

@ -13,11 +13,16 @@ import {AddContextToTranslations} from "./AddContextToTranslations";
class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> { class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> {
private readonly _state: DesugaringContext; private readonly _state: DesugaringContext;
private readonly _self: LayerConfigJson; private readonly _self: LayerConfigJson;
private readonly _options: {
/* If true, will copy the 'osmSource'-tags into the condition */
applyCondition?: true | boolean;
};
constructor(state: DesugaringContext, self: LayerConfigJson) { constructor(state: DesugaringContext, self: LayerConfigJson, options?: { applyCondition?: true | boolean;}) {
super("Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question", [], "ExpandTagRendering"); super("Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question", [], "ExpandTagRendering");
this._state = state; this._state = state;
this._self = self; this._self = self;
this._options = options;
} }
convert(json: string | TagRenderingConfigJson | { builtin: string | string[]; override: any }, context: string): { result: TagRenderingConfigJson[]; errors: string[]; warnings: string[] } { convert(json: string | TagRenderingConfigJson | { builtin: string | string[]; override: any }, context: string): { result: TagRenderingConfigJson[]; errors: string[]; warnings: string[] } {
@ -65,13 +70,15 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
const contextWriter = new AddContextToTranslations<TagRenderingConfigJson>("layers:") const contextWriter = new AddContextToTranslations<TagRenderingConfigJson>("layers:")
for (let i = 0; i < matchingTrs.length; i++) { for (let i = 0; i < matchingTrs.length; i++) {
// The matched tagRenderings are 'stolen' from another layer. This means that they must match the layer condition before being shown
let found: TagRenderingConfigJson = Utils.Clone(matchingTrs[i]); let found: TagRenderingConfigJson = Utils.Clone(matchingTrs[i]);
if(this._options?.applyCondition){
// The matched tagRenderings are 'stolen' from another layer. This means that they must match the layer condition before being shown
if (found.condition === undefined) { if (found.condition === undefined) {
found.condition = layer.source.osmTags found.condition = layer.source.osmTags
} else { } else {
found.condition = {and: [found.condition, layer.source.osmTags]} found.condition = {and: [found.condition, layer.source.osmTags]}
} }
}
found = contextWriter.convertStrict(found, layer.id + ".tagRenderings." + found["id"]) found = contextWriter.convertStrict(found, layer.id + ".tagRenderings." + found["id"])
matchingTrs[i] = found matchingTrs[i] = found
@ -561,7 +568,7 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
new On("tagRenderings", layer => new Concat(new ExpandTagRendering(state, layer))), new On("tagRenderings", layer => new Concat(new ExpandTagRendering(state, layer))),
new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
new On("mapRendering", layer => new Each(new On("icon", new FirstOf(new ExpandTagRendering(state, layer))))), new On("mapRendering", layer => new Each(new On("icon", new FirstOf(new ExpandTagRendering(state, layer, {applyCondition: false}))))),
new SetDefault("titleIcons", ["defaults"]), new SetDefault("titleIcons", ["defaults"]),
new On("titleIcons", layer => new Concat(new ExpandTagRendering(state, layer))) new On("titleIcons", layer => new Concat(new ExpandTagRendering(state, layer)))
); );

View file

@ -613,6 +613,13 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
information.push(...(r.information ?? [])) information.push(...(r.information ?? []))
} }
{
const hasCondition = json.mapRendering?.filter(mr => mr["icon"] !== undefined && mr["icon"]["condition"] !== undefined)
if(hasCondition?.length > 0){
errors.push("At "+context+":\n One or more icons in the mapRenderings have a condition set. Don't do this, as this will result in an invisible but clickable element. Use extra filters in the source instead. The offending mapRenderings are:\n"+JSON.stringify(hasCondition, null, " "))
}
}
if (json.presets !== undefined) { if (json.presets !== undefined) {
// Check that a preset will be picked up by the layer itself // Check that a preset will be picked up by the layer itself

View file

@ -14,13 +14,32 @@ export default interface UnitConfigJson {
/** /**
* The possible denominations * The possible denominations
*/ */
applicableUnits: ApplicableUnitJson[] applicableUnits: DenominationConfigJson[]
} }
export interface ApplicableUnitJson { export interface DenominationConfigJson {
/** /**
* The canonical value which will be added to the value in OSM. * If this evaluates to true and the value to interpret has _no_ unit given, assumes that this unit is meant.
* Alternatively, a list of country codes can be given where this acts as the default interpretation
*
* E.g., a denomination using "meter" would probably set this flag to "true";
* a denomination for "mp/h" will use the condition "_country=gb" to indicate that it is the default in the UK.
*
* If none of the units indicate that they are the default, the first denomination will be used instead
*/
useIfNoUnitGiven?: boolean | string[]
/**
* Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).
* If unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false
*/
useAsDefaultInput?: boolean | string[]
/**
* The canonical value for this denomination which will be added to the value in OSM.
* e.g. "m" for meters * e.g. "m" for meters
* If the user inputs '42', the canonical value will be added and it'll become '42m'. * If the user inputs '42', the canonical value will be added and it'll become '42m'.
* *
@ -28,8 +47,11 @@ export interface ApplicableUnitJson {
* In this case, an empty string should be used * In this case, an empty string should be used
*/ */
canonicalDenomination: string, canonicalDenomination: string,
/** /**
* The canonical denomination in the case that the unit is precisely '1' * The canonical denomination in the case that the unit is precisely '1'.
* Used for display purposes
*/ */
canonicalDenominationSingular?: string, canonicalDenominationSingular?: string,
@ -63,9 +85,5 @@ export interface ApplicableUnitJson {
*/ */
prefix?: boolean prefix?: boolean
/**
* The default interpretation - only one can be set.
* If none is set, the first unit will be considered the default interpretation of a value without a unit
*/
default?: boolean
} }

View file

@ -14,8 +14,6 @@ import List from "../../UI/Base/List";
import {MappingConfigJson, QuestionableTagRenderingConfigJson} from "./Json/QuestionableTagRenderingConfigJson"; import {MappingConfigJson, QuestionableTagRenderingConfigJson} from "./Json/QuestionableTagRenderingConfigJson";
import {FixedUiElement} from "../../UI/Base/FixedUiElement"; import {FixedUiElement} from "../../UI/Base/FixedUiElement";
import {Paragraph} from "../../UI/Base/Paragraph"; import {Paragraph} from "../../UI/Base/Paragraph";
import spec = Mocha.reporters.spec;
import SpecialVisualizations from "../../UI/SpecialVisualizations";
export interface Mapping { export interface Mapping {
readonly if: TagsFilter, readonly if: TagsFilter,

View file

@ -8,14 +8,11 @@ export class Unit {
public readonly appliesToKeys: Set<string>; public readonly appliesToKeys: Set<string>;
public readonly denominations: Denomination[]; public readonly denominations: Denomination[];
public readonly denominationsSorted: Denomination[]; public readonly denominationsSorted: Denomination[];
public readonly defaultDenom: Denomination;
public readonly eraseInvalid: boolean; public readonly eraseInvalid: boolean;
private readonly possiblePostFixes: string[] = []
constructor(appliesToKeys: string[], applicableUnits: Denomination[], eraseInvalid: boolean) { constructor(appliesToKeys: string[], applicableDenominations: Denomination[], eraseInvalid: boolean) {
this.appliesToKeys = new Set(appliesToKeys); this.appliesToKeys = new Set(appliesToKeys);
this.denominations = applicableUnits; this.denominations = applicableDenominations;
this.defaultDenom = applicableUnits.filter(denom => denom.default)[0]
this.eraseInvalid = eraseInvalid this.eraseInvalid = eraseInvalid
const seenUnitExtensions = new Set<string>(); const seenUnitExtensions = new Set<string>();
@ -52,8 +49,6 @@ export class Unit {
addPostfixesOf(denomination._canonicalSingular) addPostfixesOf(denomination._canonicalSingular)
denomination.alternativeDenominations.forEach(addPostfixesOf) denomination.alternativeDenominations.forEach(addPostfixesOf)
} }
this.possiblePostFixes = Array.from(possiblePostFixes)
this.possiblePostFixes.sort((a, b) => b.length - a.length)
} }
@ -71,16 +66,12 @@ export class Unit {
} }
// Some keys do have unit handling // Some keys do have unit handling
const defaultSet = json.applicableUnits.filter(u => u.default === true) if(json.applicableUnits.some(denom => denom.useAsDefaultInput !== undefined)){
// No default is defined - we pick the first as default json.applicableUnits.forEach(denom => {
if (defaultSet.length === 0) { denom.useAsDefaultInput = denom.useAsDefaultInput ?? false
json.applicableUnits[0].default = true })
} }
// Check that there are not multiple defaults
if (defaultSet.length > 1) {
throw `Multiple units are set as default: they have canonical values of ${defaultSet.map(u => u.canonicalDenomination).join(", ")}`
}
const applicable = json.applicableUnits.map((u, i) => new Denomination(u, `${ctx}.units[${i}]`)) const applicable = json.applicableUnits.map((u, i) => new Denomination(u, `${ctx}.units[${i}]`))
return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false) return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false)
} }
@ -96,12 +87,13 @@ export class Unit {
/** /**
* Finds which denomination is applicable and gives the stripped value back * Finds which denomination is applicable and gives the stripped value back
*/ */
findDenomination(valueWithDenom: string): [string, Denomination] { findDenomination(valueWithDenom: string, country: () => string): [string, Denomination] {
if (valueWithDenom === undefined) { if (valueWithDenom === undefined) {
return undefined; return undefined;
} }
const defaultDenom = this.getDefaultDenomination(country)
for (const denomination of this.denominationsSorted) { for (const denomination of this.denominationsSorted) {
const bare = denomination.StrippedValue(valueWithDenom) const bare = denomination.StrippedValue(valueWithDenom, defaultDenom === denomination)
if (bare !== null) { if (bare !== null) {
return [bare, denomination] return [bare, denomination]
} }
@ -109,11 +101,11 @@ export class Unit {
return [undefined, undefined] return [undefined, undefined]
} }
asHumanLongValue(value: string): BaseUIElement { asHumanLongValue(value: string, country: () => string): BaseUIElement {
if (value === undefined) { if (value === undefined) {
return undefined; return undefined;
} }
const [stripped, denom] = this.findDenomination(value) const [stripped, denom] = this.findDenomination(value, country)
const human = stripped === "1" ? denom?.humanSingular : denom?.human const human = stripped === "1" ? denom?.humanSingular : denom?.human
if (human === undefined) { if (human === undefined) {
return new FixedUiElement(stripped ?? value); return new FixedUiElement(stripped ?? value);
@ -124,24 +116,46 @@ export class Unit {
} }
/**
* Returns the value without any (sub)parts of any denomination - usefull as preprocessing step for validating inputs. public getDefaultInput(country: () => string | string[]) {
* E.g. console.log("Searching the default denomination for input", country)
* if 'megawatt' is a possible denomination, then '5 Meg' will be rewritten to '5' (which can then be validated as a valid pnat) for (const denomination of this.denominations) {
* if (denomination.useAsDefaultInput === true) {
* Returns the original string if nothign matches return denomination
*/ }
stripUnitParts(str: string) { if (denomination.useAsDefaultInput === undefined || denomination.useAsDefaultInput === false) {
if (str === undefined) { continue
return undefined; }
let countries: string | string[] = country()
if (typeof countries === "string") {
countries = countries.split(",")
}
const denominationCountries: string[] = denomination.useAsDefaultInput
if (countries.some(country => denominationCountries.indexOf(country) >= 0)) {
return denomination
}
}
return this.denominations[0]
} }
for (const denominationPart of this.possiblePostFixes) { public getDefaultDenomination(country: () => string){
if (str.endsWith(denominationPart)) { for (const denomination of this.denominations) {
return str.substring(0, str.length - denominationPart.length).trim() if (denomination.useIfNoUnitGiven === true || denomination.canonical === "") {
return denomination
} }
if (denomination.useIfNoUnitGiven === undefined || denomination.useIfNoUnitGiven === false) {
continue
}
let countries: string | string[] = country()
if (typeof countries === "string") {
countries = countries.split(",")
}
const denominationCountries: string[] = denomination.useIfNoUnitGiven
if (countries.some(country => denominationCountries.indexOf(country) >= 0)) {
return denomination
}
}
return this.denominations[0]
} }
return str;
}
} }

View file

@ -1,6 +1,6 @@
import BaseUIElement from "../BaseUIElement"; import BaseUIElement from "../BaseUIElement";
import {Chart, ChartConfiguration, ChartType, DefaultDataPoint, registerables} from 'chart.js'; import {Chart, ChartConfiguration, ChartType, DefaultDataPoint, registerables} from 'chart.js';
Chart.register(...registerables); Chart?.register(...(registerables ?? []));
export default class ChartJs< export default class ChartJs<

View file

@ -78,7 +78,6 @@ export class SubtleButton extends UIElement {
}) })
const loading = new Lazy(() => new Loading(loadingText) ) const loading = new Lazy(() => new Loading(loadingText) )
return new VariableUiElement(state.map(st => { return new VariableUiElement(state.map(st => {
console.log("State is: ", st)
if(st === "idle"){ if(st === "idle"){
return button return button
} }

View file

@ -20,6 +20,7 @@ import {QueryParameters} from "../../Logic/Web/QueryParameters";
import {TagUtils} from "../../Logic/Tags/TagUtils"; import {TagUtils} from "../../Logic/Tags/TagUtils";
import {InputElement} from "../Input/InputElement"; import {InputElement} from "../Input/InputElement";
import {DropDown} from "../Input/DropDown"; import {DropDown} from "../Input/DropDown";
import {FixedUiElement} from "../Base/FixedUiElement";
export default class FilterView extends VariableUiElement { export default class FilterView extends VariableUiElement {
constructor(filteredLayer: UIEventSource<FilteredLayer[]>, constructor(filteredLayer: UIEventSource<FilteredLayer[]>,
@ -91,7 +92,7 @@ export default class FilterView extends VariableUiElement {
if (filteredLayer.layerDef.name === undefined) { if (filteredLayer.layerDef.name === undefined) {
// Name is not defined: we hide this one // Name is not defined: we hide this one
return new Toggle( return new Toggle(
filteredLayer?.layerDef?.description?.Clone()?.SetClass("subtle") , new FixedUiElement(filteredLayer?.layerDef?.id ).SetClass("block") ,
undefined, undefined,
state?.featureSwitchIsDebugging state?.featureSwitchIsDebugging
); );

View file

@ -90,7 +90,7 @@ export class TextFieldDef {
if (options.unit !== undefined) { if (options.unit !== undefined) {
// Reformatting is handled by the unit in this case // Reformatting is handled by the unit in this case
options["isValid"] = str => { options["isValid"] = str => {
const denom = options.unit.findDenomination(str); const denom = options.unit.findDenomination(str, options?.country);
if (denom === undefined) { if (denom === undefined) {
return false; return false;
} }
@ -153,7 +153,7 @@ export class TextFieldDef {
} }
}) })
) )
unitDropDown.GetValue().setData(unit.defaultDenom) unitDropDown.GetValue().setData(unit.getDefaultInput(options.country))
unitDropDown.SetClass("w-min") unitDropDown.SetClass("w-min")
const fixedDenom = unit.denominations.length === 1 ? unit.denominations[0] : undefined const fixedDenom = unit.denominations.length === 1 ? unit.denominations[0] : undefined
@ -169,7 +169,7 @@ export class TextFieldDef {
}, },
(valueWithDenom: string) => { (valueWithDenom: string) => {
// Take the value from OSM and feed it into the textfield and the dropdown // Take the value from OSM and feed it into the textfield and the dropdown
const withDenom = unit.findDenomination(valueWithDenom); const withDenom = unit.findDenomination(valueWithDenom, options?.country);
if (withDenom === undefined) { if (withDenom === undefined) {
// Not a valid value at all - we give it undefined and leave the details up to the other elements (but we keep the previous denomination) // Not a valid value at all - we give it undefined and leave the details up to the other elements (but we keep the previous denomination)
return [undefined, fixedDenom] return [undefined, fixedDenom]

View file

@ -617,6 +617,7 @@ export default class TagRenderingQuestion extends Combine {
const tagsData = tags.data; const tagsData = tags.data;
const feature = state?.allElements?.ContainingFeatures?.get(tagsData.id) const feature = state?.allElements?.ContainingFeatures?.get(tagsData.id)
const center = feature != undefined ? GeoOperations.centerpointCoordinates(feature) : [0, 0] const center = feature != undefined ? GeoOperations.centerpointCoordinates(feature) : [0, 0]
console.log("Creating a tr-question with applicableUnit", applicableUnit)
const input: InputElement<string> = ValidatedTextField.ForType(configuration.freeform.type)?.ConstructInputElement({ const input: InputElement<string> = ValidatedTextField.ForType(configuration.freeform.type)?.ConstructInputElement({
country: () => tagsData._country, country: () => tagsData._country,
location: [center[1], center[0]], location: [center[1], center[0]],

View file

@ -1273,6 +1273,9 @@ export default class SpecialVisualizations {
const tagRendering = layer.tagRenderings.find(tr => tr.id === tagRenderingId) const tagRendering = layer.tagRenderings.find(tr => tr.id === tagRenderingId)
tagRenderings.push([layer, tagRendering]) tagRenderings.push([layer, tagRendering])
} }
if(tagRenderings.length === 0){
throw "Could not create stolen tagrenddering: tagRenderings not found"
}
return new VariableUiElement(featureTags.map(tags => { return new VariableUiElement(featureTags.map(tags => {
const featureId = tags[featureIdKey] const featureId = tags[featureIdKey]
if (featureId === undefined) { if (featureId === undefined) {

View file

@ -151,8 +151,8 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
return res; return res;
} }
public static NoNull<T>(array: T[]): T[] { public static NoNull<T>(array: T[]): NonNullable<T>[] {
return array?.filter(o => o !== undefined && o !== null) return <any> array?.filter(o => o !== undefined && o !== null)
} }
public static Hist(array: string[]): Map<string, number> { public static Hist(array: string[]): Map<string, number> {

View file

@ -271,6 +271,9 @@
"smoking", "smoking",
"service:electricity", "service:electricity",
"dog-access", "dog-access",
"internet",
"internet-fee",
"internet-ssid",
"reviews" "reviews"
], ],
"filter": [ "filter": [

View file

@ -185,6 +185,7 @@
"alternativeDenomination": [ "alternativeDenomination": [
"meter" "meter"
], ],
"useIfNoUnitGiven": true,
"human": { "human": {
"en": "meter", "en": "meter",
"fr": "mètre", "fr": "mètre",
@ -193,7 +194,7 @@
} }
}, },
{ {
"default": true, "useAsDefaultInput": true,
"canonicalDenomination": "cm", "canonicalDenomination": "cm",
"alternativeDenomination": [ "alternativeDenomination": [
"centimeter", "centimeter",

View file

@ -467,10 +467,12 @@
"units": [ "units": [
{ {
"appliesToKey": [ "appliesToKey": [
"kerb:height" "kerb:height",
"width"
], ],
"applicableUnits": [ "applicableUnits": [
{ {
"useIfNoUnitGiven": true,
"canonicalDenomination": "m", "canonicalDenomination": "m",
"alternativeDenomination": [ "alternativeDenomination": [
"meter" "meter"
@ -483,7 +485,7 @@
} }
}, },
{ {
"default": true, "useAsDefaultInput": true,
"canonicalDenomination": "cm", "canonicalDenomination": "cm",
"alternativeDenomination": [ "alternativeDenomination": [
"centimeter", "centimeter",

View file

@ -764,6 +764,9 @@
}, },
"service:electricity", "service:electricity",
"dog-access", "dog-access",
"internet",
"internet-fee",
"internet-ssid",
"reviews" "reviews"
], ],
"filter": [ "filter": [

View file

@ -80,7 +80,10 @@
"phone", "phone",
"email", "email",
"website", "website",
"wheelchair-access" "wheelchair-access",
"internet",
"internet-fee",
"internet-ssid"
], ],
"allowMove": { "allowMove": {
"enableImproveAccuracy": true, "enableImproveAccuracy": true,

View file

@ -1,3 +1,45 @@
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14"> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<path d="M 0.5 3 C 0.223 3 0 3.223 0 3.5 L 0 4 L 0 8 L 0 9 L 0 9.5 L 0 11 L 1 11 L 1 9.5 L 1 9 L 13 9 L 13 10.5 L 13 11 L 13.5 11 L 14 11 L 14 10.5 L 14 9 L 14 8 L 14 6 L 14 5.5 C 14 5.223 13.777 5 13.5 5 C 13.223 5 13 5.223 13 5.5 L 13 6 L 13 8 L 1 8 L 1 4 L 1 3.5 C 1 3.223 0.777 3 0.5 3 z M 3.5 4 C 2.671573 4 2 4.671573 2 5.5 C 2 6.328427 2.671573 7 3.5 7 C 4.328427 7 5 6.328427 5 5.5 C 5 4.671573 4.328427 4 3.5 4 z M 6 5 L 6 7 L 12 7 C 12 6 10.963825 5 10 5 L 6 5 z"/> <svg
width="500"
height="500"
viewBox="0 0 500 500"
version="1.1"
id="svg4"
sodipodi:docname="hotel.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="0.5912918"
inkscape:cx="43.125915"
inkscape:cy="341.6249"
inkscape:window-width="1920"
inkscape:window-height="1007"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<sodipodi:guide
position="-213.39489,250.02313"
orientation="0,-1"
id="guide827" />
</sodipodi:namedview>
<path
d="m 20.793355,103.8662 c -9.838754,0 -17.7594848,7.92073 -17.7594848,17.75948 v 17.75948 142.07589 35.51898 17.75949 53.27845 H 38.552842 V 334.73952 316.98003 H 464.78049 v 53.27845 17.75949 h 17.7595 17.75948 v -17.75949 -53.27845 -35.51898 -71.03793 -17.75949 c 0,-9.83876 -7.92073,-17.75949 -17.75948,-17.75949 -9.83876,0 -17.7595,7.92073 -17.7595,17.75949 v 17.75949 71.03793 H 38.552842 V 139.38516 121.62568 c 0,-9.83875 -7.920731,-17.75948 -17.759487,-17.75948 z m 106.556915,35.51896 c -29.424876,0 -53.278458,23.85359 -53.278458,53.27847 0,29.42487 23.853582,53.27845 53.278458,53.27845 29.42487,0 53.27845,-23.85358 53.27845,-53.27845 0,-29.42488 -23.85358,-53.27847 -53.27845,-53.27847 z m 88.79743,35.51898 v 71.03794 h 213.11382 c 0,-35.51896 -36.80387,-71.03794 -71.03793,-71.03794 z"
id="path2"
style="stroke-width:35.519" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

View file

@ -456,7 +456,6 @@
{ {
"applicableUnits": [ "applicableUnits": [
{ {
"default": true,
"canonicalDenomination": "", "canonicalDenomination": "",
"alternativeDenomination": [ "alternativeDenomination": [
"mm", "mm",

View file

@ -178,8 +178,6 @@
"centroid" "centroid"
], ],
"icon": { "icon": {
"render": null,
"condition": "indoor=room",
"mappings": [ "mappings": [
{ {
"if": { "if": {

View file

@ -339,8 +339,7 @@
"nl": "centimeter", "nl": "centimeter",
"de": "Zentimeter", "de": "Zentimeter",
"fr": "centimètre" "fr": "centimètre"
}, }
"default": true
}, },
{ {
"canonicalDenomination": "m", "canonicalDenomination": "m",

View file

@ -110,16 +110,19 @@
"allowSplit": true, "allowSplit": true,
"mapRendering": [ "mapRendering": [
{ {
"render": null,
"icon": { "icon": {
"render": null,
"mappings": [ "mappings": [
{ {
"if": "maxspeed~[1-9]0|1[0-4]0", "if": "maxspeed~[1-9]0|1[0-4]0",
"then": "./assets/themes/maxspeed/maxspeed_{maxspeed} mph.svg"
},
{
"if": "maxspeed~[1-9]0|1[0-4]0 mph",
"then": "./assets/themes/maxspeed/maxspeed_{maxspeed}.svg" "then": "./assets/themes/maxspeed/maxspeed_{maxspeed}.svg"
} }
] ]
}, },
"condition": "maxspeed!=30",
"iconSize": { "iconSize": {
"render": "32,32,center" "render": "32,32,center"
}, },
@ -154,7 +157,6 @@
"kmh", "kmh",
"kph" "kph"
], ],
"default": true,
"human": { "human": {
"en": "kilometers/hour", "en": "kilometers/hour",
"ca": "quilòmetres/hora", "ca": "quilòmetres/hora",
@ -172,6 +174,7 @@
}, },
{ {
"canonicalDenomination": "mph", "canonicalDenomination": "mph",
"useIfNoUnitGiven": ["gb","us"],
"alternativeDenomination": [ "alternativeDenomination": [
"m/u", "m/u",
"mh", "mh",

View file

@ -164,6 +164,15 @@
"fr": "Il n'y a pas de places de stationnement pour personnes à mobilité réduite" "fr": "Il n'y a pas de places de stationnement pour personnes à mobilité réduite"
}, },
"hideInAnswer": true "hideInAnswer": true
},
{
"if": "capacity:disabled=0",
"then": {
"en": "There are no disabled parking spots",
"nl": "Er zijn geen parkeerplaatsen voor gehandicapten",
"de": "Es gibt keine barrierefreien Stellplätze",
"fr": "Il n'y a pas de places de stationnement pour personnes à mobilité réduite"
}
} }
], ],
"question": { "question": {
@ -247,9 +256,9 @@
"iconBadges": [ "iconBadges": [
{ {
"if": { "if": {
"and": [ "or": [
"capacity:disabled~*", "capacity:disabled>0",
"capacity:disabled!=no" "capacity:disabled=yes"
] ]
}, },
"then": "circle:white;./assets/layers/toilet/wheelchair.svg" "then": "circle:white;./assets/layers/toilet/wheelchair.svg"

View file

@ -0,0 +1,10 @@
[
{
"path": "parking_space.svg",
"license": "CC0",
"authors": [
"Robin van der Linde"
],
"sources": []
}
]

View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="500"
height="500"
viewBox="0 0 132.29166 132.29167"
version="1.1"
id="svg8"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
sodipodi:docname="Parking_space.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="105.28861"
inkscape:cy="436.23499"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2066"
inkscape:window-x="2149"
inkscape:window-y="-11"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Laag 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#1d5485;stroke-width:5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 26.458333,121.70833 0,-111.124997 h 79.374997 l 0,111.124997"
id="path833"
sodipodi:nodetypes="cccc" />
<g
aria-label="P"
id="text833"
style="font-style:normal;font-weight:normal;font-size:68.771px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.71928">
<path
d="m 82.499093,56.25664 q 0,3.32438 -1.175286,6.178645 -1.141706,2.820686 -3.22364,4.90262 -2.585629,2.585629 -6.111486,3.895233 -3.525857,1.276025 -8.898592,1.276025 H 56.44133 V 91.145836 H 49.792571 V 41.145824 h 13.566155 q 4.499665,0 7.622567,0.772331 3.122902,0.738751 5.540633,2.350571 2.854265,1.914037 4.398926,4.768302 1.578241,2.854265 1.578241,7.219612 z m -6.917396,0.167898 q 0,-2.585628 -0.906649,-4.499665 -0.906649,-1.914037 -2.753526,-3.122902 -1.611821,-1.040968 -3.693755,-1.477502 -2.048355,-0.470115 -5.204837,-0.470115 h -6.5816 v 19.979858 h 5.607792 q 4.029551,0 6.54802,-0.705172 2.51847,-0.738751 4.09671,-2.316992 1.578241,-1.61182 2.216253,-3.391538 0.671592,-1.779719 0.671592,-3.995972 z"
style="stroke-width:1.71928"
id="path835" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,155 @@
{
"id": "parking_spaces",
"name": {
"en": "Parking Spaces"
},
"description": {
"en": "Layer showing individual parking spaces."
},
"minzoom": 20,
"source": {
"osmTags": "amenity=parking_space"
},
"tagRenderings": [
{
"id": "type",
"question": {
"en": "What kind of parking space is this?"
},
"mappings": [
{
"if": "parking_space=",
"then": {
"en": "This is a normal parking space"
},
"hideInAnswer": true
},
{
"if": "parking_space=normal",
"then": {
"en": "This is a normal parking space."
}
},
{
"if": "parking_space=disabled",
"then": {
"en": "This is a disabled parking space."
}
},
{
"if": "parking_space=private",
"then": {
"en": "This is a private parking space."
}
},
{
"if": "parking_space=charging",
"then": {
"en": "This is parking space reserved for charging vehicles."
}
},
{
"if": "parking_space=delivery",
"then": {
"en": "This is parking space reserved for deliveries."
}
},
{
"if": "parking_space=hgv",
"then": {
"en": "This is parking space reserved for heavy goods vehicles."
}
},
{
"if": "parking_space=caravan",
"then": {
"en": "This is parking space reserved for caravans or RVs."
}
},
{
"if": "parking_space=bus",
"then": {
"en": "This is parking space reserved for buses."
}
},
{
"if": "parking_space=motorcycle",
"then": {
"en": "This is parking space reserved for motorcycles."
}
},
{
"if": "parking_space=parent",
"then": {
"en": "This is a parking space reserved for parents with children."
}
},
{
"if": "parking_space=staff",
"then": {
"en": "This is a parking space reserved for staff."
}
},
{
"if": "parking_space=taxi",
"then": {
"en": "This is a parking space reserved for taxis."
}
},
{
"if": "parking_space=trailer",
"then": {
"en": "This is a parking space reserved for vehicles towing a trailer."
}
},
{
"if": "parking_space=car_sharing",
"then": {
"en": "This is a parking space reserved for car sharing."
}
}
]
},
{
"id": "capacity",
"render": {
"en": "This parking spaces has {capacity} spaces."
},
"mappings": [
{
"if": "capacity=1",
"then": {
"en": "This parking space has 1 space."
}
}
]
}
],
"title": {
"render": {
"en": "Parking Space"
}
},
"mapRendering": [
{
"icon": {
"render": "./assets/layers/parking_spaces/parking_space.svg",
"mappings": [
{
"if": "parking_space=disabled",
"then": "./assets/layers/toilet/wheelchair.svg"
}
]
},
"iconSize": "20,20,center",
"location": [
"point",
"centroid"
]
},
{
"color": "#696969",
"width": "1"
}
]
}

View file

@ -201,6 +201,9 @@
} }
] ]
}, },
"internet",
"internet-fee",
"internet-ssid",
"questions", "questions",
"reviews" "reviews"
], ],

View file

@ -264,7 +264,7 @@
}, },
"mappings": [ "mappings": [
{ {
"if": "access=public", "if": "access=yes",
"then": { "then": {
"nl": "Publiek toegankelijk", "nl": "Publiek toegankelijk",
"fr": "Accessible au public", "fr": "Accessible au public",
@ -309,6 +309,19 @@
"de": "Der Sportplatz ist nicht öffentlich zugänglich (es ist ein privater Sportplatz)", "de": "Der Sportplatz ist nicht öffentlich zugänglich (es ist ein privater Sportplatz)",
"es": "Privada - no accesible al público" "es": "Privada - no accesible al público"
} }
},
{
"if": "access=public",
"then": {
"nl": "Publiek toegankelijk",
"fr": "Accessible au public",
"en": "Public access",
"it": "Aperto al pubblico",
"ru": "Свободный доступ",
"de": "Der Sportplatz ist öffentlich zugänglich",
"es": "Acceso público"
},
"hideInAnswer": true
} }
] ]
}, },

View file

@ -63,6 +63,7 @@
], ],
"applicableUnits": [ "applicableUnits": [
{ {
"useIfNoUnitGiven": true,
"canonicalDenomination": "m", "canonicalDenomination": "m",
"alternativeDenomination": [ "alternativeDenomination": [
"meter" "meter"
@ -74,7 +75,7 @@
} }
}, },
{ {
"default": true, "useAsDefaultInput": true,
"canonicalDenomination": "cm", "canonicalDenomination": "cm",
"alternativeDenomination": [ "alternativeDenomination": [
"centimeter", "centimeter",

View file

@ -1148,5 +1148,110 @@
} }
} }
] ]
},
"internet": {
"question": {
"en": "Does this place offer internet access?",
"nl": "Biedt deze plaats internettoegang aan?"
},
"mappings": [
{
"if": "internet_access=wlan",
"then": {
"en": "This place offers wireless internet access",
"nl": "Deze plaats biedt draadloze internettoegang aan"
}
},
{
"if": "internet_access=no",
"then": {
"en": "This place <b>does not</b> offer internet access",
"nl": "Deze plaats biedt <b>geen</b> internettoegang aan"
}
},
{
"if": "internet_access=yes",
"then": {
"en": "This place offers internet access",
"nl": "Deze plaats biedt internettoegang aan"
},
"hideInAnswer": true
},
{
"if": "internet_access=terminal",
"then": {
"en": "This place offers internet access via a terminal or computer",
"nl": "Deze plaats biedt internettoegang via een terminal of computer aan"
}
},
{
"if": "internet_access=wired",
"then": {
"en": "This place offers wired internet access",
"nl": "Deze plaats biedt bedrade internettoegang aan"
}
}
]
},
"internet-fee": {
"condition": {
"and": [
"internet_access!=no",
"internet_access!="
]
},
"question": {
"en": "Is there a fee for internet access?",
"nl": "Zijn er kosten voor internettoegang?"
},
"mappings": [
{
"if": "internet_access:fee=yes",
"then": {
"en": "There is a fee for the internet access at this place",
"nl": "Er zijn kosten voor internettoegang op deze plaats"
}
},
{
"if": "internet_access:fee=no",
"then": {
"en": "Internet access is free at this place",
"nl": "Internettoegang is gratis op deze plaats"
}
},
{
"if": "internet_access:fee=customers",
"then": {
"en": "Internet access is free at this place, for customers only",
"nl": "Internettoegang is gratis op deze plaats, alleen voor klanten"
}
}
]
},
"internet-ssid": {
"condition": "internet_access=wlan",
"question": {
"en": "What is the network name for the wireless internet access?",
"nl": "Wat is de netwerknaam voor de draadloze internettoegang?"
},
"freeform": {
"key": "internet_access:ssid",
"type": "string",
"placeholder": {
"en": "Enter the network name",
"nl": "Voer de netwerknaam in"
}
},
"mappings": [
{
"if": "internet_access:ssid=Telekom",
"then": "Telekom",
"hideInAnswer": "_country!=de"
}
],
"render": {
"en": "The network name is <b>{internet_access:ssid}</b>",
"nl": "De netwerknaam is <b>{internet_access:ssid}</b>"
}
} }
} }

View file

@ -132,8 +132,7 @@
"ca": " metre", "ca": " metre",
"nb_NO": " meter", "nb_NO": " meter",
"es": " metro" "es": " metro"
}, }
"default": true
}, },
{ {
"canonicalDenomination": "ft", "canonicalDenomination": "ft",

View file

@ -0,0 +1,18 @@
{
"id": "hotels",
"title": {
"en": "Hotels"
},
"description": {
"en": "On this map, you'll find hotels in your area"
},
"maintainer": "MapComplete",
"version": "0.0.1",
"icon": "./assets/layers/hotel/hotel.svg",
"startLat": 50.8552,
"startLon": 4.3755,
"startZoom": 13,
"layers": [
"hotel"
]
}

View file

@ -207,6 +207,10 @@
"if": "theme=healthcare", "if": "theme=healthcare",
"then": "./assets/layers/doctors/doctors.svg" "then": "./assets/layers/doctors/doctors.svg"
}, },
{
"if": "theme=hotels",
"then": "./assets/layers/hotel/hotel.svg"
},
{ {
"if": "theme=indoors", "if": "theme=indoors",
"then": "./assets/layers/entrance/entrance.svg" "then": "./assets/layers/entrance/entrance.svg"

View file

@ -1,142 +1,142 @@
[ [
{ {
"path": "maxspeed_10.svg", "path": "maxspeed_10 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_100.svg", "path": "maxspeed_100 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_110.svg", "path": "maxspeed_110 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_120.svg", "path": "maxspeed_120 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_130.svg", "path": "maxspeed_130 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_140.svg", "path": "maxspeed_140 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_20.svg", "path": "maxspeed_20 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_30.svg", "path": "maxspeed_30 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_40.svg", "path": "maxspeed_40 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_50.svg", "path": "maxspeed_50 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_60.svg", "path": "maxspeed_60 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_70.svg", "path": "maxspeed_70 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_80.svg", "path": "maxspeed_80 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
"path": "maxspeed_90.svg", "path": "maxspeed_90 mph.svg",
"license": "CC 4.0", "license": "CC 4.0",
"authors": [ "authors": [
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
}, },
{ {
@ -146,7 +146,7 @@
"yopaseopor" "yopaseopor"
], ],
"sources": [ "sources": [
"https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30.svg" "https://raw.githubusercontent.com/yopaseopor/beta_preset_josm/master/ES/traffic_signs/ES/ES_R301-30 mph.svg"
] ]
} }
] ]

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

@ -45,6 +45,7 @@
"startZoom": 12, "startZoom": 12,
"widenFactor": 1.2, "widenFactor": 1.2,
"layers": [ "layers": [
"parking" "parking",
"parking_spaces"
] ]
} }

View file

@ -3566,7 +3566,7 @@
}, },
"width": { "width": {
"question": "Wie breit ist diese Tür bzw. dieser Eingang?", "question": "Wie breit ist diese Tür bzw. dieser Eingang?",
"render": "Diese Tür hat eine Durchgangsbreite von {canonical(width)} Meter" "render": "Diese Tür hat eine Durchgangsbreite von {canonical(width)}"
} }
}, },
"title": { "title": {
@ -4249,10 +4249,51 @@
}, },
"question": "Welche Bauform hat der Hydrant?", "question": "Welche Bauform hat der Hydrant?",
"render": " Hydranten-Typ: {fire_hydrant:type}" "render": " Hydranten-Typ: {fire_hydrant:type}"
},
"hydrant-couplings-diameters": {
"question": "Welchen Durchmesser haben die Kupplungen dieses Hydranten?",
"freeform": {
"placeholder": "Kupplungsdurchmesser"
},
"render": "Kupplungsdurchmesser: {couplings:diameters}"
},
"hydrant-couplings": {
"freeform": {
"placeholder": "Art der Kupplung"
},
"mappings": {
"0": {
"then": "Storz-Kupplung"
},
"1": {
"then": "UNI-Kupplung"
},
"2": {
"then": "Barcelona-Kupplung"
}
},
"question": "Welche Art von Kupplungen hat dieser Hydrant?",
"render": "Kupplungen: {couplings:type}"
},
"hydrant-diameter": {
"freeform": {
"placeholder": "Rohrdurchmesser"
},
"question": "Was ist der Rohrdurchmesser dieses Hydranten?",
"render": "Rohrdurchmesser: {canonical(fire_hydrant:diameter)}"
} }
}, },
"title": { "title": {
"render": "Hydrant" "render": "Hydrant"
},
"units": {
"0": {
"applicableUnits": {
"0": {
"human": "Millimeter"
}
}
}
} }
}, },
"indoors": { "indoors": {
@ -4366,7 +4407,7 @@
"then": "Der Bordstein hat kein taktiles Pflaster." "then": "Der Bordstein hat kein taktiles Pflaster."
}, },
"2": { "2": {
"then": "Der Bordstein hat ein taktiles Pflaster, das aber falsch ist" "then": "Der Bordstein hat ein taktiles Pflaster, das aber falsch ist."
} }
}, },
"question": "Gibt es am Bordstein ein taktiles Pflaster?" "question": "Gibt es am Bordstein ein taktiles Pflaster?"
@ -4648,7 +4689,7 @@
} }
}, },
"question": "Wie hoch ist die zulässige Höchstgeschwindigkeit, die man auf dieser Straße fahren darf?", "question": "Wie hoch ist die zulässige Höchstgeschwindigkeit, die man auf dieser Straße fahren darf?",
"render": "Die zulässige Höchstgeschwindigkeit auf dieser Straße ist {maxspeed}" "render": "Die zulässige Höchstgeschwindigkeit auf dieser Straße ist {canonical(maxspeed)}"
} }
}, },
"title": { "title": {

View file

@ -3566,7 +3566,7 @@
}, },
"width": { "width": {
"question": "What is the width of this door/entrance?", "question": "What is the width of this door/entrance?",
"render": "This door has a width of {canonical(width)} meter" "render": "This door has a width of {canonical(width)}"
} }
}, },
"title": { "title": {
@ -4438,7 +4438,7 @@
"then": "This kerb does not have tactile paving." "then": "This kerb does not have tactile paving."
}, },
"2": { "2": {
"then": "This kerb has tactile paving, but it is incorrect" "then": "This kerb has tactile paving, but it is incorrect."
} }
}, },
"question": "Is there tactile paving at this kerb?" "question": "Is there tactile paving at this kerb?"
@ -4720,7 +4720,7 @@
} }
}, },
"question": "What is the legal maximum speed one is allowed to drive on this road?", "question": "What is the legal maximum speed one is allowed to drive on this road?",
"render": "The maximum allowed speed on this road is {maxspeed}" "render": "The maximum allowed speed on this road is {canonical(maxspeed)}"
} }
}, },
"title": { "title": {

View file

@ -3049,7 +3049,7 @@
"then": "Cette bordure n'a pas de revêtement podotactile." "then": "Cette bordure n'a pas de revêtement podotactile."
}, },
"2": { "2": {
"then": "Cette bordure a un pavage tactile, mais il est incorrect" "then": "Cette bordure a un pavage tactile, mais il est incorrect."
} }
}, },
"question": "Y a-t-il un revêtement tactile sur cette bordure ?" "question": "Y a-t-il un revêtement tactile sur cette bordure ?"

View file

@ -3406,6 +3406,9 @@
"presets": { "presets": {
"0": { "0": {
"title": "een toegang" "title": "een toegang"
},
"1": {
"title": "een binnendeur"
} }
}, },
"tagRenderings": { "tagRenderings": {
@ -3509,7 +3512,7 @@
}, },
"width": { "width": {
"question": "Wat is de breedte van deze deur/toegang?", "question": "Wat is de breedte van deze deur/toegang?",
"render": "Deze deur heeft een breedte van {canonical(width)} meter" "render": "Deze deur heeft een breedte van {canonical(width)}"
} }
}, },
"title": { "title": {
@ -4221,7 +4224,43 @@
"description": "Een basis voor indoor-navigatie: toont binnenruimtes", "description": "Een basis voor indoor-navigatie: toont binnenruimtes",
"name": "Binnenruimtes", "name": "Binnenruimtes",
"title": { "title": {
"render": "Binnenruimte {name}" "render": "Binnenruimte {name}",
"mappings": {
"0": {
"then": "Binnenruimte {name}"
},
"4": {
"then": "Binnendeur {name}"
},
"3": {
"then": "Gang in gebouw {name}"
},
"1": {
"then": "Gebied in gebouw {name}"
},
"5": {
"then": "Verdieping in gebouw {name}"
},
"2": {
"then": "Muur in gebouw {name}"
}
}
},
"tagRenderings": {
"name": {
"render": "Deze ruimte heet {name}",
"question": "Wat is de naam van deze ruimte?",
"freeform": {
"placeholder": "Naam van de ruimte"
}
},
"ref": {
"question": "Wat is het referentienummer van deze ruimte?",
"freeform": {
"placeholder": "Referentienummer van de ruimte (bv. '1.1' of A1' )"
},
"render": "Deze ruimte heeft het referentienummer {ref}"
}
} }
}, },
"information_board": { "information_board": {
@ -4264,7 +4303,16 @@
"1": { "1": {
"options": { "options": {
"0": { "0": {
"question": "Drempes met of zonder" "question": "Stoepranden met of zonder voelbare bestrating"
},
"1": {
"question": "Stoeprand met voelbare bestrating"
},
"2": {
"question": "Stoeprand zonder voelbare bestrating"
},
"3": {
"question": "Stoeprand zonder informatie over voelbare bestrating"
} }
} }
} }
@ -4303,6 +4351,20 @@
} }
}, },
"question": "Hoe hoog is deze stoeprand?" "question": "Hoe hoog is deze stoeprand?"
},
"tactile-paving": {
"mappings": {
"0": {
"then": "Deze stoeprand heeft voelbare bestrating."
},
"1": {
"then": "Deze stoeprand heeft geen voelbare bestrating."
},
"2": {
"then": "Deze stoeprand heeft voelbare bestrating, maar deze is incorrect."
}
},
"question": "Is er voelbare bestrating bij deze stoeprand?"
} }
}, },
"title": { "title": {
@ -4353,6 +4415,10 @@
"override": { "override": {
"question": "Wanneer is deze kinderopvang geopend?" "question": "Wanneer is deze kinderopvang geopend?"
} }
},
"name": {
"question": "Wat is de naam van deze faciliteit?",
"render": "Deze faciliteit heet <b>{name}</b>"
} }
}, },
"title": { "title": {
@ -4423,7 +4489,7 @@
"then": "Dit is een woonerf en heeft dus een maximale snelheid van 20km/h" "then": "Dit is een woonerf en heeft dus een maximale snelheid van 20km/h"
} }
}, },
"render": "De maximum toegestane snelheid op deze weg is {maxspeed}" "render": "De maximum toegestane snelheid op deze weg is {canonical(maxspeed)}"
} }
}, },
"title": { "title": {
@ -6800,5 +6866,92 @@
} }
} }
} }
},
"maproulette": {
"filter": {
"0": {
"options": {
"1": {
"question": "Toon aangemaakte taken"
},
"8": {
"question": "Toon uitgeschakelde taken"
},
"0": {
"question": "Toon taken met alle statussen"
},
"3": {
"question": "Toon vals-positieve taken"
},
"4": {
"question": "Toon overgeslagen taken"
},
"5": {
"question": "Toon verwijderde taken"
},
"6": {
"question": "Toon al opgeloste taken"
},
"7": {
"question": "Toon taken die als te lastig gemarkeerd zijn"
},
"2": {
"question": "Toon opgeloste taken"
}
}
},
"1": {
"options": {
"0": {
"question": "Naam uitdaging bevat {search}"
}
}
},
"2": {
"options": {
"0": {
"question": "ID uitdaging is {search}"
}
}
}
},
"description": "Laag met alle taken uit MapRoulette",
"name": "MapRoulette Taken"
},
"transit_routes": {
"tagRenderings": {
"operator": {
"question": "Welk bedrijf exploiteert deze buslijn?",
"render": "Deze buslijn wordt geëxploiteerd door {operator}"
},
"from": {
"render": "Deze buslijn begint bij {from}",
"question": "Wat is het beginpunt van deze buslijn?"
},
"to": {
"render": "Deze buslijn eindigt bij {to}",
"question": "Wat is het eindpunt van deze buslijn?"
},
"colour": {
"question": "Wat is de kleur van deze buslijn?",
"render": "Deze buslijn heeft de kleur {colour}"
},
"name": {
"question": "Wat is de naam van deze buslijn (bv. Bus XX: Van => Via => Naar)"
},
"network": {
"question": "Bij welk netwerk hoort deze buslijn?",
"render": "Deze buslijn is onderdeel van het {network} netwerk"
},
"via": {
"question": "Via welk punt gaat deze buslijn?",
"render": "Deze buslijn gaat via {via}"
}
},
"title": {
"render": "Buslijn"
},
"description": "Laag met buslijnen",
"name": "Buslijnen"
} }
} }

View file

@ -26,7 +26,7 @@
"readMessages": "Je hebt ongelezen berichten. Je moet deze lezen voordat je een punt verwijderd, een andere bijdrager heeft misschien feedback", "readMessages": "Je hebt ongelezen berichten. Je moet deze lezen voordat je een punt verwijderd, een andere bijdrager heeft misschien feedback",
"reasons": { "reasons": {
"disused": "Het wordt niet meer onderhouden of is verwijderd", "disused": "Het wordt niet meer onderhouden of is verwijderd",
"duplicate": "Dit punt is een duplicaat van een ander punt", "duplicate": "Dit object is een duplicaat van een ander object",
"notFound": "Het kon niet gevonden worden", "notFound": "Het kon niet gevonden worden",
"test": "Dit punt was een test en was nooit echt aanwezig" "test": "Dit punt was een test en was nooit echt aanwezig"
}, },
@ -49,19 +49,19 @@
"confirmIntro": "<h3>Voeg een {title} toe?</h3>Het punt dat je toevoegt, is <b>zichtbaar voor iedereen</b>. Veel applicaties gebruiken deze data, voeg dus enkel punten toe die echt bestaan.", "confirmIntro": "<h3>Voeg een {title} toe?</h3>Het punt dat je toevoegt, is <b>zichtbaar voor iedereen</b>. Veel applicaties gebruiken deze data, voeg dus enkel punten toe die echt bestaan.",
"disableFilters": "Zet alle filters af", "disableFilters": "Zet alle filters af",
"disableFiltersExplanation": "Interessepunten kunnen verborgen zijn door een filter", "disableFiltersExplanation": "Interessepunten kunnen verborgen zijn door een filter",
"hasBeenImported": "Dit object is reeds geimporteerd", "hasBeenImported": "Dit object is reeds geïmporteerd",
"import": { "import": {
"hasBeenImported": "Dit object is geïmporteerd", "hasBeenImported": "Dit object is geïmporteerd",
"howToTest": "Voor testmode, voeg <b>test=true</b> of <b>backend=osm-test</b> to aan de URL. De wijzigingenset zal in de console geprint worden. Gelieve een PR te openen om dit thema als officieel thema toe te voegen en zo de import-knop te activeren.", "howToTest": "Voor testmode, voeg <b>test=true</b> of <b>backend=osm-test</b> to aan de URL. De wijzigingenset zal in de console geprint worden. Gelieve een PR te openen om dit thema als officieel thema toe te voegen en zo de import-knop te activeren.",
"importTags": "Het element zal deze tags krijgen: {tags}", "importTags": "Het element zal deze tags krijgen: {tags}",
"officialThemesOnly": "In onofficiële thema's is de importeerknop uitgeschakeld om ongelukjes te vermijden", "officialThemesOnly": "In onofficiële thema's is de importeerknop uitgeschakeld om ongelukjes te vermijden",
"wrongType": "Dit object is geen punt of lijn, en kan daarom niet geïmporteerd worden", "wrongType": "Dit object is geen punt of lijn, en kan daarom niet geïmporteerd worden",
"wrongTypeToConflate": "Dit element is geen punt of weg en kan dus niet samengevoegd worden", "wrongTypeToConflate": "Dit object is geen punt of weg en kan dus niet samengevoegd worden",
"zoomInMore": "Zoom verder in om dit object af te handelen" "zoomInMore": "Zoom verder in om dit object af te handelen"
}, },
"importTags": "Het object zal deze tags krijgen: {tags}", "importTags": "Het object zal deze tags krijgen: {tags}",
"intro": "Kies hieronder welk punt je wilt toevoegen<br/>", "intro": "Kies hieronder welk punt je wilt toevoegen<br/>",
"layerNotEnabled": "De laag {layer} is gedeactiveerd. Activeer deze om een punt toe te voegen", "layerNotEnabled": "De laag {layer} is gedeactiveerd. Activeer deze om een object toe te voegen",
"openLayerControl": "Open de laag-instellingen", "openLayerControl": "Open de laag-instellingen",
"pleaseLogin": "Gelieve je aan te melden om een punt toe te voegen", "pleaseLogin": "Gelieve je aan te melden om een punt toe te voegen",
"presetInfo": "Het nieuwe object krijgt de attributen {tags}", "presetInfo": "Het nieuwe object krijgt de attributen {tags}",
@ -272,7 +272,8 @@
"died": "Gestorven: {value}" "died": "Gestorven: {value}"
}, },
"searchWikidata": "Zoek op Wikidata", "searchWikidata": "Zoek op Wikidata",
"wikipediaboxTitle": "Wikipedia" "wikipediaboxTitle": "Wikipedia",
"searchToShort": "Je zoekopdracht is te kort, vul een langere tekst in"
} }
}, },
"image": { "image": {
@ -578,7 +579,8 @@
"noNearOrIn": "Sorry, Ik begreep je opdracht niet omdat ik geen <code>dichtbij</code> of <code>in</code> in je zoekopdracht.\nProbeer iets als <code>Zoek drinkwater in Londen</code>, <code>Zoek frituur in Brussel</code><p></p>\n <p>Daarnaast kan je ook <code>info {cmd}</code> proberen, om info te krijgen over een enkel object.</p>", "noNearOrIn": "Sorry, Ik begreep je opdracht niet omdat ik geen <code>dichtbij</code> of <code>in</code> in je zoekopdracht.\nProbeer iets als <code>Zoek drinkwater in Londen</code>, <code>Zoek frituur in Brussel</code><p></p>\n <p>Daarnaast kan je ook <code>info {cmd}</code> proberen, om info te krijgen over een enkel object.</p>",
"nothingFound": "Sorry, ik kon niets vinden voor <code>{search}</code>, dus kan ik {layerTitle} niet zoeken", "nothingFound": "Sorry, ik kon niets vinden voor <code>{search}</code>, dus kan ik {layerTitle} niet zoeken",
"overview": "Ik heb {length} overeenkomende items gevonden.", "overview": "Ik heb {length} overeenkomende items gevonden.",
"searching": "Aan het zoeken naar {layerTitle} {mode} <code>{search}</code>…" "searching": "Aan het zoeken naar {layerTitle} {mode} <code>{search}</code>…",
"docs": "Zoekt voor POIs in of nabij een locatie"
}, },
"shutdown": { "shutdown": {
"argmode": "Geeft aan op welke manier ik moet afsluiten. Dit moet één van de volgende woorden zijn: {verbs}", "argmode": "Geeft aan op welke manier ik moet afsluiten. Dit moet één van de volgende woorden zijn: {verbs}",

View file

@ -34,6 +34,47 @@
}, },
"question": "Does this place have an audio induction loop for people with reduced hearing?" "question": "Does this place have an audio induction loop for people with reduced hearing?"
}, },
"internet": {
"mappings": {
"0": {
"then": "This place offers wireless internet access"
},
"1": {
"then": "This place <b>does not</b> offer internet access"
},
"2": {
"then": "This place offers internet access"
},
"3": {
"then": "This place offers internet access via a terminal or computer"
},
"4": {
"then": "This place offers wired internet access"
}
},
"question": "Does this place offer internet access?"
},
"internet-fee": {
"mappings": {
"0": {
"then": "There is a fee for the internet access at this place"
},
"1": {
"then": "Internet access is free at this place"
},
"2": {
"then": "Internet access is free at this place, for customers only"
}
},
"question": "Is there a fee for internet access?"
},
"internet-ssid": {
"freeform": {
"placeholder": "Enter the network name"
},
"question": "What is the network name for the wireless internet access?",
"render": "The network name is <b>{internet_access:ssid}</b>"
},
"level": { "level": {
"mappings": { "mappings": {
"0": { "0": {

View file

@ -23,6 +23,47 @@
"email": { "email": {
"question": "Wat is het e-mailadres van {title()}?" "question": "Wat is het e-mailadres van {title()}?"
}, },
"internet": {
"mappings": {
"0": {
"then": "Deze plaats biedt draadloze internettoegang aan"
},
"1": {
"then": "Deze plaats biedt <b>geen</b> internettoegang aan"
},
"2": {
"then": "Deze plaats biedt internettoegang aan"
},
"3": {
"then": "Deze plaats biedt internettoegang via een terminal of computer aan"
},
"4": {
"then": "Deze plaats biedt bedrade internettoegang aan"
}
},
"question": "Biedt deze plaats internettoegang aan?"
},
"internet-fee": {
"mappings": {
"0": {
"then": "Er zijn kosten voor internettoegang op deze plaats"
},
"1": {
"then": "Internettoegang is gratis op deze plaats"
},
"2": {
"then": "Internettoegang is gratis op deze plaats, alleen voor klanten"
}
},
"question": "Zijn er kosten voor internettoegang?"
},
"internet-ssid": {
"freeform": {
"placeholder": "Voer de netwerknaam in"
},
"question": "Wat is de netwerknaam voor de draadloze internettoegang?",
"render": "De netwerknaam is <b>{internet_access:ssid}</b>"
},
"level": { "level": {
"mappings": { "mappings": {
"0": { "0": {

View file

@ -906,6 +906,28 @@
"tagRenderings": { "tagRenderings": {
"streetname": { "streetname": {
"render": "Diese Straße heißt {name}" "render": "Diese Straße heißt {name}"
},
"left-right-questions": {
"renderings": {
"1": {
"question": "Gibt es auf dieser Straßenseite einen Bürgersteig?",
"mappings": {
"1": {
"then": "Nein, es gibt keinen Bürgersteig für Fußgänger"
},
"0": {
"then": "Ja, es gibt einen Bürgersteig auf dieser Straßenseite"
},
"2": {
"then": "Es gibt einen separat kartierten Bürgersteig für Fußgänger"
}
}
},
"2": {
"question": "Wie breit ist der Bürgersteig auf dieser Straßenseite?",
"render": "Dieser Bürgersteig ist {sidewalk:left|right:width}m breit"
}
}
} }
}, },
"title": { "title": {

View file

@ -10,7 +10,7 @@
"cannotBeDeleted": "這圖徵無法刪除", "cannotBeDeleted": "這圖徵無法刪除",
"delete": "刪除", "delete": "刪除",
"explanations": { "explanations": {
"hardDelete": "這個已經在開放街圖被刪除了,可以被實驗性的貢獻者恢復", "hardDelete": "這個圖徵已經在開放街圖被刪除了,可以被實驗性的貢獻者恢復",
"selectReason": "請選擇為什麼這個圖徵應該被刪除", "selectReason": "請選擇為什麼這個圖徵應該被刪除",
"softDelete": "這個圖徵已經被更新,然後從程式被隱藏了。<span class=\"subtle\">{reason}</span>" "softDelete": "這個圖徵已經被更新,然後從程式被隱藏了。<span class=\"subtle\">{reason}</span>"
}, },

View file

@ -7,22 +7,21 @@ describe("Unit", () => {
it("should convert a value back and forth", () => { it("should convert a value back and forth", () => {
const unit = new Denomination({ const denomintion = new Denomination({
"canonicalDenomination": "MW", "canonicalDenomination": "MW",
"alternativeDenomination": ["megawatts", "megawatt"], "alternativeDenomination": ["megawatts", "megawatt"],
"human": { "human": {
"en": " megawatts", "en": " megawatts",
"nl": " megawatt" "nl": " megawatt"
}, },
"default": true
}, "test"); }, "test");
const canonical = unit.canonicalValue("5") const canonical = denomintion.canonicalValue("5", true)
expect(canonical).eq( "5 MW") expect(canonical).eq( "5 MW")
const units = new Unit(["key"], [unit], false) const units = new Unit(["key"], [denomintion], false)
const [detected, detectedDenom] = units.findDenomination("5 MW") const [detected, detectedDenom] = units.findDenomination("5 MW", () => "be")
expect(detected).eq( "5") expect(detected).eq( "5")
expect(detectedDenom).eq( unit) expect(detectedDenom).eq( denomintion)
} }
) )
}) })