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(
{
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"],
},
@ -261,13 +261,14 @@ export default class SimpleMetaTaggers {
continue;
}
const value = feature.properties[key]
const denom = unit.findDenomination(value)
const denom = unit.findDenomination(value, () => feature.properties["_country"])
if (denom === undefined) {
// no valid value found
break;
}
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) {
break;
}

View file

@ -1,21 +1,22 @@
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 {Store, UIEventSource} from "../Logic/UIEventSource";
import {Store} from "../Logic/UIEventSource";
import BaseUIElement from "../UI/BaseUIElement";
import Toggle from "../UI/Input/Toggle";
export class Denomination {
public readonly canonical: 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 alternativeDenominations: string [];
private readonly _human: Translation;
private readonly _humanSingular?: Translation;
constructor(json: ApplicableUnitJson, context: string) {
constructor(json: DenominationConfigJson, context: string) {
context = `${context}.unit(${json.canonicalDenomination})`
this.canonical = json.canonicalDenomination.trim()
if (this.canonical === undefined) {
@ -32,8 +33,12 @@ export class Denomination {
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._humanSingular = Translations.T(json.humanSingular, context + "humanSingular")
@ -68,32 +73,31 @@ export class Denomination {
* const unit = new Denomination({
* canonicalDenomination: "m",
* alternativeDenomination: ["meter"],
* 'default': true,
* human: {
* en: "meter"
* }
* }, "test")
* unit.canonicalValue("42m") // =>"42 m"
* unit.canonicalValue("42") // =>"42 m"
* unit.canonicalValue("42 m") // =>"42 m"
* unit.canonicalValue("42 meter") // =>"42 m"
*
* unit.canonicalValue("42m", true) // =>"42 m"
* unit.canonicalValue("42", true) // =>"42 m"
* unit.canonicalValue("42 m", true) // =>"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
* const unit = new Denomination({
* canonicalDenomination: "",
* alternativeDenomination: ["meter","m"],
* 'default': true,
* human: {
* en: "meter"
* }
* }, "test")
* unit.canonicalValue("42m") // =>"42"
* unit.canonicalValue("42") // =>"42"
* unit.canonicalValue("42 m") // =>"42"
* unit.canonicalValue("42 meter") // =>"42"
* unit.canonicalValue("42m", true) // =>"42"
* unit.canonicalValue("42", true) // =>"42"
* unit.canonicalValue("42 m", true) // =>"42"
* unit.canonicalValue("42 meter", true) // =>"42"
*/
public canonicalValue(value: string, actAsDefault?: boolean) : string {
public canonicalValue(value: string, actAsDefault: boolean) : string {
if (value === undefined) {
return undefined;
}
@ -114,7 +118,7 @@ export class Denomination {
*
* 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) {
return undefined;
@ -153,15 +157,26 @@ export class Denomination {
}
}
if (this.default || actAsDefault) {
const parsed = Number(value.trim())
if (!isNaN(parsed)) {
return value.trim();
}
if (!actAsDefault) {
return null
}
const parsed = Number(value.trim())
if (!isNaN(parsed)) {
return value.trim();
}
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[]> {
private readonly _state: DesugaringContext;
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");
this._state = state;
this._self = self;
this._options = options;
}
convert(json: string | TagRenderingConfigJson | { builtin: string | string[]; override: any }, context: string): { result: TagRenderingConfigJson[]; errors: string[]; warnings: string[] } {
@ -65,12 +70,14 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
const contextWriter = new AddContextToTranslations<TagRenderingConfigJson>("layers:")
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]);
if (found.condition === undefined) {
found.condition = layer.source.osmTags
} else {
found.condition = {and: [found.condition, layer.source.osmTags]}
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) {
found.condition = layer.source.osmTags
} else {
found.condition = {and: [found.condition, layer.source.osmTags]}
}
}
found = contextWriter.convertStrict(found, layer.id + ".tagRenderings." + found["id"])
@ -561,7 +568,7 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
new On("tagRenderings", layer => new Concat(new ExpandTagRendering(state, layer))),
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 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 ?? []))
}
{
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) {
// 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
*/
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
* 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
*/
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,
@ -63,9 +85,5 @@ export interface ApplicableUnitJson {
*/
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 {FixedUiElement} from "../../UI/Base/FixedUiElement";
import {Paragraph} from "../../UI/Base/Paragraph";
import spec = Mocha.reporters.spec;
import SpecialVisualizations from "../../UI/SpecialVisualizations";
export interface Mapping {
readonly if: TagsFilter,

View file

@ -8,14 +8,11 @@ export class Unit {
public readonly appliesToKeys: Set<string>;
public readonly denominations: Denomination[];
public readonly denominationsSorted: Denomination[];
public readonly defaultDenom: Denomination;
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.denominations = applicableUnits;
this.defaultDenom = applicableUnits.filter(denom => denom.default)[0]
this.denominations = applicableDenominations;
this.eraseInvalid = eraseInvalid
const seenUnitExtensions = new Set<string>();
@ -52,8 +49,6 @@ export class Unit {
addPostfixesOf(denomination._canonicalSingular)
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
const defaultSet = json.applicableUnits.filter(u => u.default === true)
// No default is defined - we pick the first as default
if (defaultSet.length === 0) {
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(", ")}`
if(json.applicableUnits.some(denom => denom.useAsDefaultInput !== undefined)){
json.applicableUnits.forEach(denom => {
denom.useAsDefaultInput = denom.useAsDefaultInput ?? false
})
}
const applicable = json.applicableUnits.map((u, i) => new Denomination(u, `${ctx}.units[${i}]`))
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
*/
findDenomination(valueWithDenom: string): [string, Denomination] {
findDenomination(valueWithDenom: string, country: () => string): [string, Denomination] {
if (valueWithDenom === undefined) {
return undefined;
}
const defaultDenom = this.getDefaultDenomination(country)
for (const denomination of this.denominationsSorted) {
const bare = denomination.StrippedValue(valueWithDenom)
const bare = denomination.StrippedValue(valueWithDenom, defaultDenom === denomination)
if (bare !== null) {
return [bare, denomination]
}
@ -109,11 +101,11 @@ export class Unit {
return [undefined, undefined]
}
asHumanLongValue(value: string): BaseUIElement {
asHumanLongValue(value: string, country: () => string): BaseUIElement {
if (value === undefined) {
return undefined;
}
const [stripped, denom] = this.findDenomination(value)
const [stripped, denom] = this.findDenomination(value, country)
const human = stripped === "1" ? denom?.humanSingular : denom?.human
if (human === undefined) {
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.
* E.g.
* if 'megawatt' is a possible denomination, then '5 Meg' will be rewritten to '5' (which can then be validated as a valid pnat)
*
* Returns the original string if nothign matches
*/
stripUnitParts(str: string) {
if (str === undefined) {
return undefined;
}
for (const denominationPart of this.possiblePostFixes) {
if (str.endsWith(denominationPart)) {
return str.substring(0, str.length - denominationPart.length).trim()
public getDefaultInput(country: () => string | string[]) {
console.log("Searching the default denomination for input", country)
for (const denomination of this.denominations) {
if (denomination.useAsDefaultInput === true) {
return denomination
}
if (denomination.useAsDefaultInput === undefined || denomination.useAsDefaultInput === false) {
continue
}
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 str;
return this.denominations[0]
}
public getDefaultDenomination(country: () => string){
for (const denomination of this.denominations) {
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]
}
}

View file

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

View file

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

View file

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

View file

@ -90,7 +90,7 @@ export class TextFieldDef {
if (options.unit !== undefined) {
// Reformatting is handled by the unit in this case
options["isValid"] = str => {
const denom = options.unit.findDenomination(str);
const denom = options.unit.findDenomination(str, options?.country);
if (denom === undefined) {
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")
const fixedDenom = unit.denominations.length === 1 ? unit.denominations[0] : undefined
@ -169,7 +169,7 @@ export class TextFieldDef {
},
(valueWithDenom: string) => {
// 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) {
// 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]

View file

@ -617,6 +617,7 @@ export default class TagRenderingQuestion extends Combine {
const tagsData = tags.data;
const feature = state?.allElements?.ContainingFeatures?.get(tagsData.id)
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({
country: () => tagsData._country,
location: [center[1], center[0]],

View file

@ -1273,6 +1273,9 @@ export default class SpecialVisualizations {
const tagRendering = layer.tagRenderings.find(tr => tr.id === tagRenderingId)
tagRenderings.push([layer, tagRendering])
}
if(tagRenderings.length === 0){
throw "Could not create stolen tagrenddering: tagRenderings not found"
}
return new VariableUiElement(featureTags.map(tags => {
const featureId = tags[featureIdKey]
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;
}
public static NoNull<T>(array: T[]): T[] {
return array?.filter(o => o !== undefined && o !== null)
public static NoNull<T>(array: T[]): NonNullable<T>[] {
return <any> array?.filter(o => o !== undefined && o !== null)
}
public static Hist(array: string[]): Map<string, number> {

View file

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

View file

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

View file

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

View file

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

View file

@ -80,7 +80,10 @@
"phone",
"email",
"website",
"wheelchair-access"
"wheelchair-access",
"internet",
"internet-fee",
"internet-ssid"
],
"allowMove": {
"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">
<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"/>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

View file

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

View file

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

View file

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

View file

@ -110,16 +110,19 @@
"allowSplit": true,
"mapRendering": [
{
"render": null,
"icon": {
"render": null,
"mappings": [
{
"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"
}
]
},
"condition": "maxspeed!=30",
"iconSize": {
"render": "32,32,center"
},
@ -154,7 +157,6 @@
"kmh",
"kph"
],
"default": true,
"human": {
"en": "kilometers/hour",
"ca": "quilòmetres/hora",
@ -172,6 +174,7 @@
},
{
"canonicalDenomination": "mph",
"useIfNoUnitGiven": ["gb","us"],
"alternativeDenomination": [
"m/u",
"mh",

View file

@ -164,6 +164,15 @@
"fr": "Il n'y a pas de places de stationnement pour personnes à mobilité réduite"
},
"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": {
@ -247,9 +256,9 @@
"iconBadges": [
{
"if": {
"and": [
"capacity:disabled~*",
"capacity:disabled!=no"
"or": [
"capacity:disabled>0",
"capacity:disabled=yes"
]
},
"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",
"reviews"
],
@ -365,4 +368,4 @@
]
}
]
}
}

View file

@ -264,7 +264,7 @@
},
"mappings": [
{
"if": "access=public",
"if": "access=yes",
"then": {
"nl": "Publiek toegankelijk",
"fr": "Accessible au public",
@ -309,6 +309,19 @@
"de": "Der Sportplatz ist nicht öffentlich zugänglich (es ist ein privater Sportplatz)",
"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": [
{
"useIfNoUnitGiven": true,
"canonicalDenomination": "m",
"alternativeDenomination": [
"meter"
@ -74,16 +75,16 @@
}
},
{
"default": true,
"useAsDefaultInput": true,
"canonicalDenomination": "cm",
"alternativeDenomination": [
"centimeter",
"cms"
],
"human": {
"en": "centimeter",
"fr": "centimètre",
"de": "Zentimeter"
"en": " centimeter",
"fr": " centimètre",
"de": " Zentimeter"
}
}
]

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",
"nb_NO": " meter",
"es": " metro"
},
"default": true
}
},
{
"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",
"then": "./assets/layers/doctors/doctors.svg"
},
{
"if": "theme=hotels",
"then": "./assets/layers/hotel/hotel.svg"
},
{
"if": "theme=indoors",
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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",
"authors": [
"yopaseopor"
],
"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"
],
"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,
"widenFactor": 1.2,
"layers": [
"parking"
"parking",
"parking_spaces"
]
}

View file

@ -3566,7 +3566,7 @@
},
"width": {
"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": {
@ -4249,10 +4249,51 @@
},
"question": "Welche Bauform hat der Hydrant?",
"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": {
"render": "Hydrant"
},
"units": {
"0": {
"applicableUnits": {
"0": {
"human": "Millimeter"
}
}
}
}
},
"indoors": {
@ -4366,7 +4407,7 @@
"then": "Der Bordstein hat kein taktiles Pflaster."
},
"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?"
@ -4648,7 +4689,7 @@
}
},
"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": {
@ -7156,4 +7197,4 @@
}
}
}
}
}

View file

@ -3566,7 +3566,7 @@
},
"width": {
"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": {
@ -4438,7 +4438,7 @@
"then": "This kerb does not have tactile paving."
},
"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?"
@ -4720,7 +4720,7 @@
}
},
"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": {
@ -7228,4 +7228,4 @@
}
}
}
}
}

View file

@ -3049,7 +3049,7 @@
"then": "Cette bordure n'a pas de revêtement podotactile."
},
"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 ?"
@ -4459,4 +4459,4 @@
}
}
}
}
}

View file

@ -3406,6 +3406,9 @@
"presets": {
"0": {
"title": "een toegang"
},
"1": {
"title": "een binnendeur"
}
},
"tagRenderings": {
@ -3509,7 +3512,7 @@
},
"width": {
"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": {
@ -4221,7 +4224,43 @@
"description": "Een basis voor indoor-navigatie: toont binnenruimtes",
"name": "Binnenruimtes",
"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": {
@ -4264,7 +4303,16 @@
"1": {
"options": {
"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?"
},
"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": {
@ -4353,6 +4415,10 @@
"override": {
"question": "Wanneer is deze kinderopvang geopend?"
}
},
"name": {
"question": "Wat is de naam van deze faciliteit?",
"render": "Deze faciliteit heet <b>{name}</b>"
}
},
"title": {
@ -4423,7 +4489,7 @@
"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": {
@ -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",
"reasons": {
"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",
"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.",
"disableFilters": "Zet alle filters af",
"disableFiltersExplanation": "Interessepunten kunnen verborgen zijn door een filter",
"hasBeenImported": "Dit object is reeds geimporteerd",
"hasBeenImported": "Dit object is reeds geïmporteerd",
"import": {
"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.",
"importTags": "Het element zal deze tags krijgen: {tags}",
"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",
"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"
},
"importTags": "Het object zal deze tags krijgen: {tags}",
"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",
"pleaseLogin": "Gelieve je aan te melden om een punt toe te voegen",
"presetInfo": "Het nieuwe object krijgt de attributen {tags}",
@ -272,7 +272,8 @@
"died": "Gestorven: {value}"
},
"searchWikidata": "Zoek op Wikidata",
"wikipediaboxTitle": "Wikipedia"
"wikipediaboxTitle": "Wikipedia",
"searchToShort": "Je zoekopdracht is te kort, vul een langere tekst in"
}
},
"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>",
"nothingFound": "Sorry, ik kon niets vinden voor <code>{search}</code>, dus kan ik {layerTitle} niet zoeken",
"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": {
"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?"
},
"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": {
"mappings": {
"0": {

View file

@ -23,6 +23,47 @@
"email": {
"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": {
"mappings": {
"0": {

View file

@ -906,6 +906,28 @@
"tagRenderings": {
"streetname": {
"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": {
@ -1008,4 +1030,4 @@
"shortDescription": "Eine Karte mit Abfalleimern",
"title": "Abfalleimer"
}
}
}

View file

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

View file

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