| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | <script lang="ts"> | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   import { UIEventSource } from "../../Logic/UIEventSource" | 
					
						
							|  |  |  |   import type { Feature } from "geojson" | 
					
						
							|  |  |  |   import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | 
					
						
							|  |  |  |   import ToSvelte from "../Base/ToSvelte.svelte" | 
					
						
							|  |  |  |   import Svg from "../../Svg.js" | 
					
						
							|  |  |  |   import Translations from "../i18n/Translations" | 
					
						
							|  |  |  |   import Loading from "../Base/Loading.svelte" | 
					
						
							|  |  |  |   import Hotkeys from "../Base/Hotkeys" | 
					
						
							|  |  |  |   import { Geocoding } from "../../Logic/Osm/Geocoding" | 
					
						
							|  |  |  |   import { BBox } from "../../Logic/BBox" | 
					
						
							|  |  |  |   import { GeoIndexedStoreForLayer } from "../../Logic/FeatureSource/Actors/GeoIndexedStore" | 
					
						
							|  |  |  |   import { createEventDispatcher, onDestroy } from "svelte" | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined | 
					
						
							|  |  |  |   export let bounds: UIEventSource<BBox> | 
					
						
							|  |  |  |   export let selectedElement: UIEventSource<Feature> | undefined = undefined | 
					
						
							|  |  |  |   export let selectedLayer: UIEventSource<LayerConfig> | undefined = undefined | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   export let clearAfterView: boolean = true | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   let searchContents: string = "" | 
					
						
							|  |  |  |   export let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined) | 
					
						
							|  |  |  |   onDestroy( | 
					
						
							|  |  |  |     triggerSearch.addCallback((_) => { | 
					
						
							|  |  |  |       performSearch() | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   ) | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   let isRunning: boolean = false | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   let inputElement: HTMLInputElement | 
					
						
							| 
									
										
										
										
											2023-04-14 02:42:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   let feedback: string = undefined | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   Hotkeys.RegisterHotkey({ ctrl: "F" }, Translations.t.hotkeyDocumentation.selectSearch, () => { | 
					
						
							|  |  |  |     inputElement?.focus() | 
					
						
							|  |  |  |     inputElement?.select() | 
					
						
							|  |  |  |   }) | 
					
						
							| 
									
										
										
										
											2023-04-14 02:42:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   const dispatch = createEventDispatcher<{ searchCompleted; searchIsValid: boolean }>() | 
					
						
							|  |  |  |   $: { | 
					
						
							|  |  |  |     if (!searchContents?.trim()) { | 
					
						
							|  |  |  |       dispatch("searchIsValid", false) | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       dispatch("searchIsValid", true) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-04-14 02:42:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   async function performSearch() { | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       isRunning = true | 
					
						
							|  |  |  |       searchContents = searchContents?.trim() ?? "" | 
					
						
							| 
									
										
										
										
											2023-05-18 15:44:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |       if (searchContents === "") { | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const result = await Geocoding.Search(searchContents, bounds.data) | 
					
						
							|  |  |  |       if (result.length == 0) { | 
					
						
							|  |  |  |         feedback = Translations.t.general.search.nothing.txt | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const poi = result[0] | 
					
						
							|  |  |  |       const [lat0, lat1, lon0, lon1] = poi.boundingbox | 
					
						
							|  |  |  |       bounds.set( | 
					
						
							|  |  |  |         new BBox([ | 
					
						
							|  |  |  |           [lon0, lat0], | 
					
						
							|  |  |  |           [lon1, lat1], | 
					
						
							|  |  |  |         ]).pad(0.01) | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       if (perLayer !== undefined) { | 
					
						
							|  |  |  |         const id = poi.osm_type + "/" + poi.osm_id | 
					
						
							|  |  |  |         const layers = Array.from(perLayer?.values() ?? []) | 
					
						
							|  |  |  |         for (const layer of layers) { | 
					
						
							|  |  |  |           const found = layer.features.data.find((f) => f.properties.id === id) | 
					
						
							|  |  |  |           selectedElement?.setData(found) | 
					
						
							|  |  |  |           selectedLayer?.setData(layer.layer.layerDef) | 
					
						
							| 
									
										
										
										
											2023-04-14 02:42:57 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |       if (clearAfterView) { | 
					
						
							|  |  |  |         searchContents = "" | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       dispatch("searchIsValid", false) | 
					
						
							|  |  |  |       dispatch("searchCompleted") | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       console.error(e) | 
					
						
							|  |  |  |       feedback = Translations.t.general.search.error.txt | 
					
						
							|  |  |  |     } finally { | 
					
						
							|  |  |  |       isRunning = false | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | </script> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:44:01 +02:00
										 |  |  | <div class="normal-background flex justify-between rounded-full pl-2"> | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |   <form class="w-full"> | 
					
						
							|  |  |  |     {#if isRunning} | 
					
						
							|  |  |  |       <Loading>{Translations.t.general.search.searching}</Loading> | 
					
						
							|  |  |  |     {:else if feedback !== undefined} | 
					
						
							|  |  |  |       <div class="alert" on:click={() => (feedback = undefined)}> | 
					
						
							|  |  |  |         {feedback} | 
					
						
							|  |  |  |       </div> | 
					
						
							|  |  |  |     {:else} | 
					
						
							|  |  |  |       <input | 
					
						
							|  |  |  |         type="search" | 
					
						
							|  |  |  |         class="w-full" | 
					
						
							|  |  |  |         bind:this={inputElement} | 
					
						
							|  |  |  |         on:keypress={(keypr) => (keypr.key === "Enter" ? performSearch() : undefined)} | 
					
						
							|  |  |  |         bind:value={searchContents} | 
					
						
							|  |  |  |         placeholder={Translations.t.general.search.search} | 
					
						
							|  |  |  |       /> | 
					
						
							|  |  |  |     {/if} | 
					
						
							|  |  |  |   </form> | 
					
						
							| 
									
										
										
										
											2023-06-14 20:44:01 +02:00
										 |  |  |   <div class="h-6 w-6 self-end" on:click={performSearch}> | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |     <ToSvelte construct={Svg.search_svg} /> | 
					
						
							|  |  |  |   </div> | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | </div> |