| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  | import {InputElement} from "./InputElement"; | 
					
						
							|  |  |  | import Loc from "../../Models/Loc"; | 
					
						
							|  |  |  | import {UIEventSource} from "../../Logic/UIEventSource"; | 
					
						
							|  |  |  | import Minimap from "../Base/Minimap"; | 
					
						
							|  |  |  | import BaseLayer from "../../Models/BaseLayer"; | 
					
						
							|  |  |  | import Combine from "../Base/Combine"; | 
					
						
							|  |  |  | import Svg from "../../Svg"; | 
					
						
							| 
									
										
										
										
											2021-07-14 16:05:50 +02:00
										 |  |  | import State from "../../State"; | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  | import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | import {BBox, GeoOperations} from "../../Logic/GeoOperations"; | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  | import * as L from "leaflet"; | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; | 
					
						
							|  |  |  | import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; | 
					
						
							|  |  |  | import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | export default class LocationInput extends InputElement<Loc> { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |     private static readonly matchLayer = new LayerConfig( | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |             id: "matchpoint", source: { | 
					
						
							|  |  |  |                 osmTags: {and: []} | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             icon: "./assets/svg/crosshair-empty.svg" | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |         }, "matchpoint icon", true | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  |     IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |     public readonly snappedOnto: UIEventSource<any> = new UIEventSource<any>(undefined) | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  |     private _centerLocation: UIEventSource<Loc>; | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |     private readonly mapBackground: UIEventSource<BaseLayer>; | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * The features to which the input should be snapped | 
					
						
							|  |  |  |      * @private | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |     private readonly _snapTo: UIEventSource<{ feature: any }[]> | 
					
						
							|  |  |  |     private readonly _value: UIEventSource<Loc> | 
					
						
							|  |  |  |     private readonly _snappedPoint: UIEventSource<any> | 
					
						
							|  |  |  |     private readonly _maxSnapDistance: number | 
					
						
							|  |  |  |     private readonly _snappedPointTags: any; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |     private readonly _bounds: UIEventSource<BBox>; | 
					
						
							|  |  |  |     public readonly _matching_layer: UIEventSource<LayerConfig>; | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |     constructor(options: { | 
					
						
							| 
									
										
										
										
											2021-07-14 16:05:50 +02:00
										 |  |  |         mapBackground?: UIEventSource<BaseLayer>, | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         snapTo?: UIEventSource<{ feature: any }[]>, | 
					
						
							|  |  |  |         maxSnapDistance?: number, | 
					
						
							|  |  |  |         snappedPointTags?: any, | 
					
						
							|  |  |  |         requiresSnapping?: boolean, | 
					
						
							|  |  |  |         centerLocation: UIEventSource<Loc>, | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |         bounds?: UIEventSource<BBox> | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  |     }) { | 
					
						
							|  |  |  |         super(); | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         this._snapTo = options.snapTo?.map(features => features?.filter(feat => feat.feature.geometry.type !== "Point")) | 
					
						
							|  |  |  |         this._maxSnapDistance = options.maxSnapDistance | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  |         this._centerLocation = options.centerLocation; | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         this._snappedPointTags = options.snappedPointTags | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |         this._bounds = options.bounds; | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         if (this._snapTo === undefined) { | 
					
						
							|  |  |  |             this._value = this._centerLocation; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             const self = this; | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (self._snappedPointTags !== undefined) { | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                 this._matching_layer = State.state.layoutToUse.map(layout => { | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     for (const layer of layout.layers) { | 
					
						
							|  |  |  |                         if (layer.source.osmTags.matchesProperties(self._snappedPointTags)) { | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                             return layer | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     console.error("No matching layer found for tags ", self._snappedPointTags) | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                     return LocationInput.matchLayer | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                 }) | 
					
						
							|  |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                this._matching_layer = new UIEventSource<LayerConfig>(LocationInput.matchLayer) | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             this._snappedPoint = options.centerLocation.map(loc => { | 
					
						
							|  |  |  |                 if (loc === undefined) { | 
					
						
							|  |  |  |                     return undefined; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // We reproject the location onto every 'snap-to-feature' and select the closest
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 let min = undefined; | 
					
						
							|  |  |  |                 let matchedWay = undefined; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                 for (const feature of self._snapTo.data ?? []) { | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                     const nearestPointOnLine = GeoOperations.nearestPoint(feature.feature, [loc.lon, loc.lat]) | 
					
						
							|  |  |  |                     if (min === undefined) { | 
					
						
							|  |  |  |                         min = nearestPointOnLine | 
					
						
							|  |  |  |                         matchedWay = feature.feature; | 
					
						
							|  |  |  |                         continue; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if (min.properties.dist > nearestPointOnLine.properties.dist) { | 
					
						
							|  |  |  |                         min = nearestPointOnLine | 
					
						
							|  |  |  |                         matchedWay = feature.feature; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                 if (min === undefined || min.properties.dist * 1000 > self._maxSnapDistance) { | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                     if (options.requiresSnapping) { | 
					
						
							|  |  |  |                         return undefined | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         return { | 
					
						
							|  |  |  |                             "type": "Feature", | 
					
						
							|  |  |  |                             "properties": options.snappedPointTags ?? min.properties, | 
					
						
							|  |  |  |                             "geometry": {"type": "Point", "coordinates": [loc.lon, loc.lat]} | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 min.properties = options.snappedPointTags ?? min.properties | 
					
						
							|  |  |  |                 self.snappedOnto.setData(matchedWay) | 
					
						
							|  |  |  |                 return min | 
					
						
							|  |  |  |             }, [this._snapTo]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             this._value = this._snappedPoint.map(f => { | 
					
						
							|  |  |  |                 const [lon, lat] = f.geometry.coordinates; | 
					
						
							|  |  |  |                 return { | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |                     lon: lon, lat: lat, zoom: undefined | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         this.mapBackground = options.mapBackground ?? State.state.backgroundLayer ?? new UIEventSource(AvailableBaseLayers.osmCarto) | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  |         this.SetClass("block h-full") | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     GetValue(): UIEventSource<Loc> { | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         return this._value; | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IsValid(t: Loc): boolean { | 
					
						
							|  |  |  |         return t !== undefined; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected InnerConstructElement(): HTMLElement { | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |             const clickLocation = new UIEventSource<Loc>(undefined); | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             const map = Minimap.createMiniMap( | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                 { | 
					
						
							|  |  |  |                     location: this._centerLocation, | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |                     background: this.mapBackground, | 
					
						
							|  |  |  |                     attribution: this.mapBackground !== State.state.backgroundLayer, | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                     lastClickLocation: clickLocation, | 
					
						
							|  |  |  |                     bounds: this._bounds | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |             clickLocation.addCallbackAndRunD(location => this._centerLocation.setData(location)) | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |             map.installBounds(0.15, true); | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |             if (this._snapTo !== undefined) { | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Show the lines to snap to
 | 
					
						
							|  |  |  |                 new ShowDataMultiLayer({ | 
					
						
							|  |  |  |                         features: new StaticFeatureSource(this._snapTo, true), | 
					
						
							|  |  |  |                         enablePopups: false, | 
					
						
							|  |  |  |                         zoomToFeatures: false, | 
					
						
							|  |  |  |                         leafletMap: map.leafletMap, | 
					
						
							|  |  |  |                         layers: State.state.filteredLayers | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |                 // Show the central point
 | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                 const matchPoint = this._snappedPoint.map(loc => { | 
					
						
							|  |  |  |                     if (loc === undefined) { | 
					
						
							|  |  |  |                         return [] | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     return [{feature: loc}]; | 
					
						
							|  |  |  |                 }) | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                     new ShowDataLayer({ | 
					
						
							|  |  |  |                         features: new StaticFeatureSource(matchPoint, true), | 
					
						
							|  |  |  |                         enablePopups: false, | 
					
						
							|  |  |  |                         zoomToFeatures: false, | 
					
						
							|  |  |  |                         leafletMap: map.leafletMap, | 
					
						
							|  |  |  |                         layerToShow: this._matching_layer.data | 
					
						
							|  |  |  |                     }) | 
					
						
							|  |  |  |                      | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |             this.mapBackground.map(layer => { | 
					
						
							|  |  |  |                 const leaflet = map.leafletMap.data | 
					
						
							|  |  |  |                 if (leaflet === undefined || layer === undefined) { | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 leaflet.setMaxZoom(layer.max_zoom) | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |                 leaflet.setMinZoom(layer.max_zoom - 2) | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                 leaflet.setZoom(layer.max_zoom - 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             }, [map.leafletMap]) | 
					
						
							|  |  |  |             return new Combine([ | 
					
						
							|  |  |  |                 new Combine([ | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |                     Svg.move_arrows_ui() | 
					
						
							|  |  |  |                         .SetClass("block relative pointer-events-none") | 
					
						
							|  |  |  |                         .SetStyle("left: -2.5rem; top: -2.5rem; width: 5rem; height: 5rem") | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                 ]).SetClass("block w-0 h-0 z-10 relative") | 
					
						
							|  |  |  |                     .SetStyle("background: rgba(255, 128, 128, 0.21); left: 50%; top: 50%"), | 
					
						
							|  |  |  |                 map | 
					
						
							|  |  |  |                     .SetClass("z-0 relative block w-full h-full bg-gray-100") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             ]).ConstructElement(); | 
					
						
							|  |  |  |         } catch (e) { | 
					
						
							|  |  |  |             console.error("Could not generate LocationInputElement:", e) | 
					
						
							|  |  |  |             return undefined; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-14 00:17:15 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |