Search: move limit responsability to the constructor, merge similar results
This commit is contained in:
parent
cdc1e05499
commit
6468e33d66
7 changed files with 62 additions and 23 deletions
|
@ -12,7 +12,7 @@ export default class FilterSearch implements GeocodingProvider {
|
|||
|
||||
}
|
||||
|
||||
async search(query: string, options?: GeocodingOptions): Promise<SearchResult[]> {
|
||||
async search(query: string): Promise<SearchResult[]> {
|
||||
return this.searchDirectly(query)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
|||
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||
import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/FilterConfig"
|
||||
import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { GeoOperations } from "../GeoOperations"
|
||||
|
||||
export type GeocodingCategory =
|
||||
"coordinate"
|
||||
|
@ -50,8 +51,7 @@ export type SearchResult =
|
|||
| GeocodeResult
|
||||
|
||||
export interface GeocodingOptions {
|
||||
bbox?: BBox,
|
||||
limit?: number
|
||||
bbox?: BBox
|
||||
}
|
||||
|
||||
|
||||
|
@ -111,6 +111,36 @@ export class GeocodingUtils {
|
|||
|
||||
}
|
||||
|
||||
public static mergeSimilarResults(results: GeocodeResult[]){
|
||||
const byName: Record<string, GeocodeResult[]> = {}
|
||||
|
||||
|
||||
for (const result of results) {
|
||||
const nm = result.display_name
|
||||
if(!byName[nm]) {
|
||||
byName[nm] = []
|
||||
}
|
||||
byName[nm].push(result)
|
||||
}
|
||||
|
||||
const merged: GeocodeResult[] = []
|
||||
for (const nm in byName) {
|
||||
const options = byName[nm]
|
||||
const added = options[0]
|
||||
merged.push(added)
|
||||
const centers: [number,number][] = [[added.lon, added.lat]]
|
||||
for (const other of options) {
|
||||
const otherCenter:[number,number] = [other.lon, other.lat]
|
||||
const nearbyFound= centers.some(center => GeoOperations.distanceBetween(center, otherCenter) < 500)
|
||||
if(!nearbyFound){
|
||||
merged.push(other)
|
||||
centers.push(otherCenter)
|
||||
}
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
|
||||
public static categoryToIcon: Record<GeocodingCategory, DefaultPinIcon> = {
|
||||
city: "building_office_2",
|
||||
|
|
|
@ -95,8 +95,8 @@ export default class LocalElementSearch implements GeocodingProvider {
|
|||
const listed: Store<IntermediateResult[]> = Stores.concat(partials).map(l => l.flatMap(x => x))
|
||||
return listed.mapD(results => {
|
||||
results.sort((a, b) => (a.physicalDistance + a.levehnsteinD * 25) - (b.physicalDistance + b.levehnsteinD * 25))
|
||||
if (this._limit || options?.limit) {
|
||||
results = results.slice(0, Math.min(this._limit ?? options?.limit, options?.limit ?? this._limit))
|
||||
if (this._limit) {
|
||||
results = results.slice(0, this._limit)
|
||||
}
|
||||
return results.map(entry => {
|
||||
const [osm_type, osm_id] = entry.feature.properties.id.split("/")
|
||||
|
|
|
@ -3,21 +3,23 @@ import { BBox } from "../BBox"
|
|||
import Constants from "../../Models/Constants"
|
||||
import { FeatureCollection } from "geojson"
|
||||
import Locale from "../../UI/i18n/Locale"
|
||||
import GeocodingProvider, { SearchResult } from "./GeocodingProvider"
|
||||
import GeocodingProvider, { GeocodingOptions, SearchResult } from "./GeocodingProvider"
|
||||
|
||||
export class NominatimGeocoding implements GeocodingProvider {
|
||||
|
||||
private readonly _host ;
|
||||
private readonly limit: number
|
||||
|
||||
constructor(host: string = Constants.nominatimEndpoint) {
|
||||
constructor(limit: number = 3, host: string = Constants.nominatimEndpoint) {
|
||||
this.limit = limit
|
||||
this._host = host
|
||||
}
|
||||
|
||||
public search(query: string, options?: { bbox?: BBox; limit?: number }): Promise<SearchResult[]> {
|
||||
public search(query: string, options?:GeocodingOptions): Promise<SearchResult[]> {
|
||||
const b = options?.bbox ?? BBox.global
|
||||
const url = `${
|
||||
this._host
|
||||
}search?format=json&limit=${options?.limit ?? 1}&viewbox=${b.getEast()},${b.getNorth()},${b.getWest()},${b.getSouth()}&accept-language=${
|
||||
}search?format=json&limit=${this.limit}&viewbox=${b.getEast()},${b.getNorth()},${b.getWest()},${b.getSouth()}&accept-language=${
|
||||
Locale.language.data
|
||||
}&q=${query}`
|
||||
return Utils.downloadJson(url)
|
||||
|
|
|
@ -2,7 +2,7 @@ import Constants from "../../Models/Constants"
|
|||
import GeocodingProvider, {
|
||||
GeocodeResult,
|
||||
GeocodingCategory,
|
||||
GeocodingOptions,
|
||||
GeocodingOptions, GeocodingUtils,
|
||||
ReverseGeocodingProvider,
|
||||
ReverseGeocodingResult,
|
||||
} from "./GeocodingProvider"
|
||||
|
@ -20,9 +20,13 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding
|
|||
"W": "way",
|
||||
"N": "node",
|
||||
}
|
||||
private readonly suggestionLimit: number = 5
|
||||
private readonly searchLimit: number = 1
|
||||
|
||||
|
||||
constructor(endpoint?: string) {
|
||||
constructor(suggestionLimit:number = 5, searchLimit:number = 1, endpoint?: string) {
|
||||
this.suggestionLimit = suggestionLimit
|
||||
this.searchLimit = searchLimit
|
||||
this._endpoint = endpoint ?? Constants.photonEndpoint ?? "https://photon.komoot.io/"
|
||||
}
|
||||
|
||||
|
@ -55,7 +59,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding
|
|||
}
|
||||
|
||||
suggest(query: string, options?: GeocodingOptions): Store<GeocodeResult[]> {
|
||||
return Stores.FromPromise(this.search(query, options))
|
||||
return Stores.FromPromise(this.search(query, options, this.suggestionLimit))
|
||||
}
|
||||
|
||||
private buildDescription(entry: Feature) {
|
||||
|
@ -107,11 +111,11 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding
|
|||
return p.type
|
||||
}
|
||||
|
||||
async search(query: string, options?: GeocodingOptions): Promise<GeocodeResult[]> {
|
||||
async search(query: string, options?: GeocodingOptions, limit?: number): Promise<GeocodeResult[]> {
|
||||
if (query.length < 3) {
|
||||
return []
|
||||
}
|
||||
const limit = options?.limit ?? 5
|
||||
limit ??= this.searchLimit
|
||||
let bbox = ""
|
||||
if (options?.bbox) {
|
||||
const [lon, lat] = options.bbox.center()
|
||||
|
@ -119,7 +123,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding
|
|||
}
|
||||
const url = `${this._endpoint}/api/?q=${encodeURIComponent(query)}&limit=${limit}${this.getLanguage()}${bbox}`
|
||||
const results = await Utils.downloadJsonCached<FeatureCollection>(url, 1000 * 60 * 60)
|
||||
return results.features.map(f => {
|
||||
const encoded= results.features.map(f => {
|
||||
const [lon, lat] = GeoOperations.centerpointCoordinates(f)
|
||||
let boundingbox: number[] = undefined
|
||||
if (f.properties.extent) {
|
||||
|
@ -138,6 +142,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding
|
|||
source: this._endpoint,
|
||||
}
|
||||
})
|
||||
return GeocodingUtils.mergeSimilarResults(encoded)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,31 +11,33 @@ export default class ThemeSearch implements GeocodingProvider {
|
|||
private static allThemes: MinimalLayoutInformation[] = (themeOverview["default"] ?? themeOverview)
|
||||
private readonly _state: SpecialVisualizationState
|
||||
private readonly _knownHiddenThemes: Store<Set<string>>
|
||||
private readonly _suggestionLimit: number
|
||||
|
||||
constructor(state: SpecialVisualizationState) {
|
||||
constructor(state: SpecialVisualizationState, suggestionLimit: number) {
|
||||
this._state = state
|
||||
this._suggestionLimit = suggestionLimit
|
||||
this._knownHiddenThemes = MoreScreen.knownHiddenThemes(this._state.osmConnection)
|
||||
}
|
||||
|
||||
async search(query: string, options?: GeocodingOptions): Promise<SearchResult[]> {
|
||||
return this.searchDirect(query, options)
|
||||
async search(query: string): Promise<SearchResult[]> {
|
||||
return this.searchDirect(query, 99)
|
||||
}
|
||||
|
||||
suggest(query: string, options?: GeocodingOptions): Store<SearchResult[]> {
|
||||
return new ImmutableStore(this.searchDirect(query, options))
|
||||
return new ImmutableStore(this.searchDirect(query, this._suggestionLimit ?? 4))
|
||||
}
|
||||
|
||||
private searchDirect(query: string, options?: GeocodingOptions): SearchResult[] {
|
||||
private searchDirect(query: string, limit: number): SearchResult[] {
|
||||
if(query.length < 1){
|
||||
return []
|
||||
}
|
||||
const limit = options?.limit ?? 4
|
||||
query = Utils.simplifyStringForSearch(query)
|
||||
const withMatch = ThemeSearch.allThemes
|
||||
.filter(th => !th.hideFromOverview || this._knownHiddenThemes.data.has(th.id))
|
||||
.filter(th => th.id !== this._state.layout.id)
|
||||
.filter(th => MoreScreen.MatchesLayout(th, query))
|
||||
.slice(0, limit + 1)
|
||||
.slice(0, limit)
|
||||
console.log("Matched", withMatch, limit)
|
||||
|
||||
return withMatch.map(match => <SearchResult> {
|
||||
payload: match,
|
||||
|
|
|
@ -388,9 +388,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
new FilterSearch(this),
|
||||
new LocalElementSearch(this, 5),
|
||||
new CoordinateSearch(),
|
||||
this.featureSwitches.featureSwitchBackToThemeOverview.data ? new ThemeSearch(this, 3) : undefined,
|
||||
new OpenStreetMapIdSearch(this),
|
||||
new PhotonSearch(), // new NominatimGeocoding(),
|
||||
this.featureSwitches.featureSwitchBackToThemeOverview.data ? new ThemeSearch(this) : undefined
|
||||
)
|
||||
|
||||
this.recentlySearched = new RecentSearch(this)
|
||||
|
|
Loading…
Reference in a new issue