Merge branches

This commit is contained in:
Pieter Vander Vennet 2020-07-16 09:54:32 +02:00
commit c1c11ed906
33 changed files with 568 additions and 130 deletions

View file

@ -5,13 +5,14 @@ import {Statues} from "./Layouts/Statues";
import {Bookcases} from "./Layouts/Bookcases";
import Cyclofix from "./Layouts/Cyclofix";
import {All} from "./Layouts/All";
import {Layout} from "./Layout";
export class AllKnownLayouts {
public static allSets: any = AllKnownLayouts.AllLayouts();
private static AllLayouts() {
private static AllLayouts() : any{
const all = new All();
const layouts = [
const layouts : Layout[] = [
new Groen(),
new GRB(),
new Cyclofix(),

View file

@ -16,9 +16,35 @@ export class LayerDefinition {
* This name is shown in the 'add XXX button'
*/
name: string;
/**
* These tags are added whenever a new point is added by the user on the map.
* This is the ideal place to add extra info, such as "fixme=added by MapComplete, geometry should be checked"
*/
newElementTags: Tag[]
/**
* Not really used anymore
* This is meant to serve as icon in the buttons
*/
icon: string;
/**
* Only show this layer starting at this zoom level
*/
minzoom: number;
/**
* This tagfilter is used to query overpass.
* Examples are:
*
* new Tag("amenity","drinking_water")
*
* or a query for bicycle pumps which have two tagging schemes:
* new Or([
* new Tag("service:bicycle:pump","yes") ,
* new And([
* new Tag("amenity","compressed_air"),
* new Tag("bicycle","yes")])
* ])
*/
overpassFilter: TagsFilter;
/**

View file

@ -2,8 +2,9 @@ import {LayerDefinition} from "../LayerDefinition";
import {And, Or, Tag} from "../../Logic/TagsFilter";
import {OperatorTag} from "../Questions/OperatorTag";
import * as L from "leaflet";
import FixedName from "../Questions/FixedName";
import FixedText from "../Questions/FixedText";
import { BikeParkingType } from "../Questions/BikeParkingType";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
export class BikeParkings extends LayerDefinition {
@ -19,8 +20,9 @@ export class BikeParkings extends LayerDefinition {
this.minzoom = 13;
this.style = this.generateStyleFunction();
this.title = new FixedName("Fietsparking");
this.title = new FixedText("Fietsparking");
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
new OperatorTag(),
new BikeParkingType()
];

View file

@ -1,13 +1,15 @@
import {LayerDefinition} from "../LayerDefinition";
import {And, Tag} from "../../Logic/TagsFilter";
import * as L from "leaflet";
import FixedName from "../Questions/FixedName";
import BikeStationChain from "../Questions/BikeStationChain";
import BikeStationPumpTools from "../Questions/BikeStationPumpTools";
import BikeStationStand from "../Questions/BikeStationStand";
import PumpManual from "../Questions/PumpManual";
import BikeStationOperator from "../Questions/BikeStationOperator";
import BikeStationBrand from "../Questions/BikeStationBrand";
import FixedText from "../Questions/FixedText";
import {BikePumpManometer} from "../Questions/BikePumpManometer";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
export default class BikeServices extends LayerDefinition {
constructor() {
@ -27,12 +29,22 @@ export default class BikeServices extends LayerDefinition {
this.minzoom = 13;
this.style = this.generateStyleFunction();
this.title = new FixedName("Bike station");
this.title = new FixedText("Bike station");
const pump = new Tag("service:bicycle:pump", "yes");
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
new BikeStationPumpTools(),
new BikeStationChain().OnlyShowIf(new Tag("service:bicycle:tools", "yes")),
new BikeStationStand().OnlyShowIf(new Tag("service:bicycle:tools", "yes")),
new PumpManual().OnlyShowIf(new Tag("service:bicycle:pump", "yes")),
new PumpManual().OnlyShowIf(pump),
new BikePumpManometer().OnlyShowIf(pump),
new BikeStationOperator(),
new BikeStationBrand()
];

View file

@ -145,7 +145,7 @@ export class Bookcases extends LayerDefinition {
new TagRenderingOptions({
freeform: {
key: "description",
renderTemplate: "<b>Beschrijving door de uitbater</b><br>{description}",
renderTemplate: "<b>Beschrijving door de uitbater:</b><br>{description}",
template: "$$$",
}
})

View file

@ -7,6 +7,7 @@ import {TagRenderingOptions} from "../TagRendering";
import {NameQuestion} from "../Questions/NameQuestion";
import {NameInline} from "../Questions/NameInline";
import {DescriptionQuestion} from "../Questions/DescriptionQuestion";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
export class Bos extends LayerDefinition {
@ -33,6 +34,7 @@ export class Bos extends LayerDefinition {
this.style = this.generateStyleFunction();
this.title = new NameInline("bos");
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
new NameQuestion(),
new AccessTag(),
new OperatorTag(),

View file

@ -0,0 +1,69 @@
import {LayerDefinition} from "../LayerDefinition";
import {Tag} from "../../Logic/TagsFilter";
import {FixedUiElement} from "../../UI/Base/FixedUiElement";
import {TagRenderingOptions} from "../TagRendering";
import FixedText from "../Questions/FixedText";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import L from "leaflet";
export class GhostBike extends LayerDefinition {
constructor() {
super();
this.name = "ghost bike";
this.overpassFilter = new Tag("memorial", "ghost_bike")
this.title = new FixedText("Ghost bike");
this.elementsToShow = [
new FixedText("A <b>ghost bike</b> is a memorial for a cyclist who died in a traffic accident," +
" in the form of a white bicycle placed permanently near the accident location."),
new ImageCarouselWithUploadConstructor(),
new TagRenderingOptions({
question: "Whom is remembered by this ghost bike?" +
"<span class='question-subtext'>" +
"<br/>" +
"Please respect privacy - only fill out the name if it is widely published or marked on the cycle." +
"</span>",
mappings: [{k: new Tag("noname", "yes"), txt: "There is no name marked on the bike"},],
freeform: {
key: "name",
extraTags: new Tag("noname", ""),
template: "$$$",
renderTemplate: "In the remembrance of <b>{name}</b>",
}
}),
new TagRenderingOptions({
question: "When was the ghost bike installed?",
freeform: {
key: "start_date",
template: "The ghost bike was placed on $$$", // TODO create a date picker
renderTemplate: "The ghost bike was placed on <b>{start_date}</b>",
}
}),
new TagRenderingOptions({
question: "On what URL can more information be found?" +
"<span class='question-subtext'>If available, add a link to a news report about the accident or about the placing of the ghost bike</span>",
freeform: {
key: "source",
template: "More information available on $$$",
renderTemplate: "<a href='{source}' target='_blank'>More information</a>",
}
}),
];
this.style = (tags: any) => {
return {
color: "#000000",
icon: L.icon({
iconUrl: 'assets/ghost_bike.svg',
iconSize: [40, 40],
iconAnchor: [20, 20],
})
}
};
}
}

View file

@ -6,6 +6,7 @@ import {OperatorTag} from "../Questions/OperatorTag";
import {NameQuestion} from "../Questions/NameQuestion";
import {NameInline} from "../Questions/NameInline";
import {DescriptionQuestion} from "../Questions/DescriptionQuestion";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
export class NatureReserves extends LayerDefinition {
@ -23,6 +24,7 @@ export class NatureReserves extends LayerDefinition {
this.title = new NameInline("natuurreservaat");
this.style = this.generateStyleFunction();
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
new NameQuestion(),
new AccessTag(),
new OperatorTag(),

View file

@ -7,6 +7,7 @@ import {TagRenderingOptions} from "../TagRendering";
import {NameQuestion} from "../Questions/NameQuestion";
import {NameInline} from "../Questions/NameInline";
import {DescriptionQuestion} from "../Questions/DescriptionQuestion";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
export class Park extends LayerDefinition {
@ -58,6 +59,7 @@ export class Park extends LayerDefinition {
this.style = this.generateStyleFunction();
this.title = new NameInline("park");
this.elementsToShow = [
new ImageCarouselWithUploadConstructor(),
new NameQuestion(),
this.accessByDefault,
this.operatorByDefault,

View file

@ -16,7 +16,21 @@ export class Layout {
public startLat: number;
public welcomeTail: string;
public locationContains: string[];
/**
*
* @param name: The name used in the query string. If in the query "quests=<name>" is defined, it will select this layout
* @param title: Will be used in the <title> of the page
* @param layers: The layers to show, a list of LayerDefinitions
* @param startzoom: The initial starting zoom of the map
* @param startLat:The initial starting latitude of the map
* @param startLon: the initial starting longitude of the map
* @param welcomeMessage: This message is shown in the collapsable box on the left
* @param gettingStartedPlzLogin: This is shown below the welcomemessage and wrapped in a login link.
* @param welcomeBackMessage: This is shown when the user is logged in
* @param welcomeTail: This text is shown below the login message. It is ideal for extra help
*/
constructor(
name: string,
title: string,
@ -25,8 +39,8 @@ export class Layout {
startLat: number,
startLon: number,
welcomeMessage: string,
gettingStartedPlzLogin: string,
welcomeBackMessage: string,
gettingStartedPlzLogin: string = "Please login to get started",
welcomeBackMessage: string = "You are logged in. Welcome back!",
welcomeTail: string = ""
) {
this.title = title;
@ -41,13 +55,5 @@ export class Layout {
this.welcomeTail = welcomeTail;
}
/*
static statues = new Layout(
);
*/
}

View file

@ -23,5 +23,6 @@ export class Bookcases extends Layout{
" </a> of door je " +
" <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">aan te melden</span>.</p>",
"Klik op een boekenruilkastje om vragen te beantwoorden");
this.locationContains= ["Bookcases.html", "Bookcase.html","bookcase"]
}
}

View file

@ -2,13 +2,15 @@ import {Layout} from "../Layout";
import {GrbToFix} from "../Layers/GrbToFix";
import { BikeParkings } from "../Layers/BikeParkings";
import BikeServices from "../Layers/BikeServices";
import {GhostBike} from "../Layers/GhostBike";
export default class Cyclofix extends Layout {
constructor() {
super(
"pomp",
"Cyclofix bicycle infrastructure",
[new BikeParkings(), new BikeServices()],
// [new BikePumps()],
[new GhostBike(), new BikeParkings(), new BikeServices()],
16,
50.8465573,
4.3516970,

View file

@ -47,5 +47,7 @@ export class Groen extends Layout {
"Als je inlogt, komt er een tweede cookie bij met je inloggegevens." +
"</small>"
);
this.locationContains = ["buurtnatuur.be"]
}
}

View file

@ -0,0 +1,20 @@
import {TagRenderingOptions} from "../TagRendering";
import {Tag} from "../../Logic/TagsFilter";
export class BikePumpManometer extends TagRenderingOptions{
constructor() {
super({
question: "Does the pump have a pressure indicator or manometer?",
mappings: [
{k: new Tag("manometer", "yes"), txt: "Yes, there is a manometer"},
{k: new Tag("manometer","broken"), txt: "Yes, but it is broken"},
{k: new Tag("manometer", "yes"), txt: "No"}
]
});
}
}

View file

@ -0,0 +1,24 @@
import {TagRenderingOptions} from "../TagRendering";
import {Tag} from "../../Logic/TagsFilter";
export class BikePumpValves extends TagRenderingOptions{
constructor() {
super({
question: "What valves are supported?",
mappings: [
{
k: new Tag("valves", " sclaverand;schrader;dunlop"),
txt: "There is a default head, so Presta, Dunlop and Auto"
},
{k: new Tag("valves", "dunlop"), txt: "Only dunlop"},
{k: new Tag("valves", "sclaverand"), txt: "Only Sclaverand (also known as Dunlop)"},
{k: new Tag("valves", "auto"), txt: "Only auto"},
],
freeform: {
key: "valves",
template: "Supported valves are $$$",
renderTemplate: "Supported valves are {valves}"
}
});
}
}

View file

@ -15,6 +15,7 @@ export default class BikeStationOperator extends TagRenderingOptions {
{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 Tag("operator", "private"), txt: "Beheer door een privépersoon"}
]
}

View file

@ -7,6 +7,7 @@ export class DescriptionQuestion extends TagRenderingOptions{
super({
question: "Zijn er bijzonderheden die we moeten weten over dit "+category+"?<br>" +
"<span class='question-subtext'>Je hoeft niet te herhalen wat je net hebt aangeduid.<br/>" +
"Een <i>naam</i> wordt in de volgende stap gevraagd.<br/>" +
"Voel je vrij om dit veld over te slaan.</span>",
freeform:{
key:"description:0",

View file

@ -1,6 +1,6 @@
import { TagRenderingOptions } from "../TagRendering";
export default class FixedName extends TagRenderingOptions {
export default class FixedText extends TagRenderingOptions {
constructor(category: string) {
super({
mappings: [

View file

@ -11,6 +11,7 @@ import {UIRadioButtonWithOther} from "../UI/Base/UIRadioButtonWithOther";
import {VariableUiElement} from "../UI/Base/VariableUIElement";
import {TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor";
import {OnlyShowIfConstructor} from "./OnlyShowIf";
import {UserDetails} from "../Logic/OsmConnection";
export class TagRenderingOptions implements TagDependantUIElementConstructor {
@ -26,11 +27,7 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
constructor(options: {
/**
* What is the priority of the question.
* By default, in the popup of a feature, only one question is shown at the same time. If multiple questions are unanswered, the question with the highest priority is asked first
*/
priority?: number
/**
* This is the string that is shown in the popup if this tag is missing.
@ -41,17 +38,12 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
question?: string,
/**
* Optional:
* if defined, this a common piece of tag that is shown in front of every mapping (except freeform)
* What is the priority of the question.
* By default, in the popup of a feature, only one question is shown at the same time. If multiple questions are unanswered, the question with the highest priority is asked first
*/
primer?: string,
tagsPreprocessor?: ((tags: any) => any),
freeform?: {
key: string, template: string,
renderTemplate: string
placeholder?: string,
extraTags?: TagsFilter,
},
priority?: number,
/**
* Mappings convert a well-known tag combination into a user friendly text.
* It converts e.g. 'access=yes' into 'this area can be accessed'
@ -64,7 +56,33 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
*
*
*/
mappings?: { k: TagsFilter, txt: string, priority?: number, substitute?: boolean }[]
mappings?: { k: TagsFilter, txt: string, priority?: number, substitute?: boolean }[],
/**
* If one wants to render a freeform tag (thus no predefined key/values) or if there are a few well-known tags with a freeform object,
* use this.
* In the question, it'll offer a textfield
*/
freeform?: {
key: string, template: string,
renderTemplate: string
placeholder?: string,
extraTags?: TagsFilter,
},
/**
* Optional:
* if defined, this a common piece of tag that is shown in front of every mapping (except freeform)
*/
primer?: string,
/**
* In some very rare cases, tags have to be rewritten before displaying
* This function adds this
*/
tagsPreprocessor?: ((tags: any) => any)
}) {
this.options = options;
}
@ -111,6 +129,7 @@ class TagRendering extends UIElement implements TagDependantUIElement {
private _priority: number;
private _userDetails: UIEventSource<UserDetails>;
Priority(): number {
return this._priority;
@ -162,6 +181,9 @@ class TagRendering extends UIElement implements TagDependantUIElement {
this.ListenTo(this._questionSkipped);
this.ListenTo(this._editMode);
this._userDetails = changes.login.userDetails;
this.ListenTo(this._userDetails);
this._question = options.question;
this._priority = options.priority ?? 0;
this._primer = options.primer ?? "";
@ -397,8 +419,14 @@ class TagRendering extends UIElement implements TagDependantUIElement {
if (html == "") {
return "";
}
let editButton = "";
if(this._userDetails.data.loggedIn){
editButton = this._editButton.Render();
}
return "<span class='answer'>" +
"<span class='answer-text'>" + html + "</span>" + this._editButton.Render() +
"<span class='answer-text'>" + html + "</span>" +
editButton +
"</span>";
}