forked from MapComplete/MapComplete
		
	Fix all the bugs, feature-complete with the non-refactored version
This commit is contained in:
		
							parent
							
								
									5adc355a48
								
							
						
					
					
						commit
						25f2aa8e92
					
				
					 11 changed files with 467 additions and 99 deletions
				
			
		|  | @ -1,5 +1,4 @@ | ||||||
| import {Layout} from "./Layout"; | import {Layout} from "./Layout"; | ||||||
| import {FromJSON} from "./JSON/FromJSON"; |  | ||||||
| import * as bookcases from "../assets/themes/bookcases/Bookcases.json"; | import * as bookcases from "../assets/themes/bookcases/Bookcases.json"; | ||||||
| import * as aed from "../assets/themes/aed/aed.json"; | import * as aed from "../assets/themes/aed/aed.json"; | ||||||
| import * as toilets from "../assets/themes/toilets/toilets.json"; | import * as toilets from "../assets/themes/toilets/toilets.json"; | ||||||
|  | @ -15,6 +14,7 @@ import * as bike_monitoring_stations from "../assets/themes/bike_monitoring_stat | ||||||
| import * as fritures from "../assets/themes/fritures/fritures.json" | import * as fritures from "../assets/themes/fritures/fritures.json" | ||||||
| import * as benches from "../assets/themes/benches/benches.json"; | import * as benches from "../assets/themes/benches/benches.json"; | ||||||
| import * as charging_stations from "../assets/themes/charging_stations/charging_stations.json" | import * as charging_stations from "../assets/themes/charging_stations/charging_stations.json" | ||||||
|  | import * as widths from "../assets/themes/widths/width.json" | ||||||
| 
 | 
 | ||||||
| import {PersonalLayout} from "../Logic/PersonalLayout"; | import {PersonalLayout} from "../Logic/PersonalLayout"; | ||||||
| import LayerConfig from "./JSON/LayerConfig"; | import LayerConfig from "./JSON/LayerConfig"; | ||||||
|  | @ -41,11 +41,20 @@ export class AllKnownLayouts { | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private static GenerateWidths(): Layout { | ||||||
|  |         const layout = Layout.LayoutFromJSON(widths, SharedLayers.sharedLayers); | ||||||
|  | 
 | ||||||
|  |         layout.enableUserBadge = false; | ||||||
|  |          | ||||||
|  |         return layout; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private static GenerateBuurtNatuur(): Layout { |     private static GenerateBuurtNatuur(): Layout { | ||||||
|         const layout = Layout.LayoutFromJSON(buurtnatuur, SharedLayers.sharedLayers); |         const layout = Layout.LayoutFromJSON(buurtnatuur, SharedLayers.sharedLayers); | ||||||
|         layout.enableMoreQuests = false; |         layout.enableMoreQuests = false; | ||||||
|         layout.enableShareScreen = false; |         layout.enableShareScreen = false; | ||||||
|         layout.hideFromOverview = true; |         layout.hideFromOverview = true; | ||||||
|  |         console.log("Buurtnatuur:",layout) | ||||||
|         return layout; |         return layout; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -73,6 +82,7 @@ export class AllKnownLayouts { | ||||||
|         Layout.LayoutFromJSON(fritures, SharedLayers.sharedLayers), |         Layout.LayoutFromJSON(fritures, SharedLayers.sharedLayers), | ||||||
|         Layout.LayoutFromJSON(benches, SharedLayers.sharedLayers), |         Layout.LayoutFromJSON(benches, SharedLayers.sharedLayers), | ||||||
|         Layout.LayoutFromJSON(charging_stations, SharedLayers.sharedLayers), |         Layout.LayoutFromJSON(charging_stations, SharedLayers.sharedLayers), | ||||||
|  |         AllKnownLayouts.GenerateWidths(), | ||||||
|         AllKnownLayouts.GenerateBuurtNatuur(), |         AllKnownLayouts.GenerateBuurtNatuur(), | ||||||
|         AllKnownLayouts.GenerateBikeMonitoringStations(), |         AllKnownLayouts.GenerateBikeMonitoringStations(), | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,8 +1,7 @@ | ||||||
| import Translation from "../../UI/i18n/Translation"; | import Translations, {Translation} from "../../UI/i18n/Translations"; | ||||||
| import TagRenderingConfig from "./TagRenderingConfig"; | import TagRenderingConfig from "./TagRenderingConfig"; | ||||||
| import {Tag, TagsFilter} from "../../Logic/Tags"; | import {Tag, TagsFilter} from "../../Logic/Tags"; | ||||||
| import {LayerConfigJson} from "./LayerConfigJson"; | import {LayerConfigJson} from "./LayerConfigJson"; | ||||||
| import Translations from "../../UI/i18n/Translations"; |  | ||||||
| import {FromJSON} from "./FromJSON"; | import {FromJSON} from "./FromJSON"; | ||||||
| import SharedTagRenderings from "../SharedTagRenderings"; | import SharedTagRenderings from "../SharedTagRenderings"; | ||||||
| import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; | import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; | ||||||
|  | @ -25,6 +24,7 @@ export default class LayerConfig { | ||||||
|     iconSize: TagRenderingConfig; |     iconSize: TagRenderingConfig; | ||||||
|     color: TagRenderingConfig; |     color: TagRenderingConfig; | ||||||
|     width: TagRenderingConfig; |     width: TagRenderingConfig; | ||||||
|  |     dashArray: TagRenderingConfig; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     wayHandling: number; |     wayHandling: number; | ||||||
|  | @ -54,11 +54,12 @@ export default class LayerConfig { | ||||||
|         this.wayHandling = json.wayHandling ?? 0; |         this.wayHandling = json.wayHandling ?? 0; | ||||||
|         this.hideUnderlayingFeaturesMinPercentage = json.hideUnderlayingFeaturesMinPercentage ?? 0; |         this.hideUnderlayingFeaturesMinPercentage = json.hideUnderlayingFeaturesMinPercentage ?? 0; | ||||||
|         this.title = new TagRenderingConfig(json.title); |         this.title = new TagRenderingConfig(json.title); | ||||||
|         this.presets = (json.presets ?? []).map(pr   => ({ |         this.presets = (json.presets ?? []).map(pr => | ||||||
|             title: Translations.T(pr.title), |             ({ | ||||||
|             tags: pr.tags.map(t => FromJSON.SimpleTag(t)), |                 title: Translations.T(pr.title), | ||||||
|             description: Translations.T(pr.description) |                 tags: pr.tags.map(t => FromJSON.SimpleTag(t)), | ||||||
|         })) |                 description: Translations.T(pr.description) | ||||||
|  |             })) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         /** |         /** | ||||||
|  | @ -108,6 +109,7 @@ export default class LayerConfig { | ||||||
|         this.iconSize = tr("iconSize", "40,40,center"); |         this.iconSize = tr("iconSize", "40,40,center"); | ||||||
|         this.color = tr("color", "#0000ff"); |         this.color = tr("color", "#0000ff"); | ||||||
|         this.width = tr("width", "7"); |         this.width = tr("width", "7"); | ||||||
|  |         this.dashArray = tr("dashArray", ""); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -64,6 +64,13 @@ export interface LayerConfigJson { | ||||||
|      */ |      */ | ||||||
|     width?: string | TagRenderingConfigJson; |     width?: string | TagRenderingConfigJson; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * A dasharray, e.g. "5 6" | ||||||
|  |      * The dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap', | ||||||
|  |      * Default value: "" (empty string == full line) | ||||||
|  |      */ | ||||||
|  |     dashArray?: string | TagRenderingConfigJson | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Wayhandling: should a way/area be displayed as: |      * Wayhandling: should a way/area be displayed as: | ||||||
|      * 0) The way itself |      * 0) The way itself | ||||||
|  |  | ||||||
|  | @ -56,6 +56,9 @@ export class FilteredLayer { | ||||||
| 
 | 
 | ||||||
|             const iconUrl = layerDef.icon?.GetRenderValue(tags)?.txt ?? "./assets/bug.svg"; |             const iconUrl = layerDef.icon?.GetRenderValue(tags)?.txt ?? "./assets/bug.svg"; | ||||||
|             const iconSize = (layerDef.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(","); |             const iconSize = (layerDef.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(","); | ||||||
|  |              | ||||||
|  |              | ||||||
|  |             const dashArray = layerDef.dashArray.GetRenderValue(tags)?.txt.split(" ").map(Number); | ||||||
| 
 | 
 | ||||||
|             function num(str, deflt = 40) { |             function num(str, deflt = 40) { | ||||||
|                 const n = Number(str); |                 const n = Number(str); | ||||||
|  | @ -97,7 +100,8 @@ export class FilteredLayer { | ||||||
|                         popupAnchor: [0, 3 - anchorH] |                         popupAnchor: [0, 3 - anchorH] | ||||||
|                     }, |                     }, | ||||||
|                 color: color, |                 color: color, | ||||||
|                 weight: weight |                 weight: weight, | ||||||
|  |                 dashArray: dashArray | ||||||
|             }; |             }; | ||||||
|         }; |         }; | ||||||
|         this.name = name; |         this.name = name; | ||||||
|  |  | ||||||
|  | @ -2,6 +2,8 @@ import {GeoOperations} from "./GeoOperations"; | ||||||
| import CodeGrid from "./Web/CodeGrid"; | import CodeGrid from "./Web/CodeGrid"; | ||||||
| import State from "../State"; | import State from "../State"; | ||||||
| import opening_hours from "opening_hours"; | import opening_hours from "opening_hours"; | ||||||
|  | import {And, Or, Tag} from "./Tags"; | ||||||
|  | import {Utils} from "../Utils"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SimpleMetaTagger { | class SimpleMetaTagger { | ||||||
|  | @ -40,90 +42,201 @@ class SimpleMetaTagger { | ||||||
| export default class MetaTagging { | export default class MetaTagging { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |     private static latlon = new SimpleMetaTagger(["_lat", "_lon"], "The latitude and longitude of the point (or centerpoint in the case of a way/area)", | ||||||
|  |         (feature => { | ||||||
|  |             const centerPoint = GeoOperations.centerpoint(feature); | ||||||
|  |             const lat = centerPoint.geometry.coordinates[1]; | ||||||
|  |             const lon = centerPoint.geometry.coordinates[0]; | ||||||
|  |             feature.properties["_lat"] = "" + lat; | ||||||
|  |             feature.properties["_lon"] = "" + lon; | ||||||
|  |         }) | ||||||
|  |     ); | ||||||
|  |     private static surfaceArea = new SimpleMetaTagger( | ||||||
|  |         ["_surface", "_surface:ha"], "The surface area of the feature, in square meters and in hectare. Not set on points and ways", | ||||||
|  |         (feature => { | ||||||
|  |             const sqMeters = GeoOperations.surfaceAreaInSqMeters(feature); | ||||||
|  |             feature.properties["_surface"] = "" + sqMeters; | ||||||
|  |             feature.properties["_surface:ha"] = "" + Math.floor(sqMeters / 1000) / 10; | ||||||
|  | 
 | ||||||
|  |         }) | ||||||
|  |     ); | ||||||
|  |     private static country = new SimpleMetaTagger( | ||||||
|  |         ["_country"], "The country code of the point", | ||||||
|  |         ((feature, index) => { | ||||||
|  |             const centerPoint = GeoOperations.centerpoint(feature); | ||||||
|  |             const lat = centerPoint.geometry.coordinates[1]; | ||||||
|  |             const lon = centerPoint.geometry.coordinates[0] | ||||||
|  |             // But the codegrid SHOULD be a number!
 | ||||||
|  |             CodeGrid.getCode(lat, lon, (error, code) => { | ||||||
|  |                 if (error === null) { | ||||||
|  |                     feature.properties["_country"] = code; | ||||||
|  | 
 | ||||||
|  |                     // There is a huge performance issue: if there are ~1000 features receiving a ping at the same time, 
 | ||||||
|  |                     // The application hangs big time
 | ||||||
|  |                     // So we disable pinging all together
 | ||||||
|  | 
 | ||||||
|  |                 } else { | ||||||
|  |                     console.warn("Could not determine country for", feature.properties.id, error); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         }) | ||||||
|  |     ) | ||||||
|  |     private static isOpen = new SimpleMetaTagger( | ||||||
|  |         ["_isOpen", "_isOpen:description"], "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no", | ||||||
|  |         (feature => { | ||||||
|  |             const tagsSource = State.state.allElements.addOrGetElement(feature); | ||||||
|  |             tagsSource.addCallback(tags => { | ||||||
|  | 
 | ||||||
|  |                 if (tags["opening_hours"] !== undefined && tags["_country"] !== undefined) { | ||||||
|  | 
 | ||||||
|  |                     if (tags._isOpen !== undefined) { | ||||||
|  |                         // Already defined
 | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     const oh = new opening_hours(tags["opening_hours"], { | ||||||
|  |                         lat: tags._lat, | ||||||
|  |                         lon: tags._lon, | ||||||
|  |                         address: { | ||||||
|  |                             country_code: tags._country | ||||||
|  |                         } | ||||||
|  |                     }, {tag_key: "opening_hours"}); | ||||||
|  | 
 | ||||||
|  |                     const updateTags = () => { | ||||||
|  |                         tags["_isOpen"] = oh.getState() ? "yes" : "no"; | ||||||
|  |                         const comment = oh.getComment(); | ||||||
|  |                         if (comment) { | ||||||
|  |                             tags["_isOpen:description"] = comment; | ||||||
|  |                         } | ||||||
|  |                         const nextChange = oh.getNextChange() as Date; | ||||||
|  |                         if (nextChange !== undefined) { | ||||||
|  |                             window.setTimeout( | ||||||
|  |                                 updateTags, | ||||||
|  |                                 (nextChange.getTime() - (new Date()).getTime()) | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     updateTags(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     public static carriageWayWidth = new SimpleMetaTagger( | ||||||
|  |         ["_width:needed","_width:needed:no_pedestrians", "_width:difference"], | ||||||
|  |         "Legacy for a specific project calculating the needed width for safe traffic on a road", | ||||||
|  |         (feature: any, index: number) => { | ||||||
|  | 
 | ||||||
|  |             const carWidth = 2; | ||||||
|  |             const cyclistWidth = 1.5; | ||||||
|  |             const pedestrianWidth = 0.75; | ||||||
|  | 
 | ||||||
|  |             const properties = feature.properties; | ||||||
|  | 
 | ||||||
|  |             const _leftSideParking = | ||||||
|  |                 new And([new Tag("parking:lane:left", "parallel"), new Tag("parking:lane:right", "no_parking")]); | ||||||
|  |             const _rightSideParking = | ||||||
|  |                 new And([new Tag("parking:lane:right", "parallel"), new Tag("parking:lane:left", "no_parking")]); | ||||||
|  | 
 | ||||||
|  |             const _bothSideParking = new Tag("parking:lane:both", "parallel"); | ||||||
|  |             const _noSideParking = new Tag("parking:lane:both", "no_parking"); | ||||||
|  |             const _otherParkingMode = | ||||||
|  |                 new Or([ | ||||||
|  |                     new Tag("parking:lane:both", "perpendicular"), | ||||||
|  |                     new Tag("parking:lane:left", "perpendicular"), | ||||||
|  |                     new Tag("parking:lane:right", "perpendicular"), | ||||||
|  |                     new Tag("parking:lane:both", "diagonal"), | ||||||
|  |                     new Tag("parking:lane:left", "diagonal"), | ||||||
|  |                     new Tag("parking:lane:right", "diagonal"), | ||||||
|  |                 ]) | ||||||
|  | 
 | ||||||
|  |             const _sidewalkBoth = new Tag("sidewalk", "both"); | ||||||
|  |             const _sidewalkLeft = new Tag("sidewalk", "left"); | ||||||
|  |             const _sidewalkRight = new Tag("sidewalk", "right"); | ||||||
|  |             const _sidewalkNone = new Tag("sidewalk", "none"); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             let parkingStateKnown = true; | ||||||
|  |             let parallelParkingCount = 0; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             const _oneSideParking = new Or([_leftSideParking, _rightSideParking]); | ||||||
|  | 
 | ||||||
|  |             if (_oneSideParking.matchesProperties(properties)) { | ||||||
|  |                 parallelParkingCount = 1; | ||||||
|  |             } else if (_bothSideParking.matchesProperties(properties)) { | ||||||
|  |                 parallelParkingCount = 2; | ||||||
|  |             } else if (_noSideParking.matchesProperties(properties)) { | ||||||
|  |                 parallelParkingCount = 0; | ||||||
|  |             } else if (_otherParkingMode.matchesProperties(properties)) { | ||||||
|  |                 parallelParkingCount = 0; | ||||||
|  |             } else { | ||||||
|  |                 parkingStateKnown = false; | ||||||
|  |                 console.log("No parking data for ", properties.name, properties.id, properties) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             let pedestrianFlowNeeded; | ||||||
|  |             if (_sidewalkBoth.matchesProperties(properties)) { | ||||||
|  |                 pedestrianFlowNeeded = 0; | ||||||
|  |             } else if (_sidewalkNone.matchesProperties(properties)) { | ||||||
|  |                 pedestrianFlowNeeded = 2; | ||||||
|  |             } else if (_sidewalkLeft.matchesProperties(properties) || _sidewalkRight.matchesProperties(properties)) { | ||||||
|  |                 pedestrianFlowNeeded = 1; | ||||||
|  |             } else { | ||||||
|  |                 pedestrianFlowNeeded = -1; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             let onewayCar = properties.oneway === "yes"; | ||||||
|  |             let onewayBike = properties["oneway:bicycle"] === "yes" || | ||||||
|  |                 (onewayCar && properties["oneway:bicycle"] === undefined) | ||||||
|  | 
 | ||||||
|  |             let cyclingAllowed = | ||||||
|  |                 !(properties.bicycle === "use_sidepath" | ||||||
|  |                     || properties.bicycle === "no"); | ||||||
|  | 
 | ||||||
|  |             let carWidthUsed = (onewayCar ? 1 : 2) * carWidth; | ||||||
|  |             properties["_width:needed:cars"] = Utils.Round(carWidthUsed); | ||||||
|  |             properties["_width:needed:parking"] = Utils.Round(parallelParkingCount * carWidth) | ||||||
|  |              | ||||||
|  |              | ||||||
|  |             let cyclistWidthUsed = 0; | ||||||
|  |             if (cyclingAllowed) { | ||||||
|  |                 cyclistWidthUsed = (onewayBike ? 1 : 2) * cyclistWidth; | ||||||
|  |             } | ||||||
|  |             properties["_width:needed:cyclists"] = Utils.Round(cyclistWidthUsed) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             const width = parseFloat(properties["width:carriageway"]); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             const targetWidthIgnoringPedestrians = | ||||||
|  |                 carWidthUsed + | ||||||
|  |                 cyclistWidthUsed + | ||||||
|  |                 parallelParkingCount * carWidthUsed; | ||||||
|  |             properties["_width:needed:no_pedestrians"] =Utils.Round(targetWidthIgnoringPedestrians); | ||||||
|  |            | ||||||
|  |             const pedestriansNeed = Math.max(0, pedestrianFlowNeeded) * pedestrianWidth; | ||||||
|  |             const targetWidth = targetWidthIgnoringPedestrians + pedestriansNeed ; | ||||||
|  |             properties["_width:needed"] = Utils.Round(targetWidth); | ||||||
|  |             properties["_width:needed:pedestrians"] = Utils.Round(pedestriansNeed) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             properties["_width:difference"] = Utils.Round(targetWidth - width ); | ||||||
|  |             properties["_width:difference:no_pedestrians"] = Utils.Round(targetWidthIgnoringPedestrians - width) ; | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|     public static metatags = [ |     public static metatags = [ | ||||||
|         new SimpleMetaTagger(["_lat", "_lon"], "The latitude and longitude of the point (or centerpoint in the case of a way/area)", |         MetaTagging.latlon, | ||||||
|             (feature => { |         MetaTagging.surfaceArea, | ||||||
|                 const centerPoint = GeoOperations.centerpoint(feature); |         MetaTagging.country, | ||||||
|                 const lat = centerPoint.geometry.coordinates[1]; |         MetaTagging.isOpen, | ||||||
|                 const lon = centerPoint.geometry.coordinates[0]; |         MetaTagging.carriageWayWidth | ||||||
|                 feature.properties["_lat"] = "" + lat; |  | ||||||
|                 feature.properties["_lon"] = "" + lon; |  | ||||||
|             }) |  | ||||||
|         ), |  | ||||||
|         new SimpleMetaTagger( |  | ||||||
|             ["_surface", "_surface:ha"], "The surface area of the feature, in square meters and in hectare. Not set on points and ways", |  | ||||||
|             (feature => { |  | ||||||
|                 const sqMeters = GeoOperations.surfaceAreaInSqMeters(feature); |  | ||||||
|                 feature.properties["_surface"] = "" + sqMeters; |  | ||||||
|                 feature.properties["_surface:ha"] = "" + Math.floor(sqMeters / 1000) / 10; |  | ||||||
| 
 | 
 | ||||||
|             }) |  | ||||||
|         ), |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         new SimpleMetaTagger( |  | ||||||
|             ["_country"], "The country code of the point", |  | ||||||
|             ((feature, index) => { |  | ||||||
|                 const centerPoint = GeoOperations.centerpoint(feature); |  | ||||||
|                 const lat = centerPoint.geometry.coordinates[1]; |  | ||||||
|                 const lon = centerPoint.geometry.coordinates[0] |  | ||||||
|                 // But the codegrid SHOULD be a number!
 |  | ||||||
|                 CodeGrid.getCode(lat, lon, (error, code) => { |  | ||||||
|                     if (error === null) { |  | ||||||
|                         feature.properties["_country"] = code; |  | ||||||
| 
 |  | ||||||
|                         // There is a huge performance issue: if there are ~1000 features receiving a ping at the same time, 
 |  | ||||||
|                         // The application hangs big time
 |  | ||||||
|                         // So we disable pinging all together
 |  | ||||||
| 
 |  | ||||||
|                     } else { |  | ||||||
|                         console.warn("Could not determine country for", feature.properties.id, error); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             }) |  | ||||||
|         ), |  | ||||||
|         new SimpleMetaTagger( |  | ||||||
|             ["_isOpen", "_isOpen:description"], "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no", |  | ||||||
|             (feature => { |  | ||||||
|                 const tagsSource = State.state.allElements.addOrGetElement(feature); |  | ||||||
|                 tagsSource.addCallback(tags => { |  | ||||||
| 
 |  | ||||||
|                     if (tags["opening_hours"] !== undefined && tags["_country"] !== undefined) { |  | ||||||
| 
 |  | ||||||
|                         if (tags._isOpen !== undefined) { |  | ||||||
|                             // Already defined
 |  | ||||||
|                             return; |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         const oh = new opening_hours(tags["opening_hours"], { |  | ||||||
|                             lat: tags._lat, |  | ||||||
|                             lon: tags._lon, |  | ||||||
|                             address: { |  | ||||||
|                                 country_code: tags._country |  | ||||||
|                             } |  | ||||||
|                         }, {tag_key: "opening_hours"}); |  | ||||||
| 
 |  | ||||||
|                         const updateTags = () => { |  | ||||||
|                             tags["_isOpen"] = oh.getState() ? "yes" : "no"; |  | ||||||
|                             const comment = oh.getComment(); |  | ||||||
|                             if (comment) { |  | ||||||
|                                 tags["_isOpen:description"] = comment; |  | ||||||
|                             } |  | ||||||
|                             const nextChange = oh.getNextChange() as Date; |  | ||||||
|                             if (nextChange !== undefined) { |  | ||||||
|                                 window.setTimeout( |  | ||||||
|                                     updateTags, |  | ||||||
|                                     (nextChange.getTime() - (new Date()).getTime()) |  | ||||||
|                                 ) |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         updateTags(); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                 }) |  | ||||||
| 
 |  | ||||||
|             }) |  | ||||||
|         ) |  | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     static addMetatags(features: any[]) { |     static addMetatags(features: any[]) { | ||||||
|  |  | ||||||
|  | @ -15,9 +15,9 @@ import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConf | ||||||
| import {UserDetails} from "../../Logic/Osm/OsmConnection"; | import {UserDetails} from "../../Logic/Osm/OsmConnection"; | ||||||
| import State from "../../State"; | import State from "../../State"; | ||||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | import {VariableUiElement} from "../Base/VariableUIElement"; | ||||||
| import {FromJSON} from "../../Customizations/JSON/FromJSON"; |  | ||||||
| import ValidatedTextField from "../Input/ValidatedTextField"; | import ValidatedTextField from "../Input/ValidatedTextField"; | ||||||
| import SpecialVisualizations from "../SpecialVisualizations"; | import SpecialVisualizations from "../SpecialVisualizations"; | ||||||
|  | import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig"; | ||||||
| 
 | 
 | ||||||
| export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> { | export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> { | ||||||
| 
 | 
 | ||||||
|  | @ -111,7 +111,7 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs | ||||||
|          |          | ||||||
|         this.validText = new VariableUiElement(value.map((json: TagRenderingConfigJson) => { |         this.validText = new VariableUiElement(value.map((json: TagRenderingConfigJson) => { | ||||||
|             try{ |             try{ | ||||||
|                 FromJSON.TagRendering(json, options?.title ?? ""); |                 new TagRenderingConfig(json, options?.title ?? ""); | ||||||
|                 return ""; |                 return ""; | ||||||
|             }catch(e){ |             }catch(e){ | ||||||
|                 return "<span class='alert'>"+e+"</span>" |                 return "<span class='alert'>"+e+"</span>" | ||||||
|  |  | ||||||
|  | @ -2,9 +2,10 @@ import {UIElement} from "../UIElement"; | ||||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | import {UIEventSource} from "../../Logic/UIEventSource"; | ||||||
| import TagRenderingPanel from "./TagRenderingPanel"; | import TagRenderingPanel from "./TagRenderingPanel"; | ||||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | import {VariableUiElement} from "../Base/VariableUIElement"; | ||||||
| import {FromJSON} from "../../Customizations/JSON/FromJSON"; |  | ||||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | import {FixedUiElement} from "../Base/FixedUiElement"; | ||||||
| import Combine from "../Base/Combine"; | import Combine from "../Base/Combine"; | ||||||
|  | import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig"; | ||||||
|  | import EditableTagRendering from "../Popup/EditableTagRendering"; | ||||||
| 
 | 
 | ||||||
| export default class TagRenderingPreview extends UIElement { | export default class TagRenderingPreview extends UIElement { | ||||||
| 
 | 
 | ||||||
|  | @ -39,8 +40,7 @@ export default class TagRenderingPreview extends UIElement { | ||||||
|             rendering = |             rendering = | ||||||
|                 new VariableUiElement(es.map(tagRenderingConfig => { |                 new VariableUiElement(es.map(tagRenderingConfig => { | ||||||
|                         try { |                         try { | ||||||
|                             const tr = FromJSON.TagRendering(tagRenderingConfig, "preview") |                             const tr = new EditableTagRendering(self.previewTagValue, new TagRenderingConfig(tagRenderingConfig, "preview")); | ||||||
|                                 .construct(self.previewTagValue); |  | ||||||
|                             return tr.Render(); |                             return tr.Render(); | ||||||
|                         } catch (e) { |                         } catch (e) { | ||||||
|                             return new Combine(["Could not show this tagrendering:", e.message]).Render(); |                             return new Combine(["Could not show this tagrendering:", e.message]).Render(); | ||||||
|  |  | ||||||
|  | @ -46,10 +46,12 @@ export class SimpleAddUI extends UIElement { | ||||||
|          |          | ||||||
|         const self = this; |         const self = this; | ||||||
|         for (const layer of State.state.filteredLayers.data) { |         for (const layer of State.state.filteredLayers.data) { | ||||||
|              | 
 | ||||||
|             this.ListenTo(layer.isDisplayed); |             this.ListenTo(layer.isDisplayed); | ||||||
|              | 
 | ||||||
|             for (const preset of layer.layerDef.presets) { |             const presets = layer.layerDef.presets; | ||||||
|  |             for (const preset of presets) { | ||||||
|  |                 console.log("Preset:", preset) | ||||||
| 
 | 
 | ||||||
|                 let icon: string = layer.layerDef.icon.GetRenderValue( |                 let icon: string = layer.layerDef.icon.GetRenderValue( | ||||||
|                     TagUtils.KVtoProperties(preset.tags ?? [])).txt ?? |                     TagUtils.KVtoProperties(preset.tags ?? [])).txt ?? | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								Utils.ts
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								Utils.ts
									
										
									
									
									
								
							|  | @ -39,6 +39,17 @@ export class Utils { | ||||||
|         return "" + i; |         return "" + i; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static Round(i: number) { | ||||||
|  |         if(i < 0){ | ||||||
|  |             return "-" + Utils.Round(-i); | ||||||
|  |         } | ||||||
|  |         const j = "" + Math.floor(i * 10); | ||||||
|  |         if (j.length == 1) { | ||||||
|  |             return "0." + j; | ||||||
|  |         } | ||||||
|  |         return j.substr(0, j.length - 1) + "." + j.substr(j.length - 1, j.length); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public static Times(f: ((i: number) => string), count: number): string { |     public static Times(f: ((i: number) => string), count: number): string { | ||||||
|         let res = ""; |         let res = ""; | ||||||
|         for (let i = 0; i < count; i++) { |         for (let i = 0; i < count; i++) { | ||||||
|  |  | ||||||
							
								
								
									
										204
									
								
								assets/themes/widths/width.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								assets/themes/widths/width.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,204 @@ | ||||||
|  | { | ||||||
|  |   "id": "width", | ||||||
|  |   "title": { | ||||||
|  |     "nl": "Straatbreedtes" | ||||||
|  |   }, | ||||||
|  |   "shortDescription": { | ||||||
|  |     "nl": "Is de straat breed genoeg?" | ||||||
|  |   }, | ||||||
|  |   "description": { | ||||||
|  |     "nl": " <h3>De straat is opgebruikt</h3>\n <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>\n <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>\n <h3>Legende</h3>\n <span style='background: red'>   </span> Straat te smal voor veilig verkeer<br/>\n <span style='background: #0f0'>   </span> Straat is breed genoeg veilig verkeer<br/>\n <span style='background: orange'>   </span> Straat zonder voetpad, te smal als ook voetgangers plaats krijgen<br/>\n <span style='background: lightgrey'>   </span> Woonerf, autoluw, autoloos of enkel plaatselijk verkeer<br/>\n <br/>\n <br/>\n Een gestippelde lijn is een straat waar ook voor fietsers éénrichtingsverkeer geldt.<br/>\n Klik op een straat om meer informatie te zien.\n <h3>Hoe gaan we verder?</h3>\n Verschillende ingrepen kunnen de stad teruggeven aan de inwoners en de stad leefbaarder en levendiger maken.<br/>\n Denk aan:\n <ul>\n <li>De autovrije zone's uitbreiden</li>\n <li>De binnenstad fietszone maken</li>\n <li>Het aantal woonerven uitbreiden</li>\n <li>Grotere auto's meer belasten - ze nemen immers meer parkeerruimte in.</li>\n <li>Laat toeristen verplicht parkeren onder het zand; een (fiets)taxi kan hen naar hun hotel brengen</li>\n <li>Voorzie in elke straat enkele parkeerplaatsen voor kortparkeren. Zo kunnen leveringen, iemand afzetten,... gebeuren zonder op het voetpad en fietspad te parkeren</li>\n </ul>\"" | ||||||
|  |   }, | ||||||
|  |   "language": [ | ||||||
|  |     "nl" | ||||||
|  |   ], | ||||||
|  |   "maintainer": "", | ||||||
|  |   "icon": "./assets/themes/widths/icon.svg", | ||||||
|  |   "version": "0", | ||||||
|  |   "startLat": 51.20875, | ||||||
|  |   "startLon": 3.22435, | ||||||
|  |   "startZoom": 14, | ||||||
|  |   "widenFactor": 0.05, | ||||||
|  |   "socialImage": "", | ||||||
|  |   "layers": [ | ||||||
|  |     { | ||||||
|  |       "id": "widths", | ||||||
|  |       "name": { | ||||||
|  |         "nl": "Straten met een breedte" | ||||||
|  |       }, | ||||||
|  |       "minzoom": 14, | ||||||
|  |       "overpassTags": { | ||||||
|  |         "and": [ | ||||||
|  |           "width:carriageway~*" | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "titleIcons": [], | ||||||
|  |       "title": { | ||||||
|  |         "render": { | ||||||
|  |           "nl": "{name}" | ||||||
|  |         }, | ||||||
|  |         "condition": { | ||||||
|  |           "and": [] | ||||||
|  |         }, | ||||||
|  |         "mappings": [ | ||||||
|  |           { | ||||||
|  |             "if": { | ||||||
|  |               "and": [ | ||||||
|  |                 "name=" | ||||||
|  |               ] | ||||||
|  |             }, | ||||||
|  |             "then": { | ||||||
|  |               "nl": "Naamloos segmet" | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "description": {}, | ||||||
|  |       "tagRenderings": [ | ||||||
|  |         { | ||||||
|  |           "render": "Deze straat is <b>{width:carriageway}m</b> breed" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "render": "Deze straat heeft <span class='alert'>{_width:difference}m</span> te weinig:", | ||||||
|  |           "mappings": [ | ||||||
|  |             { | ||||||
|  |               "if": { | ||||||
|  |                 "or": [ | ||||||
|  |                   "_width:difference~-.*", | ||||||
|  |                   "_width:difference=0.0" | ||||||
|  |                 ] | ||||||
|  |               }, | ||||||
|  |               "then": "Deze straat is breed genoeg:" | ||||||
|  |             } | ||||||
|  |           ] | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "render": "<b>{_width:needed:cars}m</b> voor het autoverkeer", | ||||||
|  |           "mappings": [ | ||||||
|  |             { | ||||||
|  |               "if": "oneway=yes", | ||||||
|  |               "then": "<b>{_width:needed:cars}m</b> voor het éénrichtings-autoverkeer" | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               "if": "oneway=no", | ||||||
|  |               "then": "<b>{_width:needed:cars}m</b> voor het tweerichtings-autoverkeer" | ||||||
|  |             } | ||||||
|  |           ] | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "render": "<b>{_width:needed:parking}m</b> voor het geparkeerde wagens", | ||||||
|  |           "condition": "_width:needed:parking!=0.0" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "render": "<b>{_width:needed:cyclists}m</b> voor fietsers", | ||||||
|  |           "mappings": [ | ||||||
|  |             { | ||||||
|  |               "if": "bicycle=use_sidepath", | ||||||
|  |               "then": "Fietsers hebben hier een vrijliggend fietspad en worden dus niet meegerekend" | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               "if": "oneway:bicycle=yes", | ||||||
|  |               "then": "<b>{_width:needed:cyclists}m</b> voor fietsers, die met de rijrichting mee moeten" | ||||||
|  |             } | ||||||
|  |           ] | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "render": "<b>{_width:needed:pedestrians}m</b> voor voetgangers", | ||||||
|  |           "condition": "_width:needed:pedestrians!=0.0", | ||||||
|  |           "mappings": [ | ||||||
|  |             { | ||||||
|  |               "if": { | ||||||
|  |                 "or": [ | ||||||
|  |                   "sidewalk=none", | ||||||
|  |                   "sidewalk=no" | ||||||
|  |                 ] | ||||||
|  |               }, | ||||||
|  |               "then": "<b>{_width:needed:pedestrians}m</b> voor voetgangers: er zijn hier geen voetpaden" | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               "if": { | ||||||
|  |                 "or": [ | ||||||
|  |                   "sidewalk=left", | ||||||
|  |                   "sidewalk=right" | ||||||
|  |                 ] | ||||||
|  |               }, | ||||||
|  |               "then": "<b>{_width:needed:pedestrians}m</b> voor voetgangers: er is slechts aan één kant een voetpad" | ||||||
|  |             } | ||||||
|  |           ] | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "render": "<span style='border: 1px solid black; border-radius: 0.5em; padding: 0.25em;'><b>{_width:needed}m</b> nodig in het totaal</span>" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "render": "{all_tags()}" | ||||||
|  |         } | ||||||
|  |       ], | ||||||
|  |       "hideUnderlayingFeaturesMinPercentage": 0, | ||||||
|  |       "icon": { | ||||||
|  |         "render": "./assets/themes/widths/icon.svg" | ||||||
|  |       }, | ||||||
|  |       "width": { | ||||||
|  |         "render": "4" | ||||||
|  |       }, | ||||||
|  |       "iconSize": { | ||||||
|  |         "render": "40,40,center" | ||||||
|  |       }, | ||||||
|  |       "color": { | ||||||
|  |         "render": "#00f", | ||||||
|  |         "mappings": [ | ||||||
|  |           { | ||||||
|  |             "if": { | ||||||
|  |               "or": [ | ||||||
|  |                 "access=destination", | ||||||
|  |                 "highway=living_street", | ||||||
|  |                 "highway=pedestrian", | ||||||
|  |                 "motor_vehicle=no", | ||||||
|  |                 "motor_vehicle=destination" | ||||||
|  |               ] | ||||||
|  |             }, | ||||||
|  |             "then": "lightgrey" | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "if": "_width:difference~-.*", | ||||||
|  |             "then": "#0f0" | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "if": { | ||||||
|  |               "and": [ | ||||||
|  |                 "_width:difference!~-.*", | ||||||
|  |                 "_width:difference:no_pedestrians~-.*" | ||||||
|  |               ] | ||||||
|  |             }, | ||||||
|  |             "then": "orange" | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "if": "_width:difference!~-.*", | ||||||
|  |             "then": "#f00" | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "dashArray": { | ||||||
|  |         "render": "", | ||||||
|  |         "mappings": [ | ||||||
|  |           { | ||||||
|  |             "if": { | ||||||
|  |               "and": [ | ||||||
|  |                 "oneway=yes", | ||||||
|  |                 { | ||||||
|  |                   "or": [ | ||||||
|  |                     "oneway:bicycle=yes", | ||||||
|  |                     "oneway:bicycle=" | ||||||
|  |                   ] | ||||||
|  |                 } | ||||||
|  |               ] | ||||||
|  |             }, | ||||||
|  |             "then": "5 6" | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       "presets": [] | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "roamingRenderings": [], | ||||||
|  |   "defaultBackgroundId": "Stadia.AlidadeSmoothDark" | ||||||
|  | } | ||||||
|  | @ -13,6 +13,7 @@ import PublicHolidayInput from "../UI/Input/OpeningHours/PublicHolidayInput"; | ||||||
| import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig"; | import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig"; | ||||||
| import EditableTagRendering from "../UI/Popup/EditableTagRendering"; | import EditableTagRendering from "../UI/Popup/EditableTagRendering"; | ||||||
| import {SubstitutedTranslation} from "../UI/SpecialVisualizations"; | import {SubstitutedTranslation} from "../UI/SpecialVisualizations"; | ||||||
|  | import {Utils} from "../Utils"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -323,5 +324,19 @@ new T([ | ||||||
|     ["OH Parse PH 12:00-17:00", () => { |     ["OH Parse PH 12:00-17:00", () => { | ||||||
|         const rules = PublicHolidayInput.LoadValue("PH 12:00-17:00"); |         const rules = PublicHolidayInput.LoadValue("PH 12:00-17:00"); | ||||||
|         equal(rules.mode, " "); |         equal(rules.mode, " "); | ||||||
|  |     }], | ||||||
|  |     ["Round", () => { | ||||||
|  |         equal(Utils.Round(15),  "15.0") | ||||||
|  |         equal(Utils.Round(1),  "1.0") | ||||||
|  |         equal(Utils.Round(1.5),  "1.5") | ||||||
|  |         equal(Utils.Round(0.5),  "0.5") | ||||||
|  |         equal(Utils.Round(1.6),  "1.6") | ||||||
|  | 
 | ||||||
|  |         equal(Utils.Round(-15),  "-15.0") | ||||||
|  |         equal(Utils.Round(-1),  "-1.0") | ||||||
|  |         equal(Utils.Round(-1.5),  "-1.5") | ||||||
|  |         equal(Utils.Round(-0.5),  "-0.5") | ||||||
|  |         equal(Utils.Round(-1.6),  "-1.6") | ||||||
|  | 
 | ||||||
|     }] |     }] | ||||||
| ]); | ]); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue