Add layers to search menu
This commit is contained in:
parent
e6dab1a83f
commit
c591770eab
33 changed files with 332 additions and 195 deletions
163
src/Logic/Search/GeocodingProvider.ts
Normal file
163
src/Logic/Search/GeocodingProvider.ts
Normal file
|
@ -0,0 +1,163 @@
|
|||
import { BBox } from "../BBox"
|
||||
import { Feature, Geometry } from "geojson"
|
||||
import { DefaultPinIcon } from "../../Models/Constants"
|
||||
import { Store } from "../UIEventSource"
|
||||
import * as search from "../../assets/generated/layers/search.json"
|
||||
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"
|
||||
| "city"
|
||||
| "house"
|
||||
| "street"
|
||||
| "locality"
|
||||
| "country"
|
||||
| "train_station"
|
||||
| "county"
|
||||
| "airport"
|
||||
| "shop"
|
||||
|
||||
export type GeocodeResult = {
|
||||
/**
|
||||
* The name of the feature being displayed
|
||||
*/
|
||||
display_name: string
|
||||
/**
|
||||
* Some optional, extra information
|
||||
*/
|
||||
description?: string | Promise<string>,
|
||||
feature?: Feature,
|
||||
lat: number
|
||||
lon: number
|
||||
/**
|
||||
* Format:
|
||||
* [lat, lat, lon, lon]
|
||||
*/
|
||||
boundingbox?: number[]
|
||||
osm_type?: "node" | "way" | "relation"
|
||||
osm_id: string,
|
||||
category?: GeocodingCategory,
|
||||
payload?: object,
|
||||
source?: string
|
||||
}
|
||||
export type FilterPayload = { option: FilterConfigOption, filter: FilterConfig, layer: LayerConfig, index: number }
|
||||
export type FilterResult = { category: "filter", osm_id: string, payload: FilterPayload }
|
||||
export type LayerResult = {category: "layer", osm_id: string, payload: LayerConfig}
|
||||
export type SearchResult =
|
||||
| FilterResult
|
||||
| { category: "theme", osm_id: string, payload: MinimalLayoutInformation }
|
||||
| LayerResult
|
||||
| GeocodeResult
|
||||
|
||||
export interface GeocodingOptions {
|
||||
bbox?: BBox
|
||||
}
|
||||
|
||||
|
||||
export default interface GeocodingProvider<T extends SearchResult = SearchResult> {
|
||||
|
||||
|
||||
search(query: string, options?: GeocodingOptions): Promise<T[]>
|
||||
|
||||
/**
|
||||
* @param query
|
||||
* @param options
|
||||
*/
|
||||
suggest?(query: string, options?: GeocodingOptions): Store<T[]>
|
||||
}
|
||||
|
||||
export type ReverseGeocodingResult = Feature<Geometry, {
|
||||
osm_id: number,
|
||||
osm_type: "node" | "way" | "relation",
|
||||
country: string,
|
||||
city: string,
|
||||
countrycode: string,
|
||||
type: GeocodingCategory,
|
||||
street: string
|
||||
}>
|
||||
|
||||
export interface ReverseGeocodingProvider {
|
||||
reverseSearch(
|
||||
coordinate: { lon: number; lat: number },
|
||||
zoom: number,
|
||||
language?: string,
|
||||
): Promise<ReverseGeocodingResult[]>;
|
||||
}
|
||||
|
||||
export class GeocodingUtils {
|
||||
|
||||
public static searchLayer = GeocodingUtils.initSearchLayer()
|
||||
|
||||
private static initSearchLayer(): LayerConfig {
|
||||
if (search["id"] === undefined) {
|
||||
// We are resetting the layeroverview; trying to parse is useless
|
||||
return undefined
|
||||
}
|
||||
return new LayerConfig(<LayerConfigJson>search, "search")
|
||||
}
|
||||
|
||||
public static categoryToZoomLevel: Record<GeocodingCategory, number> = {
|
||||
city: 12,
|
||||
county: 10,
|
||||
coordinate: 16,
|
||||
country: 8,
|
||||
house: 16,
|
||||
locality: 14,
|
||||
street: 15,
|
||||
train_station: 14,
|
||||
airport: 13,
|
||||
shop: 16,
|
||||
|
||||
}
|
||||
|
||||
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",
|
||||
coordinate: "globe_alt",
|
||||
country: "globe_alt",
|
||||
house: "house",
|
||||
locality: "building_office_2",
|
||||
street: "globe_alt",
|
||||
train_station: "train",
|
||||
county: "building_office_2",
|
||||
airport: "airport",
|
||||
shop: "building_storefront",
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue