Add better relation support
This commit is contained in:
parent
7b47af8978
commit
12afdcab75
18 changed files with 2637 additions and 2386 deletions
|
@ -130,8 +130,11 @@ export interface LayerConfigJson {
|
||||||
*/
|
*/
|
||||||
rotation?: string | TagRenderingConfigJson;
|
rotation?: string | TagRenderingConfigJson;
|
||||||
/**
|
/**
|
||||||
* A HTML-fragment that is shown at the center of the icon, for example:
|
* A HTML-fragment that is shown below the icon, for example:
|
||||||
* <div style="background: white; display: block">{name}</div>
|
* <div style="background: white; display: block">{name}</div>
|
||||||
|
*
|
||||||
|
* If the icon is undefined, then the label is shown in the center of the feature.
|
||||||
|
* Note that, if the wayhandling hides the icon then no label is shown as well.
|
||||||
*/
|
*/
|
||||||
label?: string | TagRenderingConfigJson ;
|
label?: string | TagRenderingConfigJson ;
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,8 @@ export default class TagRenderingConfig {
|
||||||
throw `Freeform.key is undefined or the empty string - this is not allowed; either fill out something or remove the freeform block alltogether. Error in ${context}`
|
throw `Freeform.key is undefined or the empty string - this is not allowed; either fill out something or remove the freeform block alltogether. Error in ${context}`
|
||||||
}
|
}
|
||||||
if (ValidatedTextField.AllTypes[this.freeform.type] === undefined) {
|
if (ValidatedTextField.AllTypes[this.freeform.type] === undefined) {
|
||||||
throw `Freeform.key ${this.freeform.key} is an invalid type`
|
const knownKeys = ValidatedTextField.tpList.map(tp => tp.name).join(", ");
|
||||||
|
throw `Freeform.key ${this.freeform.key} is an invalid type. Known keys are ${knownKeys}`
|
||||||
}
|
}
|
||||||
if (this.freeform.addExtraTags) {
|
if (this.freeform.addExtraTags) {
|
||||||
const usedKeys = new And(this.freeform.addExtraTags).usedKeys();
|
const usedKeys = new And(this.freeform.addExtraTags).usedKeys();
|
||||||
|
@ -204,7 +205,7 @@ export default class TagRenderingConfig {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.multiAnswer) {
|
if (this.multiAnswer) {
|
||||||
for (const m of this.mappings) {
|
for (const m of this.mappings ?? []) {
|
||||||
if (TagUtils.MatchesMultiAnswer(m.if, tags)) {
|
if (TagUtils.MatchesMultiAnswer(m.if, tags)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ The above code will be executed for every feature in the layer. The feature is a
|
||||||
* distanceTo
|
* distanceTo
|
||||||
* overlapWith
|
* overlapWith
|
||||||
* closest
|
* closest
|
||||||
|
* memberships
|
||||||
|
|
||||||
### distanceTo
|
### distanceTo
|
||||||
|
|
||||||
|
@ -71,4 +72,8 @@ Gives a list of features from the specified layer which this feature overlaps wi
|
||||||
|
|
||||||
Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered.
|
Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered.
|
||||||
|
|
||||||
* list of features
|
* list of features
|
||||||
|
|
||||||
|
### memberships
|
||||||
|
|
||||||
|
Gives a list of {role: string, relation: Relation}-objects, containing all the relations that this feature is part of. For example: \`\_part\_of\_walking\_routes=feat.memberships().map(r => r.relation.tags.name).join(';')\`
|
|
@ -1,6 +1,7 @@
|
||||||
import {GeoOperations} from "./GeoOperations";
|
import {GeoOperations} from "./GeoOperations";
|
||||||
import {UIElement} from "../UI/UIElement";
|
import {UIElement} from "../UI/UIElement";
|
||||||
import Combine from "../UI/Base/Combine";
|
import Combine from "../UI/Base/Combine";
|
||||||
|
import State from "../State";
|
||||||
|
|
||||||
export class ExtraFunction {
|
export class ExtraFunction {
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ The above code will be executed for every feature in the layer. The feature is a
|
||||||
Some advanced functions are available on <b>feat</b> as well:
|
Some advanced functions are available on <b>feat</b> as well:
|
||||||
|
|
||||||
`
|
`
|
||||||
private static OverlapFunc = new ExtraFunction(
|
private static readonly OverlapFunc = new ExtraFunction(
|
||||||
"overlapWith",
|
"overlapWith",
|
||||||
"Gives a list of features from the specified layer which this feature overlaps with, the amount of overlap in m². The returned value is <b>{ feat: GeoJSONFeature, overlap: number}</b>",
|
"Gives a list of features from the specified layer which this feature overlaps with, the amount of overlap in m². The returned value is <b>{ feat: GeoJSONFeature, overlap: number}</b>",
|
||||||
["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"],
|
["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"],
|
||||||
|
@ -56,26 +57,26 @@ Some advanced functions are available on <b>feat</b> as well:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
private static DistanceToFunc = new ExtraFunction(
|
private static readonly DistanceToFunc = new ExtraFunction(
|
||||||
"distanceTo",
|
"distanceTo",
|
||||||
"Calculates the distance between the feature and a specified point",
|
"Calculates the distance between the feature and a specified point",
|
||||||
["longitude", "latitude"],
|
["longitude", "latitude"],
|
||||||
(featuresPerLayer, feature) => {
|
(featuresPerLayer, feature) => {
|
||||||
return (arg0, lat) => {
|
return (arg0, lat) => {
|
||||||
if(typeof arg0 === "number"){
|
if (typeof arg0 === "number") {
|
||||||
const lon = arg0
|
const lon = arg0
|
||||||
// Feature._lon and ._lat is conveniently place by one of the other metatags
|
// Feature._lon and ._lat is conveniently place by one of the other metatags
|
||||||
return GeoOperations.distanceBetween([lon, lat], [feature._lon, feature._lat]);
|
return GeoOperations.distanceBetween([lon, lat], [feature._lon, feature._lat]);
|
||||||
}else{
|
} else {
|
||||||
// arg0 is probably a feature
|
// arg0 is probably a feature
|
||||||
return GeoOperations.distanceBetween(GeoOperations.centerpointCoordinates(arg0),[feature._lon, feature._lat])
|
return GeoOperations.distanceBetween(GeoOperations.centerpointCoordinates(arg0), [feature._lon, feature._lat])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
private static ClosestObjectFunc = new ExtraFunction(
|
private static readonly ClosestObjectFunc = new ExtraFunction(
|
||||||
"closest",
|
"closest",
|
||||||
"Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered.",
|
"Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered.",
|
||||||
["list of features"],
|
["list of features"],
|
||||||
|
@ -87,7 +88,7 @@ Some advanced functions are available on <b>feat</b> as well:
|
||||||
let closestFeature = undefined;
|
let closestFeature = undefined;
|
||||||
let closestDistance = undefined;
|
let closestDistance = undefined;
|
||||||
for (const otherFeature of features) {
|
for (const otherFeature of features) {
|
||||||
if(otherFeature == feature){
|
if (otherFeature == feature) {
|
||||||
continue; // We ignore self
|
continue; // We ignore self
|
||||||
}
|
}
|
||||||
let distance = undefined;
|
let distance = undefined;
|
||||||
|
@ -99,10 +100,10 @@ Some advanced functions are available on <b>feat</b> as well:
|
||||||
[feature._lon, feature._lat]
|
[feature._lon, feature._lat]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if(distance === undefined){
|
if (distance === undefined) {
|
||||||
throw "Undefined distance!"
|
throw "Undefined distance!"
|
||||||
}
|
}
|
||||||
if(closestFeature === undefined || distance < closestDistance){
|
if (closestFeature === undefined || distance < closestDistance) {
|
||||||
closestFeature = otherFeature
|
closestFeature = otherFeature
|
||||||
closestDistance = distance;
|
closestDistance = distance;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +114,19 @@ Some advanced functions are available on <b>feat</b> as well:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
private static readonly allFuncs: ExtraFunction[] = [ExtraFunction.DistanceToFunc, ExtraFunction.OverlapFunc, ExtraFunction.ClosestObjectFunc];
|
private static readonly Memberships = new ExtraFunction(
|
||||||
|
"memberships",
|
||||||
|
"Gives a list of {role: string, relation: Relation}-objects, containing all the relations that this feature is part of. \n\nFor example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')`",
|
||||||
|
[],
|
||||||
|
(featuresPerLayer, feature) => {
|
||||||
|
return () => {
|
||||||
|
return State.state.knownRelations.data?.get(feature.id) ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
private static readonly allFuncs: ExtraFunction[] = [ExtraFunction.DistanceToFunc, ExtraFunction.OverlapFunc, ExtraFunction.ClosestObjectFunc, ExtraFunction.Memberships];
|
||||||
private readonly _name: string;
|
private readonly _name: string;
|
||||||
private readonly _args: string[];
|
private readonly _args: string[];
|
||||||
private readonly _doc: string;
|
private readonly _doc: string;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
|
||||||
import Loc from "../../Models/Loc";
|
import Loc from "../../Models/Loc";
|
||||||
import GeoJsonSource from "./GeoJsonSource";
|
import GeoJsonSource from "./GeoJsonSource";
|
||||||
import MetaTaggingFeatureSource from "./MetaTaggingFeatureSource";
|
import MetaTaggingFeatureSource from "./MetaTaggingFeatureSource";
|
||||||
|
import RegisteringFeatureSource from "./RegisteringFeatureSource";
|
||||||
|
|
||||||
export default class FeaturePipeline implements FeatureSource {
|
export default class FeaturePipeline implements FeatureSource {
|
||||||
|
|
||||||
|
@ -24,33 +25,38 @@ export default class FeaturePipeline implements FeatureSource {
|
||||||
locationControl: UIEventSource<Loc>) {
|
locationControl: UIEventSource<Loc>) {
|
||||||
|
|
||||||
const amendedOverpassSource =
|
const amendedOverpassSource =
|
||||||
new RememberingSource(new FeatureDuplicatorPerLayer(flayers,
|
new RememberingSource(
|
||||||
new LocalStorageSaver(updater, layout))
|
new LocalStorageSaver(
|
||||||
);
|
new MetaTaggingFeatureSource( // first we metatag, then we save to get the metatags into storage too
|
||||||
|
new RegisteringFeatureSource(
|
||||||
|
new FeatureDuplicatorPerLayer(flayers,
|
||||||
|
updater)
|
||||||
|
)), layout));
|
||||||
|
|
||||||
const geojsonSources: GeoJsonSource [] = []
|
const geojsonSources: GeoJsonSource [] = []
|
||||||
for (const flayer of flayers.data) {
|
for (const flayer of flayers.data) {
|
||||||
const sourceUrl = flayer.layerDef.source.geojsonSource
|
const sourceUrl = flayer.layerDef.source.geojsonSource
|
||||||
if (sourceUrl !== undefined) {
|
if (sourceUrl !== undefined) {
|
||||||
geojsonSources.push(
|
geojsonSources.push(new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers,
|
||||||
new GeoJsonSource(flayer.layerDef.id, sourceUrl))
|
new GeoJsonSource(flayer.layerDef.id, sourceUrl))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const amendedLocalStorageSource =
|
const amendedLocalStorageSource =
|
||||||
new RememberingSource(new FeatureDuplicatorPerLayer(flayers, new LocalStorageSource(layout))
|
new RememberingSource(new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers, new LocalStorageSource(layout))
|
||||||
);
|
));
|
||||||
|
|
||||||
newPoints = new FeatureDuplicatorPerLayer(flayers, newPoints);
|
newPoints = new MetaTaggingFeatureSource(new FeatureDuplicatorPerLayer(flayers,
|
||||||
|
new RegisteringFeatureSource(newPoints)));
|
||||||
|
|
||||||
const merged =
|
const merged =
|
||||||
new MetaTaggingFeatureSource(
|
|
||||||
new FeatureSourceMerger([
|
new FeatureSourceMerger([
|
||||||
amendedOverpassSource,
|
amendedOverpassSource,
|
||||||
amendedLocalStorageSource,
|
amendedLocalStorageSource,
|
||||||
newPoints,
|
newPoints,
|
||||||
...geojsonSources
|
...geojsonSources
|
||||||
]));
|
]);
|
||||||
|
|
||||||
const source =
|
const source =
|
||||||
new WayHandlingApplyingFeatureSource(flayers,
|
new WayHandlingApplyingFeatureSource(flayers,
|
||||||
|
|
|
@ -16,10 +16,6 @@ export default class MetaTaggingFeatureSource implements FeatureSource {
|
||||||
featuresFreshness.forEach(featureFresh => {
|
featuresFreshness.forEach(featureFresh => {
|
||||||
const feature = featureFresh.feature;
|
const feature = featureFresh.feature;
|
||||||
|
|
||||||
if(!State.state.allElements.has(feature.properties.id)){
|
|
||||||
State.state.allElements.addOrGetElement(feature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Hash.hash.data === feature.properties.id) {
|
if (Hash.hash.data === feature.properties.id) {
|
||||||
State.state.selectedElement.setData(feature);
|
State.state.selectedElement.setData(feature);
|
||||||
}
|
}
|
||||||
|
|
19
Logic/FeatureSource/RegisteringFeatureSource.ts
Normal file
19
Logic/FeatureSource/RegisteringFeatureSource.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import FeatureSource from "./FeatureSource";
|
||||||
|
import {UIEventSource} from "../UIEventSource";
|
||||||
|
import State from "../../State";
|
||||||
|
|
||||||
|
export default class RegisteringFeatureSource implements FeatureSource {
|
||||||
|
features: UIEventSource<{ feature: any; freshness: Date }[]>;
|
||||||
|
|
||||||
|
constructor(source: FeatureSource) {
|
||||||
|
this.features = source.features;
|
||||||
|
this.features.addCallbackAndRun(features => {
|
||||||
|
for (const feature of features ?? []) {
|
||||||
|
if (!State.state.allElements.has(feature.feature.properties.id)) {
|
||||||
|
State.state.allElements.addOrGetElement(feature.feature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import * as turf from 'turf'
|
import * as turf from '@turf/turf'
|
||||||
|
|
||||||
export class GeoOperations {
|
export class GeoOperations {
|
||||||
|
|
||||||
|
@ -118,6 +118,9 @@ export class GeoOperations {
|
||||||
return inside;
|
return inside;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static lengthInMeters(feature: any) {
|
||||||
|
return turf.length(feature) * 1000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
58
Logic/Osm/ExtractRelations.ts
Normal file
58
Logic/Osm/ExtractRelations.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import State from "../../State";
|
||||||
|
|
||||||
|
export interface Relation {
|
||||||
|
id: number,
|
||||||
|
type: "relation"
|
||||||
|
members: {
|
||||||
|
type: ("way" | "node" | "relation"),
|
||||||
|
ref: number,
|
||||||
|
role: string
|
||||||
|
}[],
|
||||||
|
tags: any,
|
||||||
|
// Alias for tags; tags == properties
|
||||||
|
properties: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ExtractRelations {
|
||||||
|
|
||||||
|
public static RegisterRelations(overpassJson: any) : void{
|
||||||
|
const memberships = ExtractRelations.BuildMembershipTable(ExtractRelations.GetRelationElements(overpassJson))
|
||||||
|
console.log("Assigned memberships: ", memberships)
|
||||||
|
State.state.knownRelations.setData(memberships)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GetRelationElements(overpassJson: any): Relation[] {
|
||||||
|
const relations = overpassJson.elements.filter(element => element.type === "relation")
|
||||||
|
for (const relation of relations) {
|
||||||
|
relation.properties = relation.tags
|
||||||
|
}
|
||||||
|
return relations
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a mapping of {memberId --> {role in relation, id of relation} }
|
||||||
|
* @param relations
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
private static BuildMembershipTable(relations: Relation[]): Map<string, { role: string, relation: Relation, }[]> {
|
||||||
|
const memberships = new Map<string, { role: string, relation: Relation }[]>()
|
||||||
|
|
||||||
|
for (const relation of relations) {
|
||||||
|
for (const member of relation.members) {
|
||||||
|
|
||||||
|
const role = {
|
||||||
|
role: member.role,
|
||||||
|
relation: relation
|
||||||
|
}
|
||||||
|
const key = member.type + "/" + member.ref
|
||||||
|
if (!memberships.has(key)) {
|
||||||
|
memberships.set(key, [])
|
||||||
|
}
|
||||||
|
memberships.get(key).push(role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return memberships
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
import * as $ from "jquery"
|
import * as $ from "jquery"
|
||||||
import * as OsmToGeoJson from "osmtogeojson";
|
import * as OsmToGeoJson from "osmtogeojson";
|
||||||
import Bounds from "../../Models/Bounds";
|
import Bounds from "../../Models/Bounds";
|
||||||
import {TagsFilter} from "../TagsFilter";
|
import {TagsFilter} from "../Tags/TagsFilter";
|
||||||
|
import ExtractRelations from "./ExtractRelations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interfaces overpass to get all the latest data
|
* Interfaces overpass to get all the latest data
|
||||||
|
@ -38,9 +39,9 @@ export class Overpass {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExtractRelations.RegisterRelations(json)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const geojson = OsmToGeoJson.default(json);
|
const geojson = OsmToGeoJson.default(json);
|
||||||
console.log("Received geojson", geojson)
|
|
||||||
const osmTime = new Date(json.osm3s.timestamp_osm_base);
|
const osmTime = new Date(json.osm3s.timestamp_osm_base);
|
||||||
continuation(geojson, osmTime);
|
continuation(geojson, osmTime);
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,18 @@ export default class SimpleMetaTagger {
|
||||||
feature.area = sqMeters;
|
feature.area = sqMeters;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private static lngth = new SimpleMetaTagger(
|
||||||
|
["_length", "_length:km"], "The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter",
|
||||||
|
(feature => {
|
||||||
|
const l = GeoOperations.lengthInMeters(feature)
|
||||||
|
feature.properties["_length"] = "" + l
|
||||||
|
const km = Math.floor(l / 1000)
|
||||||
|
const kmRest = Math.round((l - km * 1000) / 100)
|
||||||
|
feature.properties["_length:km"] = "" + km+ "." + kmRest
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
private static country = new SimpleMetaTagger(
|
private static country = new SimpleMetaTagger(
|
||||||
["_country"], "The country code of the property (with latlon2country)",
|
["_country"], "The country code of the property (with latlon2country)",
|
||||||
feature => {
|
feature => {
|
||||||
|
@ -294,6 +306,7 @@ export default class SimpleMetaTagger {
|
||||||
public static metatags = [
|
public static metatags = [
|
||||||
SimpleMetaTagger.latlon,
|
SimpleMetaTagger.latlon,
|
||||||
SimpleMetaTagger.surfaceArea,
|
SimpleMetaTagger.surfaceArea,
|
||||||
|
SimpleMetaTagger.lngth,
|
||||||
SimpleMetaTagger.country,
|
SimpleMetaTagger.country,
|
||||||
SimpleMetaTagger.isOpen,
|
SimpleMetaTagger.isOpen,
|
||||||
SimpleMetaTagger.carriageWayWidth,
|
SimpleMetaTagger.carriageWayWidth,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Utils } from "../Utils";
|
||||||
|
|
||||||
export default class Constants {
|
export default class Constants {
|
||||||
|
|
||||||
public static vNumber = "0.6.8c";
|
public static vNumber = "0.6.9";
|
||||||
|
|
||||||
// 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 = {
|
||||||
|
|
6
State.ts
6
State.ts
|
@ -17,6 +17,7 @@ import UpdateFromOverpass from "./Logic/Actors/UpdateFromOverpass";
|
||||||
import LayerConfig from "./Customizations/JSON/LayerConfig";
|
import LayerConfig from "./Customizations/JSON/LayerConfig";
|
||||||
import TitleHandler from "./Logic/Actors/TitleHandler";
|
import TitleHandler from "./Logic/Actors/TitleHandler";
|
||||||
import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader";
|
import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader";
|
||||||
|
import {Relation} from "./Logic/Osm/ExtractRelations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the global state: a bunch of UI-event sources
|
* Contains the global state: a bunch of UI-event sources
|
||||||
|
@ -76,6 +77,11 @@ export default class State {
|
||||||
*/
|
*/
|
||||||
public readonly selectedElement = new UIEventSource<any>(undefined, "Selected element")
|
public readonly selectedElement = new UIEventSource<any>(undefined, "Selected element")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of relations: which way is part of which other way?
|
||||||
|
* Set by the overpass-updater; used in the metatagging
|
||||||
|
*/
|
||||||
|
public readonly knownRelations = new UIEventSource<Map<string, {role: string, relation: Relation}[]>>(undefined, "Relation memberships")
|
||||||
|
|
||||||
public readonly featureSwitchUserbadge: UIEventSource<boolean>;
|
public readonly featureSwitchUserbadge: UIEventSource<boolean>;
|
||||||
public readonly featureSwitchSearch: UIEventSource<boolean>;
|
public readonly featureSwitchSearch: UIEventSource<boolean>;
|
||||||
|
|
|
@ -46,7 +46,7 @@ export default class TagRenderingAnswer extends UIElement {
|
||||||
if (this._configuration.multiAnswer) {
|
if (this._configuration.multiAnswer) {
|
||||||
|
|
||||||
let freeformKeyUsed = this._configuration.freeform?.key === undefined; // If it is undefined, it is "used" already, or at least we don't have to check for it anymore
|
let freeformKeyUsed = this._configuration.freeform?.key === undefined; // If it is undefined, it is "used" already, or at least we don't have to check for it anymore
|
||||||
const applicableThens: Translation[] = Utils.NoNull(this._configuration.mappings.map(mapping => {
|
const applicableThens: Translation[] = Utils.NoNull(this._configuration.mappings?.map(mapping => {
|
||||||
if (mapping.if === undefined) {
|
if (mapping.if === undefined) {
|
||||||
return mapping.then;
|
return mapping.then;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ export default class TagRenderingAnswer extends UIElement {
|
||||||
return mapping.then;
|
return mapping.then;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}))
|
}) ?? [])
|
||||||
|
|
||||||
if (!freeformKeyUsed
|
if (!freeformKeyUsed
|
||||||
&& tags[this._configuration.freeform.key] !== undefined) {
|
&& tags[this._configuration.freeform.key] !== undefined) {
|
||||||
|
|
|
@ -27,10 +27,126 @@
|
||||||
"play_forest",
|
"play_forest",
|
||||||
"playground",
|
"playground",
|
||||||
"sport_pitch",
|
"sport_pitch",
|
||||||
"slow_roads",
|
{ "builtin": "slow_roads",
|
||||||
|
"override": {
|
||||||
|
"calculatedTags": [
|
||||||
|
"_part_of_walking_routes=feat.memberships().map(r => \"<a href='#relation/\"+r.relation.id+\"'>\" + r.relation.tags.name + \"</a>\").join(', ')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"grass_in_parks",
|
"grass_in_parks",
|
||||||
"village_green"
|
"village_green",
|
||||||
|
{
|
||||||
|
"id": "walking_routes",
|
||||||
|
"name": {
|
||||||
|
"nl": "Wandelroutes van provincie Antwerpen"
|
||||||
|
},
|
||||||
|
"description": "Walking routes by 'provincie Antwerpen'",
|
||||||
|
"source": {
|
||||||
|
"osmTags": {
|
||||||
|
"and": [
|
||||||
|
"type=route",
|
||||||
|
"route=foot",
|
||||||
|
"operator=provincie Antwerpen"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"render": "Wandeling <i>{name}</i>",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"if": "name~.*wandeling.*",
|
||||||
|
"then": "{name}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tagRenderings": [
|
||||||
|
{
|
||||||
|
"render": {
|
||||||
|
"nl": "Deze wandeling is <b>{_length:km}km</b> lang"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"if": "route=iwn",
|
||||||
|
"then": {
|
||||||
|
"nl": "Dit is een internationale wandelroute"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": "route=nwn",
|
||||||
|
"then": {
|
||||||
|
"nl": "Dit is een nationale wandelroute"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": "route=rwn",
|
||||||
|
"then": {
|
||||||
|
"nl": "Dit is een regionale wandelroute"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"if": "route=lwn",
|
||||||
|
"then": {
|
||||||
|
"nl": "Dit is een lokale wandelroute"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"render": {
|
||||||
|
"nl": "<h3>Korte beschrijving:</h3>{description}"
|
||||||
|
},
|
||||||
|
"question": "Geef een korte beschrijving van de wandeling (max 255 tekens)",
|
||||||
|
"freeform": {
|
||||||
|
"key": "description",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": {
|
||||||
|
"nl": "Wie beheert deze wandeling en plaatst dus de signalisatiebordjes?"
|
||||||
|
},
|
||||||
|
"render": "Signalisatie geplaatst door {operator}",
|
||||||
|
"freeform":{
|
||||||
|
"key": "operator"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": {
|
||||||
|
"nl": "Naar wie kan men emailen bij problemen rond signalisatie?"
|
||||||
|
},
|
||||||
|
"render": {
|
||||||
|
"nl": "Bij problemen met signalisatie kan men emailen naar <a href='mailto:{operator:email}'>{operator:email}</a>"
|
||||||
|
},
|
||||||
|
"freeform": {
|
||||||
|
"key": "operator:email",
|
||||||
|
"type": "email"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions",
|
||||||
|
"reviews"
|
||||||
|
],
|
||||||
|
"color": {
|
||||||
|
"render": "#6d6",
|
||||||
|
"mappings":[
|
||||||
|
{
|
||||||
|
"if": "color~*",
|
||||||
|
"then": "{color}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"render": "3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
],
|
],
|
||||||
"roamingRenderings": []
|
"roamingRenderings": [
|
||||||
|
{
|
||||||
|
"render": "Maakt deel uit van {_part_of_walking_routes}",
|
||||||
|
"condition": "_part_of_walking_routes~*"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
4691
package-lock.json
generated
4691
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -38,7 +38,11 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/preset-env": "7.13.8",
|
"@babel/preset-env": "7.13.8",
|
||||||
"@tailwindcss/postcss7-compat": "^2.0.2",
|
"@tailwindcss/postcss7-compat": "^2.0.2",
|
||||||
|
"@turf/buffer": "^6.3.0",
|
||||||
|
"@turf/collect": "^6.3.0",
|
||||||
"@turf/distance": "^6.3.0",
|
"@turf/distance": "^6.3.0",
|
||||||
|
"@turf/length": "^6.3.0",
|
||||||
|
"@turf/turf": "^6.3.0",
|
||||||
"@types/jquery": "^3.5.5",
|
"@types/jquery": "^3.5.5",
|
||||||
"@types/leaflet-markercluster": "^1.0.3",
|
"@types/leaflet-markercluster": "^1.0.3",
|
||||||
"@types/leaflet-providers": "^1.2.0",
|
"@types/leaflet-providers": "^1.2.0",
|
||||||
|
|
|
@ -42,7 +42,7 @@ writeFileSync("./assets/generated/known_layers_and_themes.json", JSON.stringify(
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
console.log("Discovered ", layerFiles.length, "layers and ", themeFiles.length, "themes\n")
|
console.log("Discovered", layerFiles.length, "layers and", themeFiles.length, "themes\n")
|
||||||
console.log(" ---------- VALIDATING ---------")
|
console.log(" ---------- VALIDATING ---------")
|
||||||
// ------------- VALIDATION --------------
|
// ------------- VALIDATION --------------
|
||||||
const licensePaths = []
|
const licensePaths = []
|
||||||
|
|
Loading…
Reference in a new issue