forked from MapComplete/MapComplete
Merge branches
This commit is contained in:
commit
c1c11ed906
33 changed files with 568 additions and 130 deletions
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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()
|
||||
];
|
||||
|
|
|
@ -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()
|
||||
];
|
||||
|
|
|
@ -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: "$$$",
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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(),
|
||||
|
|
69
Customizations/Layers/GhostBike.ts
Normal file
69
Customizations/Layers/GhostBike.ts
Normal 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],
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
||||
);
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
20
Customizations/Questions/BikePumpManometer.ts
Normal file
20
Customizations/Questions/BikePumpManometer.ts
Normal 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"}
|
||||
]
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
24
Customizations/Questions/BikePumpValves.ts
Normal file
24
Customizations/Questions/BikePumpValves.ts
Normal 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}"
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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: [
|
|
@ -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>";
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue