forked from MapComplete/MapComplete
		
	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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,3 +73,7 @@ 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,7 +57,7 @@ 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"],
 | 
				
			||||||
| 
						 | 
					@ -75,7 +76,7 @@ Some advanced functions are available on <b>feat</b> as well:
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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"],
 | 
				
			||||||
| 
						 | 
					@ -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~*"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										5101
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										5101
									
								
								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",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue