2024-09-12 16:14:57 +02:00
|
|
|
import GeocodingProvider, { type SearchResult } from "../Search/GeocodingProvider"
|
2024-09-03 01:14:08 +02:00
|
|
|
import { ImmutableStore, Store, Stores, UIEventSource } from "../UIEventSource"
|
2024-09-11 01:46:55 +02:00
|
|
|
import CombinedSearcher from "../Search/CombinedSearcher"
|
2024-09-11 17:31:38 +02:00
|
|
|
import FilterSearch, { FilterSearchResult } from "../Search/FilterSearch"
|
2024-09-11 01:46:55 +02:00
|
|
|
import LocalElementSearch from "../Search/LocalElementSearch"
|
|
|
|
import CoordinateSearch from "../Search/CoordinateSearch"
|
|
|
|
import ThemeSearch from "../Search/ThemeSearch"
|
|
|
|
import OpenStreetMapIdSearch from "../Search/OpenStreetMapIdSearch"
|
|
|
|
import PhotonSearch from "../Search/PhotonSearch"
|
2024-08-30 02:18:29 +02:00
|
|
|
import ThemeViewState from "../../Models/ThemeViewState"
|
|
|
|
import type { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
|
|
|
import { Translation } from "../../UI/i18n/Translation"
|
2024-09-11 01:46:55 +02:00
|
|
|
import GeocodingFeatureSource from "../Search/GeocodingFeatureSource"
|
|
|
|
import LayerSearch from "../Search/LayerSearch"
|
2024-09-11 17:31:38 +02:00
|
|
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
2024-09-12 16:14:57 +02:00
|
|
|
import { FeatureSource } from "../FeatureSource/FeatureSource"
|
|
|
|
import { Feature } from "geojson"
|
2024-08-30 02:18:29 +02:00
|
|
|
|
|
|
|
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[]>
|
2024-09-11 17:31:38 +02:00
|
|
|
public readonly filterSuggestions: Store<FilterSearchResult[]>
|
2024-08-30 02:18:29 +02:00
|
|
|
public readonly themeSuggestions: Store<MinimalLayoutInformation[]>
|
2024-09-11 17:31:38 +02:00
|
|
|
public readonly layerSuggestions: Store<LayerConfig[]>
|
|
|
|
public readonly locationSearchers: ReadonlyArray<GeocodingProvider>
|
2024-08-30 02:18:29 +02:00
|
|
|
|
|
|
|
private readonly state: ThemeViewState
|
|
|
|
public readonly showSearchDrawer: UIEventSource<boolean>
|
2024-09-03 01:14:08 +02:00
|
|
|
public readonly suggestionsSearchRunning: Store<boolean>
|
2024-09-12 16:14:57 +02:00
|
|
|
public readonly locationResults: FeatureSource
|
2024-08-30 02:18:29 +02:00
|
|
|
|
|
|
|
constructor(state: ThemeViewState) {
|
|
|
|
this.state = state
|
|
|
|
|
2024-09-03 01:14:08 +02:00
|
|
|
this.locationSearchers = [
|
2024-09-11 17:31:38 +02:00
|
|
|
new LocalElementSearch(state, 5),
|
2024-08-30 02:18:29 +02:00
|
|
|
new CoordinateSearch(),
|
|
|
|
new OpenStreetMapIdSearch(state),
|
2024-09-11 01:46:55 +02:00
|
|
|
new PhotonSearch(), // new NominatimGeocoding(),
|
2024-09-03 01:14:08 +02:00
|
|
|
]
|
2024-08-30 02:18:29 +02:00
|
|
|
|
|
|
|
const bounds = state.mapProperties.bounds
|
2024-09-03 01:14:08 +02:00
|
|
|
const suggestionsList = this.searchTerm.stabilized(250).mapD(search => {
|
2024-08-30 02:18:29 +02:00
|
|
|
if (search.length === 0) {
|
|
|
|
return undefined
|
|
|
|
}
|
2024-09-03 01:14:08 +02:00
|
|
|
return this.locationSearchers.map(ls => ls.suggest(search, { bbox: bounds.data }))
|
|
|
|
|
2024-09-11 01:46:55 +02:00
|
|
|
}, [bounds],
|
2024-09-03 01:14:08 +02:00
|
|
|
)
|
2024-09-11 01:46:55 +02:00
|
|
|
this.suggestionsSearchRunning = suggestionsList.bind(suggestions => {
|
|
|
|
if (suggestions === undefined) {
|
2024-09-03 01:14:08 +02:00
|
|
|
return new ImmutableStore(true)
|
2024-08-30 02:18:29 +02:00
|
|
|
}
|
2024-09-03 01:14:08 +02:00
|
|
|
return Stores.concat(suggestions).map(suggestions => suggestions.some(list => list === undefined))
|
|
|
|
})
|
|
|
|
this.suggestions = suggestionsList.bindD(suggestions =>
|
2024-09-11 01:46:55 +02:00
|
|
|
Stores.concat(suggestions).map(suggestions => CombinedSearcher.merge(suggestions)),
|
2024-08-30 02:18:29 +02:00
|
|
|
)
|
|
|
|
|
2024-09-11 17:31:38 +02:00
|
|
|
const themeSearch = new ThemeSearch(state)
|
|
|
|
this.themeSuggestions = this.searchTerm.mapD(query => themeSearch.search(query, 3))
|
2024-08-30 02:18:29 +02:00
|
|
|
|
2024-09-12 16:14:57 +02:00
|
|
|
const layerSearch = new LayerSearch(state.layout)
|
2024-09-11 17:31:38 +02:00
|
|
|
this.layerSuggestions = this.searchTerm.mapD(query => layerSearch.search(query, 5))
|
2024-08-30 02:18:29 +02:00
|
|
|
|
|
|
|
const filterSearch = new FilterSearch(state)
|
2024-09-11 01:46:55 +02:00
|
|
|
this.filterSuggestions = this.searchTerm.stabilized(50)
|
2024-09-11 17:31:38 +02:00
|
|
|
.mapD(query => filterSearch.search(query))
|
2024-09-11 01:46:55 +02:00
|
|
|
.mapD(filterResult => {
|
|
|
|
const active = state.layerState.activeFilters.data
|
2024-09-11 17:31:38 +02:00
|
|
|
return filterResult.filter(({ filter, index, layer }) => {
|
2024-09-11 01:46:55 +02:00
|
|
|
const foundMatch = active.some(active =>
|
|
|
|
active.filter.id === filter.id && layer.id === active.layer.id && active.control.data === index)
|
|
|
|
|
|
|
|
return !foundMatch
|
|
|
|
})
|
|
|
|
}, [state.layerState.activeFilters])
|
2024-09-17 02:16:25 +02:00
|
|
|
this.locationResults = new GeocodingFeatureSource(this.suggestions.stabilized(250))
|
2024-08-30 02:18:29 +02:00
|
|
|
|
|
|
|
this.showSearchDrawer = new UIEventSource(false)
|
2024-09-03 01:14:08 +02:00
|
|
|
|
2024-08-30 02:18:29 +02:00
|
|
|
this.searchIsFocused.addCallbackAndRunD(sugg => {
|
|
|
|
if (sugg) {
|
|
|
|
this.showSearchDrawer.set(true)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-09-17 02:16:25 +02:00
|
|
|
public async apply(result: FilterSearchResult[] | LayerConfig) {
|
2024-09-11 17:31:38 +02:00
|
|
|
if (result instanceof LayerConfig) {
|
2024-09-11 01:46:55 +02:00
|
|
|
return this.applyLayer(result)
|
|
|
|
}
|
2024-09-11 17:31:38 +02:00
|
|
|
return this.applyFilter(result)
|
2024-09-11 01:46:55 +02:00
|
|
|
}
|
|
|
|
|
2024-09-11 17:31:38 +02:00
|
|
|
private async applyLayer(layer: LayerConfig) {
|
2024-09-11 01:46:55 +02:00
|
|
|
for (const [name, otherLayer] of this.state.layerState.filteredLayers) {
|
2024-09-11 17:31:38 +02:00
|
|
|
otherLayer.isDisplayed.setData(name === layer.id)
|
2024-09-11 01:46:55 +02:00
|
|
|
}
|
|
|
|
}
|
2024-08-30 02:18:29 +02:00
|
|
|
|
2024-09-17 02:16:25 +02:00
|
|
|
private async applyFilter(payload: FilterSearchResult[]) {
|
2024-08-30 02:18:29 +02:00
|
|
|
const state = this.state
|
|
|
|
|
2024-09-17 02:16:25 +02:00
|
|
|
const layers = payload.map(fsr => fsr.layer.id)
|
2024-08-30 02:18:29 +02:00
|
|
|
for (const [name, otherLayer] of state.layerState.filteredLayers) {
|
2024-09-15 02:25:57 +02:00
|
|
|
const layer = otherLayer.layerDef
|
2024-09-17 02:16:25 +02:00
|
|
|
if (!layer.isNormal()) {
|
2024-09-15 02:25:57 +02:00
|
|
|
continue
|
|
|
|
}
|
2024-09-17 02:16:25 +02:00
|
|
|
if(otherLayer.layerDef.minzoom > state.mapProperties.minzoom.data) {
|
|
|
|
// Currently not displayed, we don't hide
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
otherLayer.isDisplayed.setData(layers.indexOf(layer.id) > 0)
|
2024-08-30 02:18:29 +02:00
|
|
|
}
|
2024-09-17 02:16:25 +02:00
|
|
|
for (const { filter, index, layer } of payload) {
|
|
|
|
const flayer = state.layerState.filteredLayers.get(layer.id)
|
|
|
|
flayer.isDisplayed.set(true)
|
|
|
|
const filtercontrol = flayer.appliedFilters.get(filter.id)
|
|
|
|
if (filtercontrol.data === index) {
|
|
|
|
filtercontrol.setData(undefined)
|
|
|
|
} else {
|
|
|
|
filtercontrol.setData(index)
|
|
|
|
}
|
2024-08-30 02:18:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-12 16:14:57 +02:00
|
|
|
closeIfFullscreen() {
|
2024-09-17 02:16:25 +02:00
|
|
|
if (window.innerWidth < 640) {
|
2024-09-12 16:14:57 +02:00
|
|
|
this.showSearchDrawer.set(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
clickedOnMap(feature: Feature) {
|
|
|
|
const osmid = feature.properties.osm_id
|
|
|
|
const localElement = this.state.indexedFeatures.featuresById.data.get(osmid)
|
2024-09-17 02:16:25 +02:00
|
|
|
if (localElement) {
|
2024-09-12 16:14:57 +02:00
|
|
|
this.state.selectedElement.set(localElement)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2024-08-30 02:18:29 +02:00
|
|
|
}
|