Port bicycle cafés to JSON-defined-layer

This commit is contained in:
Pieter Vander Vennet 2020-09-11 02:14:16 +02:00
parent 3653c3ecaa
commit 0c2f662040
17 changed files with 229 additions and 276 deletions

View file

@ -47,6 +47,10 @@ export class AllKnownLayouts {
let layer = layout.layers[i]; let layer = layout.layers[i];
if (typeof (layer) === "string") { if (typeof (layer) === "string") {
layer = layout.layers[i] = FromJSON.sharedLayers.get(layer); layer = layout.layers[i] = FromJSON.sharedLayers.get(layer);
if(layer === undefined){
console.log("Defined layers are ", FromJSON.sharedLayers.keys())
throw `Layer ${layer} was not found or defined - probably a type was made`
}
} }

View file

@ -16,6 +16,7 @@ import * as bike_parking from "../../assets/layers/bike_parking/bike_parking.jso
import * as bike_repair_station from "../../assets/layers/bike_repair_station/bike_repair_station.json" import * as bike_repair_station from "../../assets/layers/bike_repair_station/bike_repair_station.json"
import * as birdhides from "../../assets/layers/bird_hide/birdhides.json" import * as birdhides from "../../assets/layers/bird_hide/birdhides.json"
import * as nature_reserve from "../../assets/layers/nature_reserve/nature_reserve.json" import * as nature_reserve from "../../assets/layers/nature_reserve/nature_reserve.json"
import * as bike_cafes from "../../assets/layers/bike_cafe/bike_cafes.json"
import {Utils} from "../../Utils"; import {Utils} from "../../Utils";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload"; import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
@ -41,6 +42,7 @@ export class FromJSON {
FromJSON.Layer(bike_repair_station), FromJSON.Layer(bike_repair_station),
FromJSON.Layer(birdhides), FromJSON.Layer(birdhides),
FromJSON.Layer(nature_reserve), FromJSON.Layer(nature_reserve),
FromJSON.Layer(bike_cafes),
]; ];
for (const layer of sharedLayersList) { for (const layer of sharedLayersList) {

View file

@ -1,65 +0,0 @@
import {LayerDefinition} from "../LayerDefinition";
import FixedText from "../Questions/FixedText";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
import Translations from "../../UI/i18n/Translations";
import CafeName from "../Questions/bike/CafeName";
import {And, Or, RegexTag, Tag} from "../../Logic/Tags";
import {PhoneNumberQuestion} from "../Questions/PhoneNumberQuestion";
import Website from "../Questions/Website";
import CafeRepair from "../Questions/bike/CafeRepair";
import CafeDiy from "../Questions/bike/CafeDiy";
import CafePump from "../Questions/bike/CafePump";
import {EmailQuestion} from "../Questions/EmailQuestion";
export default class BikeCafes extends LayerDefinition {
private readonly to = Translations.t.cyclofix.cafe
constructor() {
super("bikecafe")
this.name = this.to.name
this.icon = "./assets/bike/cafe.svg"
this.overpassFilter = new And([
new RegexTag(/^amenity$/, /^pub|bar|cafe$/),
new Or([
new RegexTag(/^service:bicycle:/, /.*/),
new RegexTag(/^pub$/, /^cycling|bicycle$/),
new RegexTag(/^theme$/, /^cycling|bicycle$/),
])
])
this.presets = [
{
title: Translations.t.cyclofix.cafe.title,
tags : [
new Tag("amenity", "pub"),
new Tag("pub", "cycling"),
]
}
]
this.maxAllowedOverlapPercentage = 10;
this.minzoom = 13
this.style = () => ({
color: "#00bb00",
icon: {
iconUrl: "./assets/bike/cafe.svg",
iconSize: [50, 50],
iconAnchor: [25, 50]
}
});
this.title = new FixedText(this.to.title)
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
new CafeName(),
new Website("{name}"),
new PhoneNumberQuestion("{name}"),
new EmailQuestion("{name}"),
new CafeRepair(),
new CafeDiy(),
new CafePump()
]
this.wayHandling = LayerDefinition.WAYHANDLING_CENTER_AND_WAY
}
}

View file

@ -3,7 +3,6 @@ import BikeShops from "../Layers/BikeShops";
import Translations from "../../UI/i18n/Translations"; import Translations from "../../UI/i18n/Translations";
import Combine from "../../UI/Base/Combine"; import Combine from "../../UI/Base/Combine";
import BikeOtherShops from "../Layers/BikeOtherShops"; import BikeOtherShops from "../Layers/BikeOtherShops";
import BikeCafes from "../Layers/BikeCafes";
export default class Cyclofix extends Layout { export default class Cyclofix extends Layout {
@ -21,8 +20,8 @@ export default class Cyclofix extends Layout {
"cyclofix", "cyclofix",
["en", "nl", "fr", "gl"], ["en", "nl", "fr", "gl"],
Translations.t.cyclofix.title, Translations.t.cyclofix.title,
["bike_repair_station", new BikeShops(), "drinking_water", "bike_parking", new BikeOtherShops(), new BikeCafes(), ["bike_repair_station", "bike_cafes", new BikeShops(), "drinking_water", "bike_parking", new BikeOtherShops(),
// The first of november, we remember our dead // The first of november, halloween and the second of november, we remember our dead
...(Cyclofix.RememberTheDead() ? ["ghost_bike"] : [])], ...(Cyclofix.RememberTheDead() ? ["ghost_bike"] : [])],
16, 16,
50.8465573, 50.8465573,

View file

@ -1,18 +0,0 @@
import {Tag} from "../../../Logic/Tags";
import Translations from "../../../UI/i18n/Translations";
import {TagRenderingOptions} from "../../TagRenderingOptions";
export default class CafeDiy extends TagRenderingOptions {
constructor() {
const key = 'service:bicycle:diy'
const to = Translations.t.cyclofix.cafe.diy
super({
question: to.question,
mappings: [
{k: new Tag(key, "yes"), txt: to.yes},
{k: new Tag(key, "no"), txt: to.no},
]
});
}
}

View file

@ -1,17 +0,0 @@
import Translations from "../../../UI/i18n/Translations";
import {TagRenderingOptions} from "../../TagRenderingOptions";
export default class CafeName extends TagRenderingOptions {
constructor() {
const to = Translations.t.cyclofix.cafe.qName
super({
question: to.question,
freeform: {
key: "name",
renderTemplate: to.render,
template: to.template
}
})
}
}

View file

@ -1,18 +0,0 @@
import {Tag} from "../../../Logic/Tags";
import Translations from "../../../UI/i18n/Translations";
import {TagRenderingOptions} from "../../TagRenderingOptions";
export default class CafePump extends TagRenderingOptions {
constructor() {
const key = 'service:bicycle:pump'
const to = Translations.t.cyclofix.cafe.pump
super({
question: to.question,
mappings: [
{k: new Tag(key, "yes"), txt: to.yes},
{k: new Tag(key, "no"), txt: to.no},
]
});
}
}

View file

@ -1,18 +0,0 @@
import {Tag} from "../../../Logic/Tags";
import Translations from "../../../UI/i18n/Translations";
import {TagRenderingOptions} from "../../TagRenderingOptions";
export default class CafeRepair extends TagRenderingOptions {
constructor() {
const key = 'service:bicycle:repair'
const to = Translations.t.cyclofix.cafe.repair
super({
question: to.question,
mappings: [
{k: new Tag(key, "yes"), txt: to.yes},
{k: new Tag(key, "no"), txt: to.no}
]
});
}
}

View file

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

View file

@ -1,27 +0,0 @@
import {Tag, And} from "../../../Logic/Tags";
import Translations from "../../../UI/i18n/Translations";
import {TagRenderingOptions} from "../../TagRenderingOptions";
export default class ParkingOperator extends TagRenderingOptions {
constructor() {
const to = Translations.t.cyclofix.parking.operator
super({
priority: 15,
question: to.question.Render(),
freeform: {
key: "operator",
template: to.template,
renderTemplate: to.render,
placeholder: Translations.t.cyclofix.freeFormPlaceholder
},
mappings: [
{k: new Tag("operator", "KU Leuven"), txt: "KU Leuven"},
{k: new Tag("operator", "Stad Halle"), txt: "Stad Halle"},
{k: new Tag("operator", "Saint Gilles - Sint Gillis"), txt: "Saint Gilles - Sint Gillis"},
{k: new Tag("operator", "Jette"), txt: "Jette"},
{k: new And([new Tag("operator", ""), new Tag("operator:type", "private")]), txt: to.private.Render()}
]
});
}
}

View file

@ -29,7 +29,10 @@ export class RegexTag extends TagsFilter {
} }
asOverpass(): string[] { asOverpass(): string[] {
return [`['${RegexTag.source(this.key)}'${this.invert ? "!" : ""}~'${RegexTag.source(this.value)}']`]; if (typeof this.key === "string") {
return [`['${this.key}'${this.invert ? "!" : ""}~'${RegexTag.source(this.value)}']`];
}
return [`[~'${this.key.source}'${this.invert ? "!" : ""}~'${RegexTag.source(this.value)}']`];
} }
private static doesMatch(fromTag: string, possibleRegex: string | RegExp): boolean { private static doesMatch(fromTag: string, possibleRegex: string | RegExp): boolean {
@ -68,7 +71,7 @@ export class RegexTag extends TagsFilter {
if (typeof this.key === "string") { if (typeof this.key === "string") {
return `${this.key}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`; return `${this.key}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`;
} }
return `~${this.key.source}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}` return `${this.key.source}${this.invert ? "!" : ""}~~${RegexTag.source(this.value)}`
} }
isEquivalent(other: TagsFilter): boolean { isEquivalent(other: TagsFilter): boolean {

View file

@ -12,7 +12,6 @@ import {LayerUpdater} from "./Logic/LayerUpdater";
import {UIEventSource} from "./Logic/UIEventSource"; import {UIEventSource} from "./Logic/UIEventSource";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
import {QueryParameters} from "./Logic/Web/QueryParameters"; import {QueryParameters} from "./Logic/Web/QueryParameters";
import {FromJSON} from "./Customizations/JSON/FromJSON";
/** /**
* Contains the global state: a bunch of UI-event sources * Contains the global state: a bunch of UI-event sources
@ -23,7 +22,7 @@ export class State {
// The singleton of the global state // The singleton of the global state
public static state: State; public static state: State;
public static vNumber = "0.0.7k"; public static vNumber = "0.0.7l";
// The user journey states thresholds when a new feature gets unlocked // The user journey states thresholds when a new feature gets unlocked
public static userJourney = { public static userJourney = {

View file

@ -43,14 +43,14 @@ export class ValidatedTextField {
"email": (str) => EmailValidator.validate(str), "email": (str) => EmailValidator.validate(str),
"url": (str) => str, "url": (str) => str,
"phone": (str, country) => { "phone": (str, country) => {
return parsePhoneNumberFromString(str, country.toUpperCase())?.isValid() ?? false; return parsePhoneNumberFromString(str, country?.toUpperCase())?.isValid() ?? false;
} }
} }
public static formatting = { public static formatting = {
"phone": (str, country) => { "phone": (str, country) => {
console.log("country formatting", country) console.log("country formatting", country)
return parsePhoneNumberFromString(str, country.toUpperCase()).formatInternational() return parsePhoneNumberFromString(str, country?.toUpperCase()).formatInternational()
} }
} }
} }

View file

@ -290,90 +290,6 @@ export default class Translations {
}), }),
} }
}, },
cafe: {
name: new T({en: "Bike cafe", nl: "Fietscafé", fr: "Café vélo", gl: "Café de ciclistas"}),
title: new T({en: "Bike cafe", nl: "Fietscafé", fr: "Café Vélo", gl: "Café de ciclistas"}),
qName: {
question: new T({
en: "What is the name of this bike cafe?",
nl: "Wat is de naam van dit fietscafé?",
fr: "Quel est le nom de ce Café vélo",
gl: "Cal é o nome deste café de ciclistas?"
}),
render: new T({
en: "This bike cafe is called {name}",
nl: "Dit fietscafé heet {name}",
fr: "Ce Café vélo s'appelle {name}",
gl: "Este café de ciclistas chámase {name}"
}),
template: new T({
en: "This bike cafe is called: $$$",
nl: "Dit fietscafé heet: <b>$$$</b>",
fr: "Ce Café vélo s'appelle $$$",
gl: "Este café de ciclistas chámase: $$$"
})
},
repair: {
question: new T({
en: "Does this bike cafe repair bikes?",
nl: "Verkoopt dit fietscafé fietsen?",
fr: "Est-ce que ce Café vélo répare les vélos?",
gl: "Este café de ciclistas arranxa bicicletas?"
}),
yes: new T({
en: "This bike cafe repairs bikes",
nl: "Dit fietscafé herstelt fietsen",
fr: "Ce Café vélo répare les vélos",
gl: "Este café de ciclistas arranxa bicicletas"
}),
no: new T({
en: "This bike cafe doesn't repair bikes",
nl: "Dit fietscafé herstelt geen fietsen",
fr: "Ce Café vélo ne répare pas les vélos",
gl: "Este café de ciclistas non arranxa bicicletas"
})
},
pump: {
question: new T({
en: "Does this bike cafe offer a bike pump for use by anyone?",
nl: "Biedt dit fietscafé een fietspomp aan voor iedereen?",
fr: "Est-ce que ce Café vélo propose une pompe en libre accès",
gl: "Este café de ciclistas ofrece unha bomba de ar para que calquera persoa poida usala?"
}),
yes: new T({
en: "This bike cafe offers a bike pump for anyone",
nl: "Dit fietscafé biedt geen fietspomp aan voor eender wie",
fr: "Ce Café vélo offre une pompe en libre accès",
gl: "Este café de ciclistas ofrece unha bomba de ar"
}),
no: new T({
en: "This bike cafe doesn't offer a bike pump for anyone",
nl: "Dit fietscafé biedt een fietspomp aan voor iedereen",
fr: "Ce Café vélo n'offre pas de pompe en libre accès",
gl: "Este café de ciclistas non ofrece unha bomba de ar"
})
},
diy: {
question: new T({
en: "Are there tools here to repair your own bike?",
nl: "Biedt dit fietscafé gereedschap aan om je fiets zelf te herstellen?",
fr: "Est-ce qu'il y a des outils pour réparer soi-même son vélo?",
gl: "Hai ferramentas aquí para arranxar a túa propia bicicleta?"
}),
yes: new T({
en: "This bike cafe offers tools for DIY repair",
nl: "Dit fietscafé biedt gereedschap aan om je fiets zelf te herstellen",
fr: "Ce Café vélo propose des outils pour réparer son vélo soi-même",
gl: "Hai ferramentas aquí para arranxar a túa propia bicicleta"
}),
no: new T({
en: "This bike cafe doesn't offer tools for DIY repair",
nl: "Dit fietscafé biedt geen gereedschap aan om je fiets zelf te herstellen",
fr: "Ce Café vélo ne propose pas d'outils pour réparer son vélo soi-même",
gl: "Non hai ferramentas aquí para arranxar a túa propia bicicleta"
})
}
},
nonBikeShop: { nonBikeShop: {
name: new T({ name: new T({
en: "shop that sells/repairs bikes", en: "shop that sells/repairs bikes",

View file

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -0,0 +1,213 @@
{
"id": "bike_cafes",
"name": {
"en": "Bike cafe",
"nl": "Fietscafé",
"fr": "Café vélo",
"gl": "Café de ciclistas"
},
"minzoom": 12,
"overpassTags": {
"and": [
"amenity~pub|bar|cafe",
{
"#": "Note the double tilde in 'service:bicycle' which interprets the key as regex too",
"or": [
"pub~cycling|bicycle",
"theme~cycling|bicycle",
"service:bicycle:.*~~*"
]
}
]
},
"title": {
"render": {
"en": "Bike cafe",
"nl": "Fietscafé",
"fr": "Café Vélo",
"gl": "Café de ciclistas"
},
"mappings": [
{
"if": "name~*",
"then": {
"en": "Bike cafe <i>{name}</i>",
"nl": "Fietscafé <i>{name}</i>",
"fr": "Café Vélo <i>{name}</i>",
"gl": "Café de ciclistas <i>{name}</i>"
}
}
]
},
"description": {},
"tagRenderings": [
{
"question": {
"en": "What is the name of this bike cafe?",
"nl": "Wat is de naam van dit fietscafé?",
"fr": "Quel est le nom de ce Café vélo",
"gl": "Cal é o nome deste café de ciclistas?"
},
"render": {
"en": "This bike cafe is called {name}",
"nl": "Dit fietscafé heet {name}",
"fr": "Ce Café vélo s'appelle {name}",
"gl": "Este café de ciclistas chámase {name}"
},
"freeform": {
"key": "name"
}
},
{
"question": {
"en": "Does this bike cafe offer a bike pump for use by anyone?",
"nl": "Biedt dit fietscafé een fietspomp aan voor iedereen?",
"fr": "Est-ce que ce Café vélo propose une pompe en libre accès",
"gl": "Este café de ciclistas ofrece unha bomba de ar para que calquera persoa poida usala?"
},
"mappings": [
{
"if": "service:bicycle:pump=yes",
"then": {
"en": "This bike cafe offers a bike pump for anyone",
"nl": "Dit fietscafé biedt een fietspomp aan voor eender wie",
"fr": "Ce Café vélo offre une pompe en libre accès",
"gl": "Este café de ciclistas ofrece unha bomba de ar"
}
},
{
"if": "service:bicycle:pump=no",
"then": {
"en": "This bike cafe doesn't offer a bike pump for anyone",
"nl": "Dit fietscafé biedt geen fietspomp aan voor iedereen",
"fr": "Ce Café vélo n'offre pas de pompe en libre accès",
"gl": "Este café de ciclistas non ofrece unha bomba de ar"
}
}
]
},
{
"question": {
"en": "Are there tools here to repair your own bike?",
"nl": "Biedt dit fietscafé gereedschap aan om je fiets zelf te herstellen?",
"fr": "Est-ce qu'il y a des outils pour réparer soi-même son vélo?",
"gl": "Hai ferramentas aquí para arranxar a túa propia bicicleta?"
},
"mappings": [
{
"if": "service:bicycle:diy=yes",
"then": {
"en": "This bike cafe offers tools for DIY repair",
"nl": "Dit fietscafé biedt gereedschap aan om je fiets zelf te herstellen",
"fr": "Ce Café vélo propose des outils pour réparer son vélo soi-même",
"gl": "Hai ferramentas aquí para arranxar a túa propia bicicleta"
}
},
{
"if": "service:bicycle:diy=no",
"then": {
"en": "This bike cafe doesn't offer tools for DIY repair",
"nl": "Dit fietscafé biedt geen gereedschap aan om je fiets zelf te herstellen",
"fr": "Ce Café vélo ne propose pas d'outils pour réparer son vélo soi-même",
"gl": "Non hai ferramentas aquí para arranxar a túa propia bicicleta"
}
}
]
},
{
"question": {
"en": "Does this bike cafe repair bikes?",
"nl": "Herstelt dit fietscafé fietsen?",
"fr": "Est-ce que ce Café vélo répare les vélos?",
"gl": "Este café de ciclistas arranxa bicicletas?"
},
"mappings": [
{
"if": "service:bicycle:repair=yes",
"then": {
"en": "This bike cafe repairs bikes",
"nl": "Dit fietscafé herstelt fietsen",
"fr": "Ce Café vélo répare les vélos",
"gl": "Este café de ciclistas arranxa bicicletas"
}
},
{
"if": "service:bicycle:repair=no",
"then": {
"en": "This bike cafe doesn't repair bikes",
"nl": "Dit fietscafé herstelt geen fietsen",
"fr": "Ce Café vélo ne répare pas les vélos",
"gl": "Este café de ciclistas non arranxa bicicletas"
}
}
]
},
{
"question": {
"en": "What is the website of {name}?",
"nl": "Wat is de website van {name}?",
"fr": "Quel est le site internet de {name}?",
"gl": "Cal é a páxina web de {name}?"
},
"render": {
"en": "<a href='{website}' target='_blank'>{website}</a>",
"nl": "<a href='{website}' target='_blank'>{website}</a>",
"fr": "<a href='{website}' target='_blank'>{website}</a>",
"gl": "<a href='{website}' target='_blank'>{website}</a>"
},
"freeform": {
"key": "website"
}
},
{
"question": {
"en": "What is the phone number of {name}?",
"nl": "Wat is het telefoonnummer van {name}?",
"fr": "Quel est le nom de {name}?",
"gl": "Cal é o número de teléfono de {name}?"
},
"render": "<a href='tel:{phone}'>{phone}</a>",
"freeform": {
"key": "phone",
"type": "phone"
}
},
{
"question": {
"en": "What is the email address of {name}?",
"nl": "Wat is het email-adres van {name}?",
"fr": "Quel est l'adresse email de {name}?",
"gl": "Cal é o enderezo de correo electrónico de {name}?"
},
"render": "<a href='mailto:{email}' target='_blank'>{email}</a>"
}
],
"hideUnderlayingFeaturesMinPercentage": 0,
"icon": {
"render": "./assets/layers/bike_cafe/bike_cafe.svg"
},
"width": {
"render": "2"
},
"iconSize": {
"render": "50,50,bottom"
},
"color": {
"render": "#694E2D"
},
"presets": [
{
"title": {
"en": "Bike cafe",
"nl": "Fietscafé",
"fr": "Café Vélo",
"gl": "Café de ciclistas"
},
"tags": [
"amenity=pub",
"pub=cycling"
]
}
],
"wayHandling": 2
}

View file

@ -1,4 +1,3 @@
import BikeCafes from "./Customizations/Layers/BikeCafes";
import {CheckBoxes} from "./UI/Input/Checkboxes"; import {CheckBoxes} from "./UI/Input/Checkboxes";
import {FixedInputElement} from "./UI/Input/FixedInputElement"; import {FixedInputElement} from "./UI/Input/FixedInputElement";
import {VariableUiElement} from "./UI/Base/VariableUIElement"; import {VariableUiElement} from "./UI/Base/VariableUIElement";