Feature(geocoding): pressing enter will now zoom to the first search result; refactor away type synonym

This commit is contained in:
Pieter Vander Vennet 2025-03-11 03:45:11 +01:00
parent 9e8aaab086
commit 686ad70511
8 changed files with 64 additions and 49 deletions

View file

@ -1,8 +1,4 @@
import GeocodingProvider, {
GeocodeResult,
GeocodingOptions,
SearchResult,
} from "./GeocodingProvider"
import GeocodingProvider, { GeocodeResult, GeocodingOptions } from "./GeocodingProvider"
import { Utils } from "../../Utils"
import { Store, Stores } from "../UIEventSource"
@ -44,12 +40,12 @@ export default class CombinedSearcher implements GeocodingProvider {
return results
}
async search(query: string, options?: GeocodingOptions): Promise<SearchResult[]> {
async search(query: string, options?: GeocodingOptions): Promise<GeocodeResult[]> {
const results = await Promise.all(this._providers.map((pr) => pr.search(query, options)))
return CombinedSearcher.merge(results)
}
suggest(query: string, options?: GeocodingOptions): Store<SearchResult[]> {
suggest(query: string, options?: GeocodingOptions): Store<GeocodeResult[]> {
return Stores.concat(
this._providersWithSuggest.map((pr) => pr.suggest(query, options))
).map((gcrss) => CombinedSearcher.merge(gcrss))

View file

@ -1,4 +1,4 @@
import { SearchResult } from "./GeocodingProvider"
import { GeocodeResult } from "./GeocodingProvider"
import { Store } from "../UIEventSource"
import { FeatureSource } from "../FeatureSource/FeatureSource"
import { Feature, Geometry } from "geojson"
@ -6,7 +6,7 @@ import { Feature, Geometry } from "geojson"
export default class GeocodingFeatureSource implements FeatureSource {
public features: Store<Feature<Geometry, Record<string, string>>[]>
constructor(provider: Store<SearchResult[]>) {
constructor(provider: Store<GeocodeResult[]>) {
this.features = provider.map((geocoded) => {
if (geocoded === undefined) {
return []

View file

@ -42,7 +42,6 @@ export type GeocodeResult = {
payload?: object
source?: string
}
export type SearchResult = GeocodeResult
export interface GeocodingOptions {
bbox?: BBox

View file

@ -1,4 +1,4 @@
import GeocodingProvider, { GeocodingOptions, SearchResult } from "./GeocodingProvider"
import GeocodingProvider, { GeocodeResult, GeocodingOptions } from "./GeocodingProvider"
import ThemeViewState from "../../Models/ThemeViewState"
import { Utils } from "../../Utils"
import { Feature } from "geojson"
@ -26,7 +26,7 @@ export default class LocalElementSearch implements GeocodingProvider {
this._limit = limit
}
async search(query: string, options?: GeocodingOptions): Promise<SearchResult[]> {
async search(query: string, options?: GeocodingOptions): Promise<GeocodeResult[]> {
return this.searchEntries(query, options, false).data
}
@ -92,7 +92,7 @@ export default class LocalElementSearch implements GeocodingProvider {
query: string,
options?: GeocodingOptions,
matchStart?: boolean
): Store<SearchResult[]> {
): Store<GeocodeResult[]> {
if (query.length < 3) {
return new ImmutableStore([])
}
@ -126,7 +126,7 @@ export default class LocalElementSearch implements GeocodingProvider {
}
return results.map((entry) => {
const [osm_type, osm_id] = entry.feature.properties.id.split("/")
return <SearchResult>{
return <GeocodeResult>{
lon: entry.center[0],
lat: entry.center[1],
osm_type,
@ -141,7 +141,7 @@ export default class LocalElementSearch implements GeocodingProvider {
})
}
suggest(query: string, options?: GeocodingOptions): Store<SearchResult[]> {
suggest(query: string, options?: GeocodingOptions): Store<GeocodeResult[]> {
return this.searchEntries(query, options, true)
}
}

View file

@ -3,7 +3,7 @@ import { BBox } from "../BBox"
import Constants from "../../Models/Constants"
import { FeatureCollection } from "geojson"
import Locale from "../../UI/i18n/Locale"
import GeocodingProvider, { GeocodingOptions, SearchResult } from "./GeocodingProvider"
import GeocodingProvider, { GeocodeResult, GeocodingOptions } from "./GeocodingProvider"
export class NominatimGeocoding implements GeocodingProvider {
private readonly _host
@ -15,7 +15,7 @@ export class NominatimGeocoding implements GeocodingProvider {
this._host = host
}
public search(query: string, options?: GeocodingOptions): Promise<SearchResult[]> {
public search(query: string, options?: GeocodingOptions): Promise<GeocodeResult[]> {
const b = options?.bbox ?? BBox.global
const url = `${this._host}search?format=json&limit=${
this.limit

View file

@ -1,4 +1,4 @@
import GeocodingProvider, { type SearchResult } from "../Search/GeocodingProvider"
import GeocodingProvider, { GeocodeResult, GeocodingUtils } from "../Search/GeocodingProvider"
import { ImmutableStore, Store, Stores, UIEventSource } from "../UIEventSource"
import CombinedSearcher from "../Search/CombinedSearcher"
import FilterSearch, { FilterSearchResult } from "../Search/FilterSearch"
@ -16,12 +16,13 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { FeatureSource } from "../FeatureSource/FeatureSource"
import { Feature } from "geojson"
import OpenLocationCodeSearch from "../Search/OpenLocationCodeSearch"
import { BBox } from "../BBox"
export default class SearchState {
public readonly feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
public readonly searchTerm: UIEventSource<string> = new UIEventSource<string>("")
public readonly searchIsFocused = new UIEventSource(false)
public readonly suggestions: Store<SearchResult[]>
public readonly suggestions: Store<GeocodeResult[]>
public readonly filterSuggestions: Store<FilterSearchResult[]>
public readonly themeSuggestions: Store<MinimalThemeInformation[]>
public readonly layerSuggestions: Store<LayerConfig[]>
@ -60,7 +61,7 @@ export default class SearchState {
return new ImmutableStore(true)
}
return Stores.concat(suggestions).map((suggestions) =>
suggestions.some((list, i) => list === undefined)
suggestions.some(list => list === undefined)
)
})
this.suggestions = suggestionsList.bindD((suggestions) =>
@ -100,7 +101,7 @@ export default class SearchState {
this.showSearchDrawer = new UIEventSource(false)
this.searchIsFocused.addCallbackAndRunD((sugg) => {
this.searchIsFocused.addCallbackAndRunD(sugg => {
if (sugg) {
this.showSearchDrawer.set(true)
}
@ -124,7 +125,6 @@ export default class SearchState {
const state = this.state
const layersToShow = payload.map((fsr) => fsr.layer.id)
console.log("Layers to show are", layersToShow)
for (const otherLayer of state.layerState.filteredLayers.values()) {
const layer = otherLayer.layerDef
if (!layer.isNormal()) {
@ -167,4 +167,45 @@ export default class SearchState {
this.state.featureProperties.trackFeature(f)
this.state.selectedElement.set(f)
}
public moveToBestMatch() {
const suggestion = this.suggestions.data?.[0]
if (suggestion) {
this.applyGeocodeResult(suggestion)
}
if (this.suggestionsSearchRunning.data) {
this.suggestionsSearchRunning.addCallback(() => {
this.applyGeocodeResult(this.suggestions.data?.[0])
return true // unregister
})
}
}
applyGeocodeResult(entry: GeocodeResult) {
if (!entry) {
console.error("ApplyGeocodeResult got undefined/null")
}
console.log("Moving to", entry.description)
const state = this.state
if (entry.boundingbox) {
const [lat0, lat1, lon0, lon1] = entry.boundingbox
state.mapProperties.bounds.set(
new BBox([
[lon0, lat0],
[lon1, lat1]
]).pad(0.01)
)
} else {
state.mapProperties.flyTo(
entry.lon,
entry.lat,
GeocodingUtils.categoryToZoomLevel[entry.category] ?? 17
)
}
if (entry.feature?.properties?.id) {
state.selectedElement.set(entry.feature)
}
state.userRelatedState.recentlyVisitedSearch.add(entry)
this.closeIfFullscreen()
}
}