Ported bicycle parking layer to .json-file

This commit is contained in:
Pieter Vander Vennet 2020-09-03 02:05:09 +02:00
parent ee9c9e201f
commit 5f4c2ec536
18 changed files with 415 additions and 336 deletions

View file

@ -8,14 +8,13 @@ import Translation from "../../UI/i18n/Translation";
import {LayerConfigJson} from "./LayerConfigJson";
import {LayerDefinition, Preset} from "../LayerDefinition";
import {TagDependantUIElementConstructor} from "../UIElementConstructor";
import FixedText from "../Questions/FixedText";
import Translations from "../../UI/i18n/Translations";
import Combine from "../../UI/Base/Combine";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import {ImageCarouselConstructor} from "../../UI/Image/ImageCarousel";
import * as drinkingWater from "../../assets/layers/drinking_water/drinking_water.json";
import * as ghostbikes from "../../assets/layers/ghost_bike/ghost_bike.json"
import * as viewpoint from "../../assets/layers/viewpoint/viewpoint.json"
import * as bike_parking from "../../assets/layers/bike_parking/bike_parking.json"
import {Utils} from "../../Utils";
export class FromJSON {
@ -29,6 +28,7 @@ export class FromJSON {
FromJSON.Layer(drinkingWater),
FromJSON.Layer(ghostbikes),
FromJSON.Layer(viewpoint),
FromJSON.Layer(bike_parking),
];
for (const layer of sharedLayersList) {
@ -201,7 +201,7 @@ export class FromJSON {
if (tag.indexOf("!~") >= 0) {
const split = Utils.SplitFirst(tag, "!~");
if (split[1] === "*") {
split[1] = ".*"
split[1] = "..*"
}
return new RegexTag(
split[0],
@ -212,7 +212,7 @@ export class FromJSON {
if (tag.indexOf("!=") >= 0) {
const split = Utils.SplitFirst(tag, "!=");
if (split[1] === "*") {
split[1] = ".*"
split[1] = "..*"
}
return new RegexTag(
split[0],
@ -223,7 +223,7 @@ export class FromJSON {
if (tag.indexOf("~") >= 0) {
const split = Utils.SplitFirst(tag, "~");
if (split[1] === "*") {
split[1] = ".*"
split[1] = "..*"
}
return new RegexTag(
split[0],
@ -259,6 +259,8 @@ export class FromJSON {
const iconSize = FromJSON.TagRenderingWithDefault(json.iconSize, "iconSize", "40,40,center");
const color = FromJSON.TagRenderingWithDefault(json.color, "layercolor", "#0000ff");
const width = FromJSON.TagRenderingWithDefault(json.width, "layerwidth", "10");
const tagRenderings = json.tagRenderings?.map(FromJSON.TagRendering) ?? [];
const renderTags = {"id": "node/-1"}
const presets: Preset[] = json?.presets?.map(preset => {
return ({
@ -317,12 +319,13 @@ export class FromJSON {
title: FromJSON.TagRendering(json.title),
minzoom: json.minzoom,
presets: presets,
elementsToShow: json.tagRenderings?.map(FromJSON.TagRendering) ?? [],
elementsToShow: tagRenderings,
style: style,
wayHandling: json.wayHandling
}
);
console.log("Parsed layer is ", layer);
return layer;
}

View file

@ -1,52 +1,17 @@
import {LayerDefinition} from "../LayerDefinition";
import {Tag} from "../../Logic/Tags";
import FixedText from "../Questions/FixedText";
import ParkingType from "../Questions/bike/ParkingType";
import ParkingCapacity from "../Questions/bike/ParkingCapacity";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import Translations from "../../UI/i18n/Translations";
import ParkingAccessCargo from "../Questions/bike/ParkingAccessCargo";
import ParkingCapacityCargo from "../Questions/bike/ParkingCapacityCargo";
export default class BikeParkings extends LayerDefinition {
private readonly accessCargoDesignated = new Tag("cargo_bike", "designated");
private readonly accessCargoDesignated = new Tag();
constructor() {
super("bikeparking");
this.name = Translations.t.cyclofix.parking.name;
this.icon = "./assets/bike/parking.svg";
this.overpassFilter = new Tag("amenity", "bicycle_parking");
this.presets = [{
title: Translations.t.cyclofix.parking.title,
tags: [
new Tag("amenity", "bicycle_parking"),
]
}];
this.maxAllowedOverlapPercentage = 10;
this.minzoom = 17;
this.style = function () {
return {
color: "#00bb00",
icon: {
iconUrl: "./assets/bike/parking.svg",
iconSize: [50, 50],
iconAnchor: [25, 50]
}
};
};
this.title = new FixedText(Translations.t.cyclofix.parking.title)
super(undefined);
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
//new ParkingOperator(),
new ParkingType(),
new ParkingCapacity(),
new ParkingAccessCargo(),
new ParkingCapacityCargo().OnlyShowIf(this.accessCargoDesignated)
//new ParkingOperator(),
];
this.wayHandling = LayerDefinition.WAYHANDLING_CENTER_AND_WAY;
}
}

View file

@ -14,7 +14,7 @@ export default class Cyclofix extends Layout {
"cyclofix",
["en", "nl", "fr","gl"],
Translations.t.cyclofix.title,
[new BikeServices(), new BikeShops(), "drinking_water", new BikeParkings(), new BikeOtherShops(), new BikeCafes()],
[new BikeServices(), new BikeShops(), "drinking_water", "bike_parking", new BikeOtherShops(), new BikeCafes()],
16,
50.8465573,
4.3516970,

View file

@ -1,20 +0,0 @@
import { Tag } from "../../../Logic/Tags";
import Translations from "../../../UI/i18n/Translations";
import {TagRenderingOptions} from "../../TagRenderingOptions";
export default class ParkingAccessCargo extends TagRenderingOptions {
constructor() {
const key = "cargo_bike"
const to = Translations.t.cyclofix.parking.access_cargo
super({
priority: 15,
question: to.question.Render(),
mappings: [
{k: new Tag(key, "yes"), txt: to.yes},
{k: new Tag(key, "designated"), txt: to.designated},
{k: new Tag(key, "no"), txt: to.no}
]
});
}
}

View file

@ -1,18 +0,0 @@
import Translations from "../../../UI/i18n/Translations";
import {TagRenderingOptions} from "../../TagRenderingOptions";
export default class ParkingCapacity extends TagRenderingOptions {
constructor() {
const to = Translations.t.cyclofix.parking.capacity
super({
priority: 15,
question: to.question,
freeform: {
key: "capacity",
renderTemplate: to.render,
template: to.template
}
});
}
}

View file

@ -1,18 +0,0 @@
import Translations from "../../../UI/i18n/Translations";
import {TagRenderingOptions} from "../../TagRenderingOptions";
export default class ParkingCapacityCargo extends TagRenderingOptions {
constructor() {
const to = Translations.t.cyclofix.parking.capacity_cargo
super({
priority: 10,
question: to.question,
freeform: {
key: "capacity:cargo_bike",
renderTemplate: to.render,
template: to.template
}
});
}
}

View file

@ -1,60 +0,0 @@
import {Tag} from "../../../Logic/Tags";
import Translations from "../../../UI/i18n/Translations";
import Combine from "../../../UI/Base/Combine";
import {TagRenderingOptions} from "../../TagRenderingOptions";
class ParkingTypeHelper {
static GenerateMappings() {
const images = {
stands: "assets/bike/parking_staple.svg",
wall_loops: "assets/bike/parking_wall_loops.svg",
handlebar_holder: "assets/bike/parking_handlebar_holder.svg",
rack: "assets/bike/parking_rack.svg",
shed: "assets/bike/parking_shed.svg"
};
const toImg = (url) => `<br /><img width="150px" src=${url}>`
const mappings = [];
const to = Translations.t.cyclofix.parking.type
for (const imagesKey in images) {
const mapping =
{
k: new Tag("bicycle_parking", imagesKey),
txt: new Combine([
to[imagesKey],
to.eg,
toImg(images[imagesKey])
])
};
mappings.push(mapping);
}
return mappings;
}
}
export default class ParkingType extends TagRenderingOptions {
constructor() {
const to = Translations.t.cyclofix.parking.type
super({
priority: 5,
question: to.question,
freeform: {
key: "bicycle_parking",
extraTags: new Tag("fixme", "Freeform bicycle_parking= tag used: possibly a wrong value"),
template: to.template.txt,
renderTemplate: to.render.txt,
placeholder: Translations.t.cyclofix.freeFormPlaceholder,
},
mappings: ParkingTypeHelper.GenerateMappings()
});
}
}

View file

@ -205,7 +205,13 @@ TagRendering extends UIElement implements TagDependantUIElement {
InputElement<TagsFilter> {
const elements = [];
let freeformElement = undefined;
if (options.freeform !== undefined) {
freeformElement = this.InputForFreeForm(options.freeform);
}
if (options.mappings !== undefined) {
const previousTexts= [];
@ -221,10 +227,11 @@ TagRendering extends UIElement implements TagDependantUIElement {
elements.push(this.InputElementForMapping(mapping));
}
}
if (options.freeform !== undefined) {
elements.push(this.InputForFreeForm(options.freeform));
if(freeformElement !== undefined) {
elements.push(freeformElement);
}
if (elements.length == 0) {
@ -239,7 +246,7 @@ TagRendering extends UIElement implements TagDependantUIElement {
}
private InputElementForMapping(mapping: { k: TagsFilter, txt: string | Translation }) {
private InputElementForMapping(mapping: { k: TagsFilter, txt: (string | Translation) }) {
return new FixedInputElement(this.ApplyTemplate(mapping.txt),
mapping.k.substituteValues(this.currentTags.data)
);
@ -454,8 +461,6 @@ TagRendering extends UIElement implements TagDependantUIElement {
"</span>"]).Render();
}
console.log("No rendering for",this)
return "";
}
@ -466,7 +471,6 @@ TagRendering extends UIElement implements TagDependantUIElement {
private ApplyTemplate(template: string | Translation): UIElement {
if (template === undefined || template === null) {
console.warn("Applying template which is undefined by ",this); // TODO THis error msg can probably be removed
return undefined;
}
return new VariableUiElement(this.currentTags.map(tags => {

View file

@ -14,37 +14,50 @@ export abstract class TagsFilter {
export class RegexTag extends TagsFilter {
private readonly key: RegExp;
private readonly value: RegExp;
private readonly key: RegExp | string;
private readonly value: RegExp | string;
private readonly invert: boolean;
constructor(key: string | RegExp, value: RegExp, invert: boolean = false) {
constructor(key: string | RegExp, value: RegExp | string, invert: boolean = false) {
super();
this.key = typeof (key) === "string" ? new RegExp(key) : key;
this.key = key;
this.value = value;
this.invert = invert;
}
asOverpass(): string[] {
return [`['${this.key.source}'${this.invert ? "!" : ""}~'${this.value.source}']`];
return [`['${RegexTag.source(this.key)}'${this.invert ? "!" : ""}~'${RegexTag.source(this.value)}']`];
}
private static doesMatch(fromTag: string, possibleRegex: string | RegExp): boolean {
if(typeof possibleRegex === "string"){
return fromTag === possibleRegex;
}
return fromTag.match(possibleRegex) !== null;
}
private static source(r: string | RegExp) {
if (typeof (r) === "string") {
return r;
}
return r.source;
}
matches(tags: { k: string; v: string }[]): boolean {
for (const tag of tags) {
if (tag.k.match(this.key)) {
return tag.v.match(this.value) !== null;
if (RegexTag.doesMatch(tag.k, this.key)){
return RegexTag.doesMatch(tag.v, this.value);
}
}
return false;
}
substituteValues(tags: any) : TagsFilter{
console.warn("Not substituting values on regex tags");
return this;
}
asHumanString() {
return `${this.key.source}${this.invert ? "!" : ""}~${this.value.source}`;
return `${RegexTag.source(this.key)}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`;
}
}

View file

@ -27,7 +27,9 @@ export class GenerateEmpty {
startLat: 0,
startLon: 0,
startZoom: 1,
widenFactor: 0.05,
socialImage: "",
layers: []
}
}

View file

@ -12,23 +12,6 @@ export default class Translations {
static t = {
climbingTrees: {
layer: {
title: new T({
nl: "Klimbomen"
}),
description: new T({
nl: "Een klimboom is een mooie boom waar men in kan klimmen, al dan niet officieel"
})
},
layout: {
title: new T({nl: "Open Klimbomenkaart"}),
welcome: new T({nl: "Markeer je favoriete klimboom"})
}
},
cyclofix: {
title: new T({
@ -59,49 +42,10 @@ export default class Translations {
"Todas as modificacións que fagas serán gardadas de xeito automático na base de datos global do OpenStreetMap e outros poderán reutilizalos libremente.<br><br>" +
"Para máis información sobre o proxecto cyclofix, vai a <a href='https://cyclofix.osm.be/'>cyclofix.osm.be</a>."
}),
freeFormPlaceholder: new T({en: 'specify', nl: 'specifieer', fr: 'Specifiéz', gl: 'especificar'}),
parking: {
name: new T({en: 'bike parking', nl: 'fietsparking', fr: 'parking à vélo'}),
title: new T({
en: 'Bike parking', nl: 'Fietsparking', fr: 'Parking à vélo',
gl: 'Aparcadoiro de bicicletas'
}),
type: {
render: new T({
en: 'This is a bicycle parking of the type: {bicycle_parking}',
nl: 'Dit is een fietsparking van het type: {bicycle_parking}',
fr: 'Ceci est un parking à vélo de type {bicycle_parking}',
gl: 'Este é un aparcadoiro de bicicletas do tipo: {bicycle_parking}'
}),
template: new T({
en: 'Some other type: $$$',
nl: 'Een ander type: $$$',
fr: "D'autres types: $$$",
gl: "Algún outro tipo:: $$$"
}),
question: new T({
en: 'What is the type of this bicycle parking?',
nl: 'Van welk type is deze fietsparking?',
fr: 'Quelle type de parking s\'agit il? ',
gl: 'Que tipo de aparcadoiro de bicicletas é?'
}),
eg: new T({en: ", for example", nl: ", bijvoorbeeld", fr: ",par example", gl: ", por exemplo"}),
stands: new T({en: 'Staple racks', nl: 'Nietjes', fr: 'Arceaux', gl: 'De roda (Stands)'}),
wall_loops: new T({en: 'Wheel rack/loops', nl: 'Wielrek/lussen', fr: 'Pinces-roues', gl: 'Aros'}),
handlebar_holder: new T({
en: 'Handlebar holder',
nl: 'Stuurhouder',
fr: 'Support guidon',
gl: 'Cadeado para guiador'
}),
shed: new T({en: 'Shed', nl: 'Schuur', fr: 'Abri', gl: 'Abeiro'}),
rack: new T({en: 'Rack', nl: 'Rek', fr: 'Râtelier', gl: 'Cremalleira'}),
"two-tier": new T({
en: 'Two-tiered',
nl: 'Dubbel (twee verdiepingen)',
fr: 'Superposé',
gl: 'Dobre cremalleira'
}),
"two-tier": new T(),
},
operator: {
render: new T({
@ -124,86 +68,7 @@ export default class Translations {
gl: 'Operado por unha persoa privada'
}),
},
covered: {
question: new T({
en: 'Is this parking covered? Also select "covered" for indoor parkings.',
nl: 'Is deze parking overdekt? Selecteer ook "overdekt" voor fietsparkings binnen een gebouw.',
gl: 'Este aparcadoiro está cuberto? Tamén escolle "cuberto" para aparcadoiros interiores.'
}),
yes: new T({
en: 'This parking is covered (it has a roof)',
nl: 'Deze parking is overdekt (er is een afdak)',
gl: 'Este aparcadoiro está cuberto (ten un teito)'
}),
no: new T({
en: 'This parking is not covered',
nl: 'Deze parking is niet overdekt',
gl: 'Este aparcadoiro non está cuberto'
})
},
capacity: {
question: new T({
en: "How many bicycles fit in this bicycle parking (including possible cargo bicycles)?",
nl: "Voor hoeveel fietsen is er bij deze fietsparking plaats (inclusief potentiëel bakfietsen)?",
gl: "Cantas bicicletas caben neste aparcadoiro de bicicletas (incluídas as posíbeis bicicletas de carga)?"
}),
template: new T({
en: "This parking fits $nat$ bikes",
nl: "Deze parking heeft plaats voor $nat$ fietsen",
gl: "Neste aparcadoiro caben $nat$ bicicletas"
}),
render: new T({
en: "Place for {capacity} bikes (in total)",
nl: "Plaats voor {capacity} fietsen (in totaal)",
gl: "Lugar para {capacity} bicicletas (en total)"
}),
},
capacity_cargo: {
question: new T({
en: "How many cargo bicycles fit in this bicycle parking?",
nl: "Voor hoeveel bakfietsen heeft deze fietsparking plaats?",
fr: "TODO: fr",
gl: "Cantas bicicletas de carga caben neste aparcadoiro de bicicletas?"
}),
template: new T({
en: "This parking fits $nat$ cargo bikes",
nl: "Deze parking heeft plaats voor $nat$ fietsen",
fr: "TODO: fr",
gl: "Neste aparcadoiro caben $nat$ bicicletas de carga"
}),
render: new T({
en: "Place for {capacity:cargo_bike} cargo bikes",
nl: "Plaats voor {capacity:cargo_bike} bakfietsen",
fr: "TODO: fr",
gl: "Lugar para {capacity:cargo_bike} bicicletas de carga"
}),
},
access_cargo: {
question: new T({
en: "Does this bicycle parking have spots for cargo bikes?",
nl: "Heeft deze fietsparking plaats voor bakfietsen?",
fr: "TODO: fr",
gl: "Este aparcadoiro de bicicletas ten espazo para bicicletas de carga?"
}),
yes: new T({
en: "This parking has room for cargo bikes",
nl: "Deze parking heeft plaats voor bakfietsen",
fr: "TODO: fr",
gl: "Este aparcadoiro ten espazo para bicicletas de carga."
}),
designated: new T({
en: "This parking has designated (official) spots for cargo bikes.",
nl: "Er zijn speciale plaatsen voorzien voor bakfietsen",
fr: "TODO: fr",
gl: "Este aparcadoiro ten espazos designados (oficiais) para bicicletas de carga."
}),
no: new T({
en: "You're not allowed to park cargo bikes",
nl: "Je mag hier geen bakfietsen parkeren",
fr: "TODO: fr",
gl: "Non está permitido aparcar bicicletas de carga"
})
}
},
station: {
name: new T({

View file

@ -0,0 +1,222 @@
{
"id": "bike_parking",
"name": {
"en": "Bike parking",
"nl": "Fietsparking",
"fr": "Parking à vélo",
"gl": "Aparcadoiro de bicicletas"
},
"minzoom": 17,
"overpassTags": {
"and": [
"amenity=bicycle_parking"
]
},
"icon": "./assets/layers/bike_parking/parking.svg",
"size": {
"render": "50,50,bottom"
},
"color": "#00f",
"stroke": "4",
"wayHandling": 1,
"presets": [
{
"title": {
"en": "Bike parking",
"nl": "Fietsparking",
"fr": "Parking à vélo",
"gl": "Aparcadoiro de bicicletas"
},
"tags": [
"amenity=bicycle_parking"
]
}
],
"title": {
"render": {
"en": "Bike parking",
"nl": "Fietsparking",
"fr": "Parking à vélo",
"gl": "Aparcadoiro de bicicletas"
}
},
"tagRenderings": [
"images",
{
"question": {
"en": "What is the type of this bicycle parking?",
"nl": "Van welk type is deze fietsparking?",
"fr": "Quelle type de parking s'agit il?",
"gl": "Que tipo de aparcadoiro de bicicletas é?"
},
"render": {
"en": "This is a bicycle parking of the type: {bicycle_parking}",
"nl": "Dit is een fietsparking van het type: {bicycle_parking}",
"fr": "Ceci est un parking à vélo de type {bicycle_parking}",
"gl": "Este é un aparcadoiro de bicicletas do tipo: {bicycle_parking}"
},
"freeform": {
"key": "bicycle_parking",
"extraTags": [
"fixme=Freeform used on 'bicycle_parking'-tag: possibly a wrong value"
]
},
"mappings": [
{
"if": "bicycle_parking=stands",
"then": {
"en": "Staple racks <img width='150px' src='./assets/layers/bike_parking/staple.svg'>",
"nl": "Nietjes <img width='150px' src='./assets/layers/bike_parking/staple.svg'>",
"fr": "Arceaux <img width='150px' src='./assets/layers/bike_parking/staple.svg'>",
"gl": "De roda (Stands) <img width='150px' src='./assets/layers/bike_parking/staple.svg'>"
}
},
{
"if": "bicycle_parking=wall_loops",
"then": {
"en": "Wheel rack/loops <img width='150px' src='./assets/layers/bike_parking/wall_loops.svg'>",
"nl": "Wielrek/lussen <img width='150px' src='./assets/layers/bike_parking/wall_loops.svg'>",
"fr": "Pinces-roues <img width='150px' src='./assets/layers/bike_parking/wall_loops.svg'>",
"gl": "Aros <img width='150px' src='./assets/layers/bike_parking/wall_loops.svg'>"
}
},
{
"if": "bicycle_parking=handlebar_holder",
"then": {
"en": "Handlebar holder <img width='150px' src='./assets/layers/bike_parking/handlebar_holder.svg'>",
"nl": "Stuurhouder <img width='150px' src='./assets/layers/bike_parking/handlebar_holder.svg'>",
"fr": "Support guidon <img width='150px' src='./assets/layers/bike_parking/handlebar_holder.svg'>",
"gl": "Cadeado para guiador <img width='150px' src='./assets/layers/bike_parking/handlebar_holder.svg'>"
}
},
{
"if": "bicycle_parking=rack",
"then": {
"en": "Rack <img width='150px' src='./assets/layers/bike_parking/rack.svg'>",
"nl": "Rek <img width='150px' src='./assets/layers/bike_parking/rack.svg'>",
"fr": "Râtelier <img width='150px' src='./assets/layers/bike_parking/rack.svg'>",
"gl": "Cremalleira <img width='150px' src='./assets/layers/bike_parking/rack.svg'>"
}
},
{
"if": "bicycle_parking=two_tier",
"then": {
"en": "Two-tiered <img width='150px' src='./assets/layers/bike_parking/two_tier.svg'>",
"nl": "Dubbel (twee verdiepingen) <img width='150px' src='./assets/layers/bike_parking/two_tier.svg'>",
"fr": "Superposé <img width='150px' src='./assets/layers/bike_parking/two_tier.svg'>",
"gl": "Dobre cremalleira <img width='150px' src='./assets/layers/bike_parking/two_tier.svg'>"
}
},
{
"if": "bicycle_parking=shed",
"then": {
"en": "Shed <img width='150px' src='./assets/layers/bike_parking/shed.svg'>",
"nl": "Schuur <img width='150px' src='./assets/layers/bike_parking/shed.svg'>",
"fr": "Abri <img width='150px' src='./assets/layers/bike_parking/shed.svg'>",
"gl": "Abeiro <img width='150px' src='./assets/layers/bike_parking/shed.svg'>"
}
}
]
},
{
"question": {
"en": "Is this parking covered? Also select \"covered\" for indoor parkings.",
"nl": "Is deze parking overdekt? Selecteer ook \"overdekt\" voor fietsparkings binnen een gebouw.",
"gl": "Este aparcadoiro está cuberto? Tamén escolle \"cuberto\" para aparcadoiros interiores."
},
"condition": "bicycle_parking!=shed",
"mappings": [
{
"if": "covered=yes",
"then": {
"en": "This parking is covered (it has a roof)",
"nl": "Deze parking is overdekt (er is een afdak)",
"gl": "Este aparcadoiro está cuberto (ten un teito)"
}
},
{
"if": "covered=no",
"then": {
"en": "This parking is not covered",
"nl": "Deze parking is niet overdekt",
"gl": "Este aparcadoiro non está cuberto"
}
}
]
},
{
"question": {
"en": "How many bicycles fit in this bicycle parking (including possible cargo bicycles)?",
"fr": "Combien de vélos entrent dans ce parking à vélos (y compris les éventuels vélos de transport) ?",
"nl": "Hoeveel fietsen kunnen in deze fietsparking (inclusief potentiëel bakfietsen)?",
"gl": "Cantas bicicletas caben neste aparcadoiro de bicicletas (incluídas as posíbeis bicicletas de carga)?"
},
"render": {
"en": "Place for {capacity} bikes",
"fr": "Place pour {capacity} vélos",
"nl": "Plaats voor {capacity} fietsen",
"gl": "Lugar para {capacity} bicicletas"
},
"freeform": {
"key": "capacity",
"type": "nat"
}
},
{
"question": {
"en": "Does this bicycle parking have spots for cargo bikes?",
"nl": "Heeft deze fietsparking plaats voor bakfietsen?",
"fr": "TODO: fr",
"gl": "Este aparcadoiro de bicicletas ten espazo para bicicletas de carga?"
},
"mappings": [
{
"if": "cargo_bike=yes",
"then": {
"en": "This parking has room for cargo bikes",
"nl": "Deze parking heeft plaats voor bakfietsen",
"fr": "TODO: fr",
"gl": "Este aparcadoiro ten espazo para bicicletas de carga."
}
},
{
"if": "cargo_bike=designated",
"then": {
"en": "This parking has designated (official) spots for cargo bikes.",
"nl": "Er zijn speciale plaatsen voorzien voor bakfietsen",
"fr": "TODO: fr",
"gl": "Este aparcadoiro ten espazos designados (oficiais) para bicicletas de carga."
}
},
{
"if": "cargo_bike=no",
"then": {
"en": "You're not allowed to park cargo bikes",
"nl": "Je mag hier geen bakfietsen parkeren",
"fr": "TODO: fr",
"gl": "Non está permitido aparcar bicicletas de carga"
}
}
]
},
{
"question": {
"en": "How many cargo bicycles fit in this bicycle parking?",
"nl": "Voor hoeveel bakfietsen heeft deze fietsparking plaats?",
"fr": "Combien de vélos de transport entrent dans ce parking à vélos ?",
"gl": "Cantas bicicletas de carga caben neste aparcadoiro de bicicletas?"
},
"render": {
"en": "This parking fits {capacity:cargo_bike} cargo bikes",
"nl": "Deze parking heeft plaats voor {capacity:cargo_bike} fietsen",
"fr": "Ce parking a de la place pour {capacity:cargo_bike} vélos de transport.",
"gl": "Neste aparcadoiro caben {capacity:cargo_bike} bicicletas de carga"
},
"condition": "cargo_bike~designated|yes",
"freeform": {
"key": "capacity:cargo_bike",
"type": "nat"
}
}
]
}

View file

@ -0,0 +1,18 @@
<svg width="97" height="123" viewBox="0 0 97 123" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M52.1412 111.419C50.4633 115.605 44.5366 115.605 42.8588 111.419L24.7014 66.1099C23.385 62.8252 25.8039 59.25 29.3426 59.25L65.6574 59.25C69.1962 59.25 71.615 62.8252 70.2986 66.11L52.1412 111.419Z" fill="#5675DF"/>
<ellipse cx="48.5" cy="47.5" rx="48.5" ry="47.5" fill="#5675DF"/>
<g filter="url(#filter0_d)">
<path d="M42.2812 53.1875V71H36.2812V25.5H53.0625C58.0417 25.5 61.9375 26.7708 64.75 29.3125C67.5833 31.8542 69 35.2188 69 39.4062C69 43.8229 67.6146 47.2292 64.8438 49.625C62.0938 52 58.1458 53.1875 53 53.1875H42.2812ZM42.2812 48.2812H53.0625C56.2708 48.2812 58.7292 47.5312 60.4375 46.0312C62.1458 44.5104 63 42.3229 63 39.4688C63 36.7604 62.1458 34.5938 60.4375 32.9688C58.7292 31.3438 56.3854 30.5 53.4062 30.4375H42.2812V48.2812Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d" x="32.2812" y="25.5" width="40.7188" height="53.5" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,93 @@
<svg width="97" height="123" viewBox="0 0 97 123" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M52.1412 111.419C50.4633 115.605 44.5366 115.605 42.8588 111.419L24.7014 66.1099C23.385 62.8252 25.8039 59.25 29.3426 59.25L65.6574 59.25C69.1962 59.25 71.615 62.8252 70.2986 66.11L52.1412 111.419Z" fill="#5675DF"/>
<ellipse cx="48.5" cy="47.5" rx="48.5" ry="47.5" fill="#5675DF"/>
<g filter="url(#filter0_d)">
<circle cx="39" cy="66" r="2" stroke="white" stroke-width="2"/>
</g>
<g filter="url(#filter1_d)">
<path d="M37.375 67H25L31.75 51.4H38.5H43.375H48.625M48.625 51.4L46.75 47H49.375H52M48.625 51.4L49.375 53.4L50.875 56.6L55 67M48.625 51.4L45.0625 57.4L42.925 61M41.5 63.4L42.925 61M34.375 55.8L38.125 64.6L30.625 47L32.875 51.8M40.375 65.4L42.925 61" stroke="white" stroke-width="2"/>
</g>
<g filter="url(#filter2_d)">
<circle cx="23" cy="67" r="9" stroke="white" stroke-width="2"/>
</g>
<g filter="url(#filter3_d)">
<circle cx="55" cy="67" r="9" stroke="white" stroke-width="2"/>
</g>
<path d="M61 77V59.0476V55.9524L62.4814 54.4851C64.4301 52.5549 67.5699 52.5549 69.5186 54.4851L71 55.9524V58.4286V77" stroke="white" stroke-width="2"/>
<line x1="66" y1="53" x2="66" y2="16" stroke="white" stroke-width="2"/>
<g filter="url(#filter4_d)">
<circle cx="66" cy="23" r="13" fill="white"/>
</g>
<g filter="url(#filter5_d)">
<circle cx="66" cy="23" r="11" fill="#496DEB"/>
</g>
<g filter="url(#filter6_d)">
<path d="M64.1729 24.9902V30H62.4854V17.2031H67.2051C68.6055 17.2031 69.7012 17.5605 70.4922 18.2754C71.2891 18.9902 71.6875 19.9365 71.6875 21.1143C71.6875 22.3564 71.2979 23.3145 70.5186 23.9883C69.7451 24.6562 68.6348 24.9902 67.1875 24.9902H64.1729ZM64.1729 23.6104H67.2051C68.1074 23.6104 68.7988 23.3994 69.2793 22.9775C69.7598 22.5498 70 21.9346 70 21.1318C70 20.3701 69.7598 19.7607 69.2793 19.3037C68.7988 18.8467 68.1396 18.6094 67.3018 18.5918H64.1729V23.6104Z" fill="white"/>
</g>
<line x1="43" y1="78" x2="72" y2="78" stroke="white" stroke-width="2"/>
<defs>
<filter id="filter0_d" x="32" y="63" width="14" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
<filter id="filter1_d" x="19.4777" y="46" width="40.4518" height="30" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
<filter id="filter2_d" x="9" y="57" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
<filter id="filter3_d" x="41" y="57" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
<filter id="filter4_d" x="49" y="10" width="34" height="34" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
<filter id="filter5_d" x="51" y="12" width="30" height="30" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
<filter id="filter6_d" x="58.4854" y="17.2031" width="17.2021" height="20.7969" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -33,7 +33,17 @@
"en": "Artwork",
"nl": "Kunstwerk",
"fr": "Oeuvre d'art"
}
},
"mappings": [
{
"if": "name~*",
"then": {
"en": "Artwork <i>{name}</i>",
"nl": "Kunstwerk <i>{name}</i>",
"fr": "Oeuvre d'art <i>{name}</i>"
}
}
]
},
"icon": {
"render": "./assets/themes/artwork/artwork.svg"
@ -177,22 +187,6 @@
}
]
},
{
"question": {
"en": "Which wikidata-entry corresponds with <b>this artwork</b>?",
"fr": "Quelle entrée wikidata correspond à <b>cette œuvre d'art</b> ?",
"nl": "Welk wikidata-item beschrijft dit kunstwerk?"
},
"render": {
"en": "Corresponds with <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"nl": "Komt overeen met <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"fr": "Correspond à <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>"
},
"freeform": {
"key": "wikidata",
"type": "wikidata"
}
},
{
"question": {
"en": "Which artist created this?",
@ -223,6 +217,22 @@
"key": "website",
"type": "url"
}
},
{
"question": {
"en": "Which wikidata-entry corresponds with <b>this artwork</b>?",
"fr": "Quelle entrée wikidata correspond à <b>cette œuvre d'art</b> ?",
"nl": "Welk wikidata-item beschrijft dit kunstwerk?"
},
"render": {
"en": "Corresponds with <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"nl": "Komt overeen met <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"fr": "Correspond à <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>"
},
"freeform": {
"key": "wikidata",
"type": "wikidata"
}
}
]
}

View file

@ -10,11 +10,10 @@ import {GenerateEmpty} from "./UI/CustomGenerator/GenerateEmpty";
import PageSplit from "./UI/Base/PageSplit";
import HelpText from "./Customizations/HelpText";
import {TagRendering} from "./Customizations/TagRendering";
import {FromJSON} from "./Customizations/JSON/FromJSON";
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson";
let layout = GenerateEmpty.createTestLayout();
let layout = GenerateEmpty.createEmptyLayout();
if(window.location.hash.length > 10){
layout = JSON.parse(atob(window.location.hash.substr(1))) as LayoutConfigJson;
}
@ -66,6 +65,6 @@ new TabbedComponent([
header: "<img src='./assets/share.svg'>",
content: new SharePanel(es, liveUrl)
}
], 1).SetClass("main-tabs")
]).SetClass("main-tabs")
.AttachTo("maindiv");

View file

@ -8,7 +8,7 @@ rm -rf /home/pietervdvn/git/pietervdvn.github.io/MapComplete/*
cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/MapComplete/
cd /home/pietervdvn/git/pietervdvn.github.io/MapComplete/
# git add . && git commit -m "New mapcomplete version" &&git push
git add . && git commit -m "New mapcomplete version" &&git push
cd -
# clean up the mess we made
# rm *.js

View file

@ -1235,6 +1235,7 @@
padding: 1em;
display: inline-block;
width: 100%;
box-sizing: border-box;
}
.tab-single-header {