forked from MapComplete/MapComplete
First search with suggestions
This commit is contained in:
parent
874f82be82
commit
3cd04df60b
37 changed files with 677 additions and 85 deletions
|
|
@ -4,7 +4,6 @@
|
|||
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"
|
||||
|
|
@ -12,6 +11,12 @@
|
|||
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { ariaLabel } from "../../Utils/ariaLabel"
|
||||
import { GeoLocationState } from "../../Logic/State/GeoLocationState"
|
||||
import { NominatimGeocoding } from "../../Logic/Geocoding/NominatimGeocoding"
|
||||
import type GeocodingProvider from "../../Logic/Geocoding/GeocodingProvider"
|
||||
import type { GeoCodeResult } from "../../Logic/Geocoding/GeocodingProvider"
|
||||
|
||||
import SearchResults from "./SearchResults.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
|
||||
export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined
|
||||
export let bounds: UIEventSource<BBox>
|
||||
|
|
@ -19,6 +24,8 @@
|
|||
|
||||
export let geolocationState: GeoLocationState | undefined = undefined
|
||||
export let clearAfterView: boolean = true
|
||||
export let searcher : GeocodingProvider = new NominatimGeocoding()
|
||||
export let state : SpecialVisualizationState
|
||||
let searchContents: string = ""
|
||||
export let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
||||
onDestroy(
|
||||
|
|
@ -54,6 +61,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
async function performSearch() {
|
||||
try {
|
||||
isRunning = true
|
||||
|
|
@ -64,7 +72,8 @@
|
|||
if (searchContents === "") {
|
||||
return
|
||||
}
|
||||
const result = await Geocoding.Search(searchContents, bounds.data)
|
||||
const result = await searcher.search(searchContents, { bbox: bounds.data, limit: 10 })
|
||||
console.log("Results are", result)
|
||||
if (result.length == 0) {
|
||||
feedback = Translations.t.general.search.nothing.txt
|
||||
focusOnSearch()
|
||||
|
|
@ -104,6 +113,16 @@
|
|||
isRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
let suggestions: GeoCodeResult[] = []
|
||||
|
||||
async function updateSuggestions(search){
|
||||
|
||||
suggestions = await searcher.suggest(search, {limit: 5})
|
||||
}
|
||||
|
||||
$: updateSuggestions(searchContents)
|
||||
|
||||
</script>
|
||||
|
||||
<div class="normal-background flex justify-between rounded-full pl-2">
|
||||
|
|
@ -133,3 +152,7 @@
|
|||
</form>
|
||||
<SearchIcon aria-hidden="true" class="h-6 w-6 self-end" on:click={performSearch} />
|
||||
</div>
|
||||
|
||||
<div class="h-2/3 ">
|
||||
<SearchResults {state} results={suggestions}/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export default class MoreScreen {
|
|||
if (search === undefined) {
|
||||
return true
|
||||
}
|
||||
search = Utils.RemoveDiacritics(search.toLocaleLowerCase())
|
||||
search = Utils.RemoveDiacritics(search.toLocaleLowerCase()) // See #1729
|
||||
if (search.length > 3 && layout.id.toLowerCase().indexOf(search) >= 0) {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Shows the current address when shaken
|
||||
**/
|
||||
import Motion from "../../Sensors/Motion"
|
||||
import { Geocoding } from "../../Logic/Osm/Geocoding"
|
||||
import { NominatimGeocoding } from "../../Logic/Geocoding/NominatimGeocoding"
|
||||
import Hotkeys from "../Base/Hotkeys"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Locale from "../i18n/Locale"
|
||||
|
|
@ -15,9 +15,11 @@
|
|||
let lastDisplayed: Date = undefined
|
||||
let currentLocation: string = undefined
|
||||
|
||||
let geocoder = new NominatimGeocoding()
|
||||
|
||||
async function displayLocation() {
|
||||
lastDisplayed = new Date()
|
||||
let result = await Geocoding.reverse(
|
||||
let result = await geocoder.reverseSearch(
|
||||
mapProperties.location.data,
|
||||
mapProperties.zoom.data,
|
||||
Locale.language.data
|
||||
|
|
|
|||
46
src/UI/BigComponents/SearchResult.svelte
Normal file
46
src/UI/BigComponents/SearchResult.svelte
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<script lang="ts">
|
||||
import type { GeoCodeResult } from "../../Logic/Geocoding/GeocodingProvider"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
|
||||
export let entry: GeoCodeResult
|
||||
export let state: SpecialVisualizationState
|
||||
let layer: LayerConfig
|
||||
if (entry.feature) {
|
||||
layer = state.layout.getMatchingLayer(entry.feature.properties)
|
||||
}
|
||||
|
||||
let dispatch = createEventDispatcher<{select}>()
|
||||
let distance = state.mapProperties.location.mapD(l => GeoOperations.distanceBetween([l.lon, l.lat], [entry.lon, entry.lat]))
|
||||
|
||||
function select() {
|
||||
state.mapProperties.flyTo(entry.lon, entry.lat, 17)
|
||||
if (entry.feature) {
|
||||
state.selectedElement.set(entry.feature)
|
||||
}
|
||||
dispatch("select")
|
||||
}
|
||||
</script>
|
||||
<button class="unstyled w-full link-no-underline"
|
||||
on:click={() => select()}>
|
||||
<div class="p-2 flex items-center w-full gap-y-2 ">
|
||||
|
||||
{#if layer}
|
||||
<ToSvelte construct={() => layer.defaultIcon(entry.feature.properties).SetClass("w-6 h-6")} />
|
||||
{/if}
|
||||
<div class="flex flex-col items-start pl-2">
|
||||
<div class="flex">
|
||||
|
||||
{entry.display_name ?? entry.osm_id}
|
||||
</div>
|
||||
<div class="subtle">
|
||||
{#if $distance}
|
||||
{GeoOperations.distanceToHuman($distance)}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
27
src/UI/BigComponents/SearchResults.svelte
Normal file
27
src/UI/BigComponents/SearchResults.svelte
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<script lang="ts">
|
||||
import type { GeoCodeResult } from "../../Logic/Geocoding/GeocodingProvider"
|
||||
import SearchResult from "./SearchResult.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { XMarkIcon } from "@babeard/svelte-heroicons/solid"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let results: GeoCodeResult[]
|
||||
|
||||
function close(){
|
||||
results = []
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if results.length > 0}
|
||||
<div class="relative w-full">
|
||||
|
||||
<div class="absolute top-0 left-0 flex flex-col gap-y-2 normal-background p-2 rounded-xl border border-black w-full">
|
||||
{#each results as entry (entry)}
|
||||
<SearchResult on:select={() => close()} {entry} {state} />
|
||||
{/each}
|
||||
</div>
|
||||
<div class="absolute top-2 right-2" on:click={() => close()}>
|
||||
<XMarkIcon class="w-4 h-4 hover:bg-stone-200 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -100,6 +100,8 @@
|
|||
{selectedElement}
|
||||
{triggerSearch}
|
||||
geolocationState={state.geolocation.geolocationState}
|
||||
searcher={state.geosearch}
|
||||
{state}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -679,4 +679,11 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public flyTo(lon: number, lat: number, zoom: number){
|
||||
this._maplibreMap.data?.flyTo({
|
||||
zoom,
|
||||
center: [lon, lat],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,8 @@
|
|||
</div>
|
||||
|
||||
{#if $reason.includeSearch}
|
||||
<Geosearch bounds={currentMapProperties.bounds} clearAfterView={false} />
|
||||
searcher={state.geosearch}
|
||||
<Geosearch bounds={currentMapProperties.bounds} clearAfterView={false} searcher={state.geosearch} {state}/>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-wrap">
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export interface SpecialVisualizationState {
|
|||
readonly layerState: LayerState
|
||||
readonly featureSummary: SummaryTileSourceRewriter
|
||||
readonly featureProperties: {
|
||||
getStore(id: string): UIEventSource<Record<string, string>>
|
||||
getStore(id: string): UIEventSource<Record<string, string>>,
|
||||
trackFeature?(feature: { properties: OsmTags })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -249,6 +249,8 @@
|
|||
perLayer={state.perLayer}
|
||||
selectedElement={state.selectedElement}
|
||||
geolocationState={state.geolocation.geolocationState}
|
||||
searcher={state.geosearch}
|
||||
{state}
|
||||
/>
|
||||
</If>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue