forked from MapComplete/MapComplete
Add layers to search menu
This commit is contained in:
parent
e6dab1a83f
commit
c591770eab
33 changed files with 332 additions and 195 deletions
|
@ -1,25 +1,26 @@
|
|||
import GeocodingProvider, {
|
||||
FilterPayload,
|
||||
FilterPayload, FilterResult,
|
||||
GeocodeResult,
|
||||
GeocodingUtils,
|
||||
type SearchResult
|
||||
} from "../Geocoding/GeocodingProvider"
|
||||
GeocodingUtils, LayerResult,
|
||||
type SearchResult,
|
||||
} from "../Search/GeocodingProvider"
|
||||
import { ImmutableStore, Store, Stores, UIEventSource } from "../UIEventSource"
|
||||
import CombinedSearcher from "../Geocoding/CombinedSearcher"
|
||||
import FilterSearch from "../Geocoding/FilterSearch"
|
||||
import LocalElementSearch from "../Geocoding/LocalElementSearch"
|
||||
import CoordinateSearch from "../Geocoding/CoordinateSearch"
|
||||
import ThemeSearch from "../Geocoding/ThemeSearch"
|
||||
import OpenStreetMapIdSearch from "../Geocoding/OpenStreetMapIdSearch"
|
||||
import PhotonSearch from "../Geocoding/PhotonSearch"
|
||||
import CombinedSearcher from "../Search/CombinedSearcher"
|
||||
import FilterSearch from "../Search/FilterSearch"
|
||||
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"
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
import Translations from "../../UI/i18n/Translations"
|
||||
import type { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import MoreScreen from "../../UI/BigComponents/MoreScreen"
|
||||
import { BBox } from "../BBox"
|
||||
import { Translation } from "../../UI/i18n/Translation"
|
||||
import GeocodingFeatureSource from "../Geocoding/GeocodingFeatureSource"
|
||||
import GeocodingFeatureSource from "../Search/GeocodingFeatureSource"
|
||||
import ShowDataLayer from "../../UI/Map/ShowDataLayer"
|
||||
import LayerSearch from "../Search/LayerSearch"
|
||||
|
||||
export default class SearchState {
|
||||
|
||||
|
@ -28,8 +29,9 @@ export default class SearchState {
|
|||
public readonly searchTerm: UIEventSource<string> = new UIEventSource<string>("")
|
||||
public readonly searchIsFocused = new UIEventSource(false)
|
||||
public readonly suggestions: Store<SearchResult[]>
|
||||
public readonly filterSuggestions: Store<FilterPayload[]>
|
||||
public readonly filterSuggestions: Store<FilterResult[]>
|
||||
public readonly themeSuggestions: Store<MinimalLayoutInformation[]>
|
||||
public readonly layerSuggestions: Store<LayerResult[]>
|
||||
public readonly locationSearchers: ReadonlyArray<GeocodingProvider<GeocodeResult>>
|
||||
|
||||
private readonly state: ThemeViewState
|
||||
|
@ -43,7 +45,7 @@ export default class SearchState {
|
|||
// new LocalElementSearch(state, 5),
|
||||
new CoordinateSearch(),
|
||||
new OpenStreetMapIdSearch(state),
|
||||
new PhotonSearch() // new NominatimGeocoding(),
|
||||
new PhotonSearch(), // new NominatimGeocoding(),
|
||||
]
|
||||
|
||||
const bounds = state.mapProperties.bounds
|
||||
|
@ -53,33 +55,36 @@ export default class SearchState {
|
|||
}
|
||||
return this.locationSearchers.map(ls => ls.suggest(search, { bbox: bounds.data }))
|
||||
|
||||
}, [bounds]
|
||||
}, [bounds],
|
||||
)
|
||||
this.suggestionsSearchRunning = suggestionsList.bind(suggestions => {
|
||||
if(suggestions === undefined){
|
||||
this.suggestionsSearchRunning = suggestionsList.bind(suggestions => {
|
||||
if (suggestions === undefined) {
|
||||
return new ImmutableStore(true)
|
||||
}
|
||||
return Stores.concat(suggestions).map(suggestions => suggestions.some(list => list === undefined))
|
||||
})
|
||||
this.suggestions = suggestionsList.bindD(suggestions =>
|
||||
Stores.concat(suggestions).map(suggestions => CombinedSearcher.merge(suggestions))
|
||||
Stores.concat(suggestions).map(suggestions => CombinedSearcher.merge(suggestions)),
|
||||
)
|
||||
|
||||
const themeSearch = new ThemeSearch(state, 3)
|
||||
this.themeSuggestions = this.searchTerm.mapD(query => themeSearch.searchDirect(query, 3))
|
||||
|
||||
const layerSearch = new LayerSearch(state, 5)
|
||||
this.layerSuggestions = this.searchTerm.mapD(query => layerSearch.searchDirect(query, 5))
|
||||
|
||||
const filterSearch = new FilterSearch(state)
|
||||
this.filterSuggestions = this.searchTerm.stabilized(50).mapD(query => filterSearch.searchDirectly(query)
|
||||
).mapD(filterResult => {
|
||||
const active = state.layerState.activeFilters.data
|
||||
return filterResult.filter(({ filter, index, layer }) => {
|
||||
const foundMatch = active.some(active =>
|
||||
active.filter.id === filter.id && layer.id === active.layer.id && active.control.data === index)
|
||||
this.filterSuggestions = this.searchTerm.stabilized(50)
|
||||
.mapD(query => filterSearch.searchDirectly(query))
|
||||
.mapD(filterResult => {
|
||||
const active = state.layerState.activeFilters.data
|
||||
return filterResult.filter(({ payload: { filter, index, layer } }) => {
|
||||
const foundMatch = active.some(active =>
|
||||
active.filter.id === filter.id && layer.id === active.layer.id && active.control.data === index)
|
||||
|
||||
return !foundMatch
|
||||
})
|
||||
}, [state.layerState.activeFilters])
|
||||
return !foundMatch
|
||||
})
|
||||
}, [state.layerState.activeFilters])
|
||||
const geocodedFeatures = new GeocodingFeatureSource(this.suggestions.stabilized(250))
|
||||
state.featureProperties.trackFeatureSource(geocodedFeatures)
|
||||
|
||||
|
@ -88,8 +93,8 @@ export default class SearchState {
|
|||
{
|
||||
layer: GeocodingUtils.searchLayer,
|
||||
features: geocodedFeatures,
|
||||
selectedElement: state.selectedElement
|
||||
}
|
||||
selectedElement: state.selectedElement,
|
||||
},
|
||||
)
|
||||
|
||||
this.showSearchDrawer = new UIEventSource(false)
|
||||
|
@ -103,21 +108,31 @@ export default class SearchState {
|
|||
|
||||
}
|
||||
|
||||
|
||||
public async apply(payload: FilterPayload) {
|
||||
const state = this.state
|
||||
const { layer, filter, index } = payload
|
||||
|
||||
const flayer = state.layerState.filteredLayers.get(layer.id)
|
||||
const filtercontrol = flayer.appliedFilters.get(filter.id)
|
||||
for (const [name, otherLayer] of state.layerState.filteredLayers) {
|
||||
if (name === layer.id) {
|
||||
otherLayer.isDisplayed.setData(true)
|
||||
continue
|
||||
}
|
||||
otherLayer.isDisplayed.setData(false)
|
||||
public async apply(result: FilterResult | LayerResult) {
|
||||
if (result.category === "filter") {
|
||||
return this.applyFilter(result.payload)
|
||||
}
|
||||
if (result.category === "layer") {
|
||||
return this.applyLayer(result)
|
||||
}
|
||||
}
|
||||
|
||||
private async applyLayer(layer: LayerResult) {
|
||||
for (const [name, otherLayer] of this.state.layerState.filteredLayers) {
|
||||
otherLayer.isDisplayed.setData(name === layer.osm_id)
|
||||
}
|
||||
}
|
||||
|
||||
private async applyFilter(payload: FilterPayload) {
|
||||
const state = this.state
|
||||
|
||||
const { layer, filter, index } = payload
|
||||
for (const [name, otherLayer] of state.layerState.filteredLayers) {
|
||||
otherLayer.isDisplayed.setData(name === layer.id)
|
||||
}
|
||||
const flayer = state.layerState.filteredLayers.get(layer.id)
|
||||
flayer.isDisplayed.set(true)
|
||||
const filtercontrol = flayer.appliedFilters.get(filter.id)
|
||||
console.log("Could not apply", layer.id, ".", filter.id, index)
|
||||
if (filtercontrol.data === index) {
|
||||
filtercontrol.setData(undefined)
|
||||
|
@ -126,76 +141,4 @@ export default class SearchState {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to search and goto a given location
|
||||
* Returns 'false' if search failed
|
||||
*/
|
||||
public async performSearch(): Promise<boolean> {
|
||||
const query = this.searchTerm.data?.trim() ?? ""
|
||||
if (query === "") {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
const geolocationState = this.state.geolocation.geolocationState
|
||||
const bounds = this.state.mapProperties.bounds
|
||||
const bbox = this.state.mapProperties.bounds.data
|
||||
try {
|
||||
this.isSearching.set(true)
|
||||
geolocationState?.allowMoving.setData(true)
|
||||
geolocationState?.requestMoment.setData(undefined) // If the GPS is still searching for a fix, we say that we don't want tozoom to it anymore
|
||||
let poi: SearchResult
|
||||
if(this.suggestions.data){
|
||||
poi = this.suggestions.data[0]
|
||||
}else{
|
||||
const results = GeocodingUtils.mergeSimilarResults([].concat(...await Promise.all(this.locationSearchers.map(ls => ls.search(query, { bbox: bounds.data })))))
|
||||
poi = results[0]
|
||||
}
|
||||
|
||||
if (poi.category === "theme") {
|
||||
const theme = <MinimalLayoutInformation>poi.payload
|
||||
const url = MoreScreen.createUrlFor(theme)
|
||||
window.location = <any>url
|
||||
return true
|
||||
}
|
||||
if (poi.category === "filter") {
|
||||
await this.apply(poi.payload)
|
||||
return true
|
||||
}
|
||||
if (poi.boundingbox) {
|
||||
const [lat0, lat1, lon0, lon1] = poi.boundingbox
|
||||
// Will trigger a 'fly to'
|
||||
bounds.set(
|
||||
new BBox([
|
||||
[lon0, lat0],
|
||||
[lon1, lat1]
|
||||
]).pad(0.01)
|
||||
)
|
||||
} else if (poi.lon && poi.lat) {
|
||||
this.state.mapProperties.flyTo(poi.lon, poi.lat, GeocodingUtils.categoryToZoomLevel[poi.category] ?? 16)
|
||||
}
|
||||
const perLayer = this.state.perLayer
|
||||
if (perLayer !== undefined) {
|
||||
const id = poi.osm_type + "/" + poi.osm_id
|
||||
const layers = Array.from(perLayer?.values() ?? [])
|
||||
for (const layer of layers) {
|
||||
const found = layer.features.data.find((f) => f.properties.id === id)
|
||||
if (found === undefined) {
|
||||
continue
|
||||
}
|
||||
this.state.selectedElement?.setData(found)
|
||||
}
|
||||
}
|
||||
return true
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.feedback.set(Translations.t.general.search.error)
|
||||
return false
|
||||
} finally {
|
||||
this.isSearching.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue