Bug fixes with preprocessing; add streetwidth map
This commit is contained in:
parent
c5b9e66bd2
commit
636bad97b3
15 changed files with 13279 additions and 41 deletions
|
@ -8,6 +8,8 @@ import { WalkByBrussels } from "./Layouts/WalkByBrussels";
|
|||
import { All } from "./Layouts/All";
|
||||
import { Layout } from "./Layout";
|
||||
import {MetaMap} from "./Layouts/MetaMap";
|
||||
import {Widths} from "./Layers/Widths";
|
||||
import {StreetWidth} from "./Layouts/StreetWidth";
|
||||
|
||||
export class AllKnownLayouts {
|
||||
public static allSets: any = AllKnownLayouts.AllLayouts();
|
||||
|
@ -21,6 +23,7 @@ export class AllKnownLayouts {
|
|||
new Bookcases(),
|
||||
new WalkByBrussels(),
|
||||
new MetaMap(),
|
||||
new StreetWidth(),
|
||||
all
|
||||
/*new Toilets(),
|
||||
new Statues(),
|
||||
|
|
|
@ -56,7 +56,16 @@ export class LayerDefinition {
|
|||
*/
|
||||
elementsToShow: TagDependantUIElementConstructor[];
|
||||
|
||||
style: (tags: any) => { color: string, icon: any };
|
||||
/**
|
||||
* A simple styling for the geojson element
|
||||
* color is the color for areas and ways
|
||||
* icon is the Leaflet icon
|
||||
* Note that this is passed entirely to leaflet, so other leaflet attributes work too
|
||||
*/
|
||||
style: (tags: any) => {
|
||||
color: string,
|
||||
icon: any ,
|
||||
};
|
||||
|
||||
/**
|
||||
* If an object of the next layer is contained for this many percent in this feature, it is eaten and not shown
|
||||
|
|
|
@ -6,7 +6,7 @@ import FixedText from "../Questions/FixedText";
|
|||
import {TagRenderingOptions} from "../TagRendering";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
|
||||
export class DrinkingWaterLayer extends LayerDefinition {
|
||||
export class DrinkingWater extends LayerDefinition {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -31,7 +31,9 @@ export class DrinkingWaterLayer extends LayerDefinition {
|
|||
this.elementsToShow = [
|
||||
new OperatorTag(),
|
||||
];
|
||||
this.elementsToShow = [new ImageCarouselWithUploadConstructor(), new TagRenderingOptions({
|
||||
this.elementsToShow = [
|
||||
new ImageCarouselWithUploadConstructor(),
|
||||
new TagRenderingOptions({
|
||||
question: "How easy is it to fill water bottles?",
|
||||
mappings: [
|
||||
{ k: new Tag("bottle", "yes"), txt: "It is easy to refill water bottles" },
|
||||
|
|
|
@ -52,19 +52,13 @@ export class GrbToFix extends LayerDefinition {
|
|||
|
||||
question: "Wat is het huisnummer?",
|
||||
tagsPreprocessor: tags => {
|
||||
const newTags = {};
|
||||
newTags["addr:housenumber"] = tags["addr:housenumber"]
|
||||
newTags["addr:street"] = tags["addr:street"]
|
||||
|
||||
const telltale = "GRB thinks that this has number ";
|
||||
const index = tags.fixme.indexOf(telltale);
|
||||
if (index >= 0) {
|
||||
const housenumber = tags.fixme.slice(index + telltale.length);
|
||||
newTags["grb:housenumber:human"] = housenumber;
|
||||
newTags["grb:housenumber"] = housenumber == "no number" ? "" : housenumber;
|
||||
tags["grb:housenumber:human"] = housenumber;
|
||||
tags["grb:housenumber"] = housenumber == "no number" ? "" : housenumber;
|
||||
}
|
||||
|
||||
return newTags;
|
||||
},
|
||||
freeform: {
|
||||
key: "addr:housenumber",
|
||||
|
|
213
Customizations/Layers/Widths.ts
Normal file
213
Customizations/Layers/Widths.ts
Normal file
|
@ -0,0 +1,213 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {And, Not, Or, Tag} from "../../Logic/TagsFilter";
|
||||
import {TagRenderingOptions} from "../TagRendering";
|
||||
import {UIEventSource} from "../../UI/UIEventSource";
|
||||
import {Park} from "./Park";
|
||||
|
||||
export class Widths extends LayerDefinition {
|
||||
|
||||
private cyclistWidth: number;
|
||||
private carWidth: number;
|
||||
|
||||
private readonly _bothSideParking = new Tag("parking:lane:both", "parallel");
|
||||
private readonly _noSideParking = new Tag("parking:lane:both", "no_parking");
|
||||
|
||||
private readonly _leftSideParking =
|
||||
new And([new Tag("parking:lane:left", "parallel"), new Tag("parking:lane:right", "no_parking")]);
|
||||
private readonly _rightSideParking =
|
||||
new And([new Tag("parking:lane:right", "parallel"), new Tag("parking:lane:left", "no_parking")]);
|
||||
|
||||
private readonly _oneSideParking = new Or([this._leftSideParking, this._rightSideParking]);
|
||||
|
||||
private readonly _carfree = new Or([new Tag("highway", "pedestrian"), new Tag("highway", "living_street")])
|
||||
private readonly _notCarFree = new Not(this._carfree);
|
||||
|
||||
private calcProps(properties) {
|
||||
let parkingStateKnown = true;
|
||||
let parallelParkingCount = 0;
|
||||
|
||||
if (this._oneSideParking.matchesProperties(properties)) {
|
||||
parallelParkingCount = 1;
|
||||
} else if (this._bothSideParking.matchesProperties(properties)) {
|
||||
parallelParkingCount = 2;
|
||||
} else if (this._noSideParking.matchesProperties(properties)) {
|
||||
parallelParkingCount = 0;
|
||||
} else {
|
||||
parkingStateKnown = false;
|
||||
console.log("No parking data for ", properties.name, properties.id, properties)
|
||||
}
|
||||
|
||||
|
||||
let onewayCar = properties.oneway === "yes";
|
||||
let onewayBike = properties["oneway:bicycle"] === "yes" ||
|
||||
(onewayCar && properties["oneway:bicycle"] === undefined)
|
||||
|
||||
|
||||
let carWidth = (onewayCar ? 1 : 2) * this.carWidth;
|
||||
|
||||
let cyclistWidth = (onewayBike ? 1 : 2) * this.cyclistWidth;
|
||||
|
||||
const width = parseFloat(properties["width:carriageway"]);
|
||||
|
||||
|
||||
const targetWidth =
|
||||
carWidth +
|
||||
cyclistWidth +
|
||||
parallelParkingCount * this.carWidth;
|
||||
|
||||
return {
|
||||
parkingLanes: parallelParkingCount,
|
||||
parkingStateKnown: parkingStateKnown,
|
||||
width: width,
|
||||
targetWidth: targetWidth,
|
||||
onewayBike: onewayBike
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
constructor(carWidth: number,
|
||||
cyclistWidth: number) {
|
||||
super();
|
||||
this.carWidth = carWidth;
|
||||
this.cyclistWidth = cyclistWidth;
|
||||
|
||||
this.name = "widths";
|
||||
this.overpassFilter = new Tag("width:carriageway", "*");
|
||||
|
||||
this.title = new TagRenderingOptions({
|
||||
freeform: {
|
||||
renderTemplate: "{name}",
|
||||
template: "$$$",
|
||||
key: "name"
|
||||
}
|
||||
})
|
||||
|
||||
const self = this;
|
||||
this.style = (properties) => {
|
||||
|
||||
let c = "#0c0";
|
||||
|
||||
|
||||
const props = self.calcProps(properties);
|
||||
|
||||
if (props.width < props.targetWidth) {
|
||||
c = "#f00";
|
||||
}
|
||||
|
||||
let dashArray = undefined;
|
||||
|
||||
if (!props.parkingStateKnown) {
|
||||
c = "#f0f"
|
||||
}
|
||||
|
||||
if (this._carfree.matchesProperties(properties)) {
|
||||
c = "#aaa";
|
||||
}
|
||||
|
||||
if (props.onewayBike) {
|
||||
dashArray = [20, 8]
|
||||
}
|
||||
|
||||
if (props.width > 15) {
|
||||
c = "#ffb72b"
|
||||
}
|
||||
|
||||
return {
|
||||
icon: null,
|
||||
color: c,
|
||||
weight: 7,
|
||||
dashArray: dashArray
|
||||
}
|
||||
}
|
||||
|
||||
this.elementsToShow = [
|
||||
new TagRenderingOptions({
|
||||
mappings: [
|
||||
{
|
||||
k: this._bothSideParking,
|
||||
txt: "Auto's kunnen langs beide zijden parkeren.<br+>Dit gebruikt <b>" + (this.carWidth * 2) + "m</b><br/>"
|
||||
},
|
||||
{
|
||||
k: this._oneSideParking,
|
||||
txt: "Auto's kunnen langs één kant parkeren.<br/>Dit gebruikt <b>" + this.carWidth + "m</b><br/>"
|
||||
},
|
||||
{k: this._noSideParking, txt: "Auto's mogen hier niet parkeren"},
|
||||
{k: null, txt: "Nog geen parkeerinformatie bekend"}
|
||||
]
|
||||
}).OnlyShowIf(this._notCarFree),
|
||||
new TagRenderingOptions({
|
||||
mappings: [
|
||||
{
|
||||
k: new Tag("oneway:bicycle", "yes"),
|
||||
txt: "Eenrichtingsverkeer, óók voor fietsers. Dit gebruikt <b>" + (this.carWidth + this.cyclistWidth) + "m</b>"
|
||||
},
|
||||
{
|
||||
k: new And([new Tag("oneway", "yes"), new Tag("oneway:bicycle", "no")]),
|
||||
txt: "Tweerichtingverkeer voor fietsers, eenrichting voor auto's Dit gebruikt <b>" + (this.carWidth + 2 * this.cyclistWidth) + "m</b>"
|
||||
},
|
||||
{
|
||||
k: new Tag("oneway", "yes"),
|
||||
txt: "Eenrichtingsverkeer voor iedereen. Dit gebruikt <b>" + (this.carWidth + this.cyclistWidth) + "m</b>"
|
||||
},
|
||||
{
|
||||
k: null,
|
||||
txt: "Tweerichtingsverkeer voor iedereen. Dit gebruikt <b>" + (2 * this.carWidth + 2 * this.cyclistWidth) + "m</b>"
|
||||
}
|
||||
]
|
||||
}).OnlyShowIf(this._notCarFree),
|
||||
|
||||
new TagRenderingOptions(
|
||||
{
|
||||
tagsPreprocessor: (tags) => {
|
||||
const props = self.calcProps(tags);
|
||||
tags.targetWidth = props.targetWidth;
|
||||
console.log("PREP", tags)
|
||||
},
|
||||
freeform: {
|
||||
key: "width:carriageway",
|
||||
renderTemplate: "De totale nodige ruimte voor vlot en veilig verkeer is dus <b>{targetWidth}m</b>.",
|
||||
template: "$$$",
|
||||
}
|
||||
}
|
||||
).OnlyShowIf(this._notCarFree),
|
||||
|
||||
|
||||
new TagRenderingOptions({
|
||||
mappings: [
|
||||
{k:new Tag("highway","living_street"),txt: "Dit is een woonerf"},
|
||||
{k:new Tag("highway","pedestrian"),txt: "Hier mogen enkel voetgangers komen"}
|
||||
]
|
||||
}),
|
||||
|
||||
new TagRenderingOptions({
|
||||
mappings: [
|
||||
{
|
||||
k: new Tag("sidewalk", "none"),
|
||||
txt: "De afstand van huis tot huis is <b>{width:carriageway}m</b>"
|
||||
},
|
||||
{
|
||||
k: new Tag("sidewalk", "left"),
|
||||
txt: "De afstand van huis tot voetpad is <b>{width:carriageway}m</b>"
|
||||
},
|
||||
{
|
||||
k: new Tag("sidewalk", "right"),
|
||||
txt: "De afstand van huis tot voetpad is <b>{width:carriageway}m</b>"
|
||||
},
|
||||
{
|
||||
k: new Tag("sidewalk", "both"),
|
||||
txt: "De afstand van voetpad tot voetpad is <b>{width:carriageway}m</b>"
|
||||
},
|
||||
{
|
||||
k: new Tag("sidewalk", ""),
|
||||
txt: "De straatbreedte is <b>{width:carriageway}m</b>"
|
||||
}
|
||||
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@ import {Layout} from "../Layout";
|
|||
import BikeParkings from "../Layers/BikeParkings";
|
||||
import BikeServices from "../Layers/BikeStations";
|
||||
import {GhostBike} from "../Layers/GhostBike";
|
||||
import {DrinkingWater, DrinkingWaterLayer} from "../Layers/DrinkingWater";
|
||||
|
||||
|
||||
export default class Cyclofix extends Layout {
|
||||
|
@ -9,7 +10,7 @@ export default class Cyclofix extends Layout {
|
|||
super(
|
||||
"pomp",
|
||||
"Cyclofix bicycle infrastructure",
|
||||
[new GhostBike(), new BikeServices(), new BikeParkings()],
|
||||
[new GhostBike(), new BikeServices(), new BikeParkings(), new DrinkingWater()],
|
||||
16,
|
||||
50.8465573,
|
||||
4.3516970,
|
||||
|
|
34
Customizations/Layouts/StreetWidth.ts
Normal file
34
Customizations/Layouts/StreetWidth.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import {Layout} from "../Layout";
|
||||
import * as Layer from "../Layers/Bookcases";
|
||||
import {Widths} from "../Layers/Widths";
|
||||
import {UIEventSource} from "../../UI/UIEventSource";
|
||||
|
||||
export class StreetWidth extends Layout{
|
||||
|
||||
constructor() {
|
||||
super( "width",
|
||||
"Straatbreedtes in Brugge",
|
||||
[new Widths(
|
||||
2.2,
|
||||
1.5
|
||||
|
||||
)],
|
||||
15,
|
||||
51.20875,
|
||||
3.22435,
|
||||
"<h3>De straat is opgebruikt</h3>" +
|
||||
"<p>Er is steeds meer druk op de openbare ruimte. Voetgangers, fietsers, steps, auto's, bussen, bestelwagens, buggies, cargobikes, ... willen allemaal hun deel van de openbare ruimte.</p>" +
|
||||
"" +
|
||||
"<p>In deze studie nemen we Brugge onder de loep en kijken we hoe breed elke straat is én hoe breed elke straat zou moeten zijn voor een veilig én vlot verkeer.</p>" +
|
||||
"Verschillende ingrepen kunnen de stad teruggeven aan de inwoners en de stad leefbaarder en levendiger maken.<br/>" +
|
||||
"Denk aan:" +
|
||||
"<ul>" +
|
||||
"<li>De autovrije zone's uitbreiden</li>" +
|
||||
"<li>De binnenstad fietszone maken</li>" +
|
||||
"<li>Het aantal woonerven uitbreiden</li>" +
|
||||
"<li>Grotere auto's meer belasten - ze nemen immers meer parkeerruimte in.</li>" +
|
||||
"</ul>",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { Layout } from "../Layout";
|
||||
import { DrinkingWaterLayer } from "../Layers/DrinkingWater";
|
||||
import { DrinkingWater } from "../Layers/DrinkingWater";
|
||||
import { NatureReserves } from "../Layers/NatureReserves";
|
||||
import { Park } from "../Layers/Park";
|
||||
|
||||
|
@ -7,7 +7,7 @@ export class WalkByBrussels extends Layout {
|
|||
constructor() {
|
||||
super("walkbybrussels",
|
||||
"Drinking Water Spots",
|
||||
[new DrinkingWaterLayer(), new Park(), new NatureReserves()],
|
||||
[new DrinkingWater(), new Park(), new NatureReserves()],
|
||||
10,
|
||||
50.8435,
|
||||
4.3688,
|
||||
|
|
|
@ -4,6 +4,9 @@ import {TagRenderingOptions} from "../TagRendering";
|
|||
export class WikipediaLink extends TagRenderingOptions {
|
||||
|
||||
private static FixLink(value: string): string {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
// @ts-ignore
|
||||
if (value.startsWith("https")) {
|
||||
return value;
|
||||
|
@ -20,6 +23,11 @@ export class WikipediaLink extends TagRenderingOptions {
|
|||
static options = {
|
||||
priority: 10,
|
||||
// question: "Wat is het overeenstemmende wkipedia-artikel?",
|
||||
tagsPreprocessor: (tags) => {
|
||||
if (tags.wikipedia !== undefined) {
|
||||
tags.wikipedia = WikipediaLink.FixLink(tags.wikipedia);
|
||||
}
|
||||
},
|
||||
freeform: {
|
||||
key: "wikipedia",
|
||||
template: "$$$",
|
||||
|
@ -28,19 +36,8 @@ export class WikipediaLink extends TagRenderingOptions {
|
|||
"<a href='{wikipedia}' target='_blank'>" +
|
||||
"<img width='64px' src='./assets/wikipedia.svg' alt='wikipedia'>" +
|
||||
"</a></span>",
|
||||
placeholder: "",
|
||||
tagsPreprocessor: (tags) => {
|
||||
placeholder: ""
|
||||
|
||||
const newTags = {};
|
||||
for (const k in tags) {
|
||||
if (k === "wikipedia") {
|
||||
newTags["wikipedia"] = WikipediaLink.FixLink(tags[k]);
|
||||
} else {
|
||||
newTags[k] = tags[k];
|
||||
}
|
||||
}
|
||||
return newTags;
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
|
|
@ -80,15 +80,16 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
|
|||
|
||||
/**
|
||||
* In some very rare cases, tags have to be rewritten before displaying
|
||||
* This function adds this
|
||||
* This function can be used for that.
|
||||
* This function is ran on a _copy_ of the original properties
|
||||
*/
|
||||
tagsPreprocessor?: ((tags: any) => any)
|
||||
tagsPreprocessor?: ((tags: any) => void)
|
||||
}) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
OnlyShowIf(dependencies): TagDependantUIElementConstructor {
|
||||
return new OnlyShowIfConstructor(dependencies, this);
|
||||
OnlyShowIf(tagsFilter: TagsFilter): TagDependantUIElementConstructor {
|
||||
return new OnlyShowIfConstructor(tagsFilter, this);
|
||||
}
|
||||
|
||||
|
||||
|
@ -183,11 +184,22 @@ class TagRendering extends UIElement implements TagDependantUIElement {
|
|||
|
||||
this._userDetails = changes.login.userDetails;
|
||||
this.ListenTo(this._userDetails);
|
||||
|
||||
|
||||
this._question = options.question;
|
||||
this._priority = options.priority ?? 0;
|
||||
this._primer = options.primer ?? "";
|
||||
this._tagsPreprocessor = options.tagsPreprocessor;
|
||||
this._tagsPreprocessor = function (properties) {
|
||||
if (options.tagsPreprocessor === undefined) {
|
||||
return properties;
|
||||
}
|
||||
const newTags = {};
|
||||
for (const k in properties) {
|
||||
newTags[k] = properties[k];
|
||||
}
|
||||
options.tagsPreprocessor(newTags);
|
||||
return newTags;
|
||||
};
|
||||
|
||||
this._mapping = [];
|
||||
this._renderMapping = [];
|
||||
this._freeform = options.freeform;
|
||||
|
@ -325,12 +337,7 @@ class TagRendering extends UIElement implements TagDependantUIElement {
|
|||
}
|
||||
|
||||
private ApplyTemplate(template: string): string {
|
||||
let tags = this._source.data;
|
||||
if (this._tagsPreprocessor !== undefined) {
|
||||
tags = this._tagsPreprocessor(tags);
|
||||
}
|
||||
|
||||
|
||||
const tags = this._tagsPreprocessor(this._source.data);
|
||||
return TagUtils.ApplyTemplate(template, tags);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ export class Regex extends TagsFilter {
|
|||
}
|
||||
|
||||
matches(tags: { k: string; v: string }[]): boolean {
|
||||
if(!(tags instanceof Array)){
|
||||
throw "You used 'matches' on something that is not a list. Did you mean to use 'matchesProperties'?"
|
||||
}
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag.k === this._k) {
|
||||
if (tag.v === "") {
|
||||
|
@ -201,6 +205,28 @@ export class And extends TagsFilter {
|
|||
}
|
||||
}
|
||||
|
||||
export class Not extends TagsFilter{
|
||||
private not: TagsFilter;
|
||||
|
||||
constructor(not: TagsFilter) {
|
||||
super();
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
asOverpass(): string[] {
|
||||
throw "Not supported yet"
|
||||
}
|
||||
|
||||
matches(tags: { k: string; v: string }[]): boolean {
|
||||
return !this.not.matches(tags);
|
||||
}
|
||||
|
||||
substituteValues(tags: any): TagsFilter {
|
||||
return new Not(this.not.substituteValues(tags));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class TagUtils {
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ Furthermore, it shows images present in the `image` tag or, if a `wikidata` or `
|
|||
- [Buurtnatuur.be](http://buurntatuur.be), developed for the Belgian [Green party](https://www.groen.be/). They also funded the initial development!
|
||||
- [Cyclofix](https://pietervdvn.github.io/MapComplete/index.html?quests=pomp), further development on [Open Summer of Code](https://summerofcode.be/) funded by [Brussels Mobility](https://mobilite-mobiliteit.brussels/en)
|
||||
- [Bookcases](https://pietervdvn.github.io/MapComplete/index.html?quests=bookcases#element) cause I like to collect them.
|
||||
- [Map of Maps](https://pietervdvn.github.io/MapComplete/index.html?layout=metamap#element), after a tweet
|
||||
|
||||
Have a theme idea? Drop it in the [issues](https://github.com/pietervdvn/MapComplete/issues)
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ export class ImageCarouselWithUploadConstructor implements TagDependantUIElement
|
|||
return 0;
|
||||
}
|
||||
|
||||
construct(tags: UIEventSource<any>, changes: Changes): TagDependantUIElement {
|
||||
return new ImageCarouselWithUpload(tags, changes);
|
||||
construct(dependencies): TagDependantUIElement {
|
||||
return new ImageCarouselWithUpload(dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12951
assets/streetwidths.geojson
Normal file
12951
assets/streetwidths.geojson
Normal file
File diff suppressed because it is too large
Load diff
2
index.ts
2
index.ts
|
@ -41,7 +41,7 @@ if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
|
|||
dryRun = true;
|
||||
// If you have a testfile somewhere, enable this to spoof overpass
|
||||
// This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules
|
||||
Overpass.testUrl = null; // "http://127.0.0.1:8080/test.json";
|
||||
Overpass.testUrl = "http://127.0.0.1:8080/streetwidths.geojson";
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue