From 5af2eeb7999c8f93054a694f7c50bda45d5b90b4 Mon Sep 17 00:00:00 2001 From: Dennis Sen Date: Mon, 26 Feb 2024 10:14:08 +0100 Subject: [PATCH] reorder contents, remove gfi (not needed) --- packages/clients/textLocator/TODO.md | 28 +++ packages/clients/textLocator/heap/map.js | 144 +++++++++++ .../textLocator/heap/requestHandling.js | 233 ++++++++++++++++++ packages/clients/textLocator/package.json | 1 - .../clients/textLocator/src/addPlugins.ts | 52 ++-- .../components/GeometrySearch.vue | 17 ++ .../GeometrySearch/components/index.ts | 1 + .../src/plugins/GeometrySearch/index.ts | 18 ++ .../src/plugins/GeometrySearch/language.ts | 32 +++ .../src/plugins/GeometrySearch/store/index.ts | 33 +++ .../src/plugins/GeometrySearch/types.ts | 3 + .../textLocator/src/search/byGeometry.ts | 1 - packages/clients/textLocator/src/types.ts | 1 - .../utils/coastalGazetteer/geometrySearch.ts | 3 + .../utils/coastalGazetteer/makeRequestUrl.ts | 47 ++++ .../coastalGazetteer/toponymSearch.ts} | 35 +-- .../src/utils/literatureByToponym.ts | 0 17 files changed, 590 insertions(+), 59 deletions(-) create mode 100644 packages/clients/textLocator/TODO.md create mode 100644 packages/clients/textLocator/heap/map.js create mode 100644 packages/clients/textLocator/heap/requestHandling.js create mode 100644 packages/clients/textLocator/src/plugins/GeometrySearch/components/GeometrySearch.vue create mode 100644 packages/clients/textLocator/src/plugins/GeometrySearch/components/index.ts create mode 100644 packages/clients/textLocator/src/plugins/GeometrySearch/index.ts create mode 100644 packages/clients/textLocator/src/plugins/GeometrySearch/language.ts create mode 100644 packages/clients/textLocator/src/plugins/GeometrySearch/store/index.ts create mode 100644 packages/clients/textLocator/src/plugins/GeometrySearch/types.ts delete mode 100644 packages/clients/textLocator/src/search/byGeometry.ts delete mode 100644 packages/clients/textLocator/src/types.ts create mode 100644 packages/clients/textLocator/src/utils/coastalGazetteer/geometrySearch.ts create mode 100644 packages/clients/textLocator/src/utils/coastalGazetteer/makeRequestUrl.ts rename packages/clients/textLocator/src/{search/coastalGazetteer.ts => utils/coastalGazetteer/toponymSearch.ts} (90%) create mode 100644 packages/clients/textLocator/src/utils/literatureByToponym.ts diff --git a/packages/clients/textLocator/TODO.md b/packages/clients/textLocator/TODO.md new file mode 100644 index 000000000..b91ff8f9e --- /dev/null +++ b/packages/clients/textLocator/TODO.md @@ -0,0 +1,28 @@ +# TODO + +## MUSS + +* Liste ermittelter Orte +* Liste ermittelter Texte mit Ortsbezug +* Ortsbezüge eines Textes graphisch darstellen +* Suchgeometrien: Rechtecke, Punkte +* Suchgeometrien: Ortspolygone (Adresssuche?) +* Kontextsuche: Filtern ermittelter Texte (???) +* Relevanzbewertung von Ortsbezügen (UI nur Count?) + +## SOLL + +* Häufigkeit von Funden +* Dataport-Geo-Design +* Ortsbezüge von Texten als Ortsgebirge (?) +* Zugriff auf weitere Geodaten (???) +* Schreibweisen/Sprachen von Orten berücksichtigen +* Historische Ortspolygone beachten +* Suchgeometrien: Kreise, Vielecke +* Ortssuche + +## KANN + +* 3D-Darstellung Ortsgebirge +* Höhenlinien Ortsgebirge +* Farbliche Markieren Ortsgebirge diff --git a/packages/clients/textLocator/heap/map.js b/packages/clients/textLocator/heap/map.js new file mode 100644 index 000000000..458d6c59d --- /dev/null +++ b/packages/clients/textLocator/heap/map.js @@ -0,0 +1,144 @@ +import { Feature, Map, View } from 'ol' +import TileLayer from 'ol/layer/Tile' +import TileWMS from 'ol/source/TileWMS.js' +import OSM, { ATTRIBUTION } from 'ol/source/OSM' +import { getTransform } from 'ol/proj.js' +import WKT from 'ol/format/WKT.js' +import Polygon from 'ol/geom/Polygon.js' +import VectorSource from 'ol/source/Vector' +import VectorLayer from 'ol/layer/Vector' +import Overlay from 'ol/Overlay.js' +import { Fill, Stroke, Style } from 'ol/style.js' +import { Control, defaults as defaultControls } from 'ol/control.js' + +const WKTfmt = new WKT() + +const resultFeatureStyle = [ + new Style({ + stroke: new Stroke({ + color: 'blue', + width: 3, + }), + fill: new Fill({ + color: 'rgba(0, 0, 255, 0.1)', + }), + }), +] +const resultVectorLayer = new VectorLayer({ + source: new VectorSource({ + features: resultFeatures, + style: resultFeatureStyle, + }), +}) +// used to display the colored Geometries when clicking a location +const geometriesVectorLayer = new VectorLayer({ + source: new VectorSource({ + features: [], + style: resultFeatureStyle, + }), +}) +geometriesVectorLayer.setOpacity(0.7) + +// for coloring all geometries to all locations found in a title +const titleGeometriesVectorLayer = new VectorLayer({ + source: new VectorSource({ + features: [], + style: resultFeatureStyle, + }), +}) +titleGeometriesVectorLayer.setOpacity(0.8) + +const popupOverlay = new Overlay({ + element: popupContainer, + autoPan: { + animation: { + duration: 250, + }, + }, +}) + +popupCloser.onclick = function () { + popupOverlay.setPosition(undefined) + popupCloser.blur() + return false + +class ResetLocationGeometries extends Control { + geometriesVectorLayer.getSource().clear() + +class ResetTextGeometries extends Control { + titleGeometriesVectorLayer.getSource().clear() + + overlays: [popupOverlay] + + showPopup(text, coordinate) { + popupOverlay.setPosition(coordinate) + this.popupContent.innerHTML = text + + displayUserSelectedPoly(geometry) { + const displayPoly = geometry.clone() + displayPoly.applyTransform(getTransform('EPSG:4326', 'EPSG:3857')) + this.resultVectorLayer.getSource().getFeatures()[0].setGeometry(displayPoly) + + removeUserSelectedPoly() { + this.resultVectorLayer + .getSource() + .getFeatures()[0] + .setGeometry(new Polygon([])) + + displayGeometriesOnMap(geometriesList) { + this.resultVectorLayer.getSource().clear() + this.resultVectorLayer.setOpacity(0.45) + geometriesList.forEach((wktGeom) => { + const feature = WKTfmt.readFeature(wktGeom, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857', + }) + this.resultVectorLayer.getSource().addFeature(feature) + + colorGeometries(locationName, nameGeometryDict) { + this.geometriesVectorLayer.getSource().clear() + const geometriesList = nameGeometryDict[locationName] + if (geometriesList && Array.isArray(geometriesList)) { + let geometryCounter = 0 + geometriesList.forEach((wktGeom) => { + const style = new Style({ + fill: new Fill({ + color: colorPalette[geometryCounter], + }), + }) + const feature = WKTfmt.readFeature(wktGeom, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857', + }) + feature.setStyle(style) + this.geometriesVectorLayer.getSource().addFeature(feature) + geometryCounter++ + }) + } else { + console.log('No Geometries for this location') + + colorGeometriesMultipleLocations( + article_title, + titleLocationFreqDict, + nameGeometryDict + ) { + this.titleGeometriesVectorLayer.getSource().clear() + const locationsFreqs = titleLocationFreqDict[article_title] + for (const locationName in locationsFreqs) { + const geometriesList = nameGeometryDict[locationName] + if (geometriesList && Array.isArray(geometriesList)) { + geometriesList.forEach((wktGeom) => { + const style = new Style({ + fill: new Fill({ + color: heatColorPalette[locationsFreqs[locationName]], + }), + }) + const feature = WKTfmt.readFeature(wktGeom, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857', + }) + feature.setStyle(style) + this.titleGeometriesVectorLayer.getSource().addFeature(feature) + }) + } else { + console.log('No Geometries for this location') diff --git a/packages/clients/textLocator/heap/requestHandling.js b/packages/clients/textLocator/heap/requestHandling.js new file mode 100644 index 000000000..8d0c4b373 --- /dev/null +++ b/packages/clients/textLocator/heap/requestHandling.js @@ -0,0 +1,233 @@ +import WKT from 'ol/format/WKT.js' + +const ignoreIdsName = ['EuroNat-33'] +const ignoreIdsGeometries = [ + 'EuroNat-33', + 'SH-WATTENMEER-DM-1', + 'SH-WATTENMEER-1', + 'Ak2006-51529', + 'Landsg-2016-110', +] +const nameGeometryDict = {} +let titleLocationFreqDict = {} + +const WKTfmt = new WKT() + +// endpoint configuration - load endpoints from a json file and fall back to default config if that json is not available +const defaultConfig = { + backend_endpoint: 'http://localhost:8000', +} + +let config_data = null +async function loadConfig() { + if (config_data != null) { + return config_data + } + try { + const response = await fetch('./textloc-config.json') + if (response.ok) { + const config = await response.json() + config_data = config + return config + } + console.error( + 'config request failed:', + response.status, + response.statusText + ) + return defaultConfig + } catch (error) { + console.error('config request error:', error) + return defaultConfig + } +} + +class requestHandler { + constructor() {} +} + +/** + * Extract all location names and geometries from the gazetteer response json while skipping names that have an ignoreId + * example for data value: [{"id":"shnwattdt075","names":[{"ObjectID":"shnwatthist1912003","GeomID":"shnwattdt075","Start":"1868-01-01","Ende":"1875-12-31","Name":"Bielshöven","Rezent":false,"Sprache":"Hochdeutsch","Typ":"früherer Name, hist. Name","Quellen":[{"Autor":"F.A. Meyer","Datum":"1875","Titel":"Elbemündung ( Katalognummer 81)","Media":"A.W. Lang, Historisches Seekartenwerk der Deutschen Bucht ( Neumünster 1969-1981)","Ort":"Hamburg"},{"Autor":"F.A. Meyer","Datum":"1868","Titel":"Einsegelung in die Elbe ( Katalognummer 75)","Media":"A.W. Lang, Historisches Seekartenwerk der Deutschen Bucht ( Neumünster 1969-1981)","Ort":"Hamburg"}]}, + * @param {dictionary} data + * @param {list of string} ignoreIds + * @returns + */ +function extractNamesAndGeometries(data) { + const namesList = [] + const allGeometriesList = [] + data.forEach((entry) => { + if (!ignoreIdsName.includes(entry.id)) { + if (entry.names && Array.isArray(entry.names)) { + entry.names.forEach((nameObj) => { + if (nameObj.Name) { + namesList.push(nameObj.Name) + } + if (!ignoreIdsGeometries.includes(entry.id)) { + const geometriesList = [] + if (entry.geoms && Array.isArray(entry.geoms)) { + entry.geoms.forEach((geomObj) => { + if (geomObj.WKT) { + allGeometriesList.push(geomObj.WKT) + geometriesList.push(geomObj.WKT) + } + }) + } + nameGeometryDict[nameObj.Name] = geometriesList + } + }) + } + } + }) + return [namesList, allGeometriesList, nameGeometryDict] +} + +/** + * send a request to our own backend to search for articles that contain the location names returned by the kuesten gazetteer. + * Each location has their own BM25 search, such that the frequencies can be determined for each location + * @param {*} locationNamesArray + * @returns {dictionary} - dictionary that contains the found article titles as keys and another dict with location : frequency pairs + * as values + */ +async function locationLookupIndividually(locationNamesArray) { + const config = await loadConfig() + const url = config.backendEndpoint + 'lookup/locations_individually' + + const locationsArray = { location_names: locationNamesArray } + const requestData = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(locationsArray), + } + + try { + const response = await fetch(url, requestData) + if (response.ok) { + const data = await response.json() + return data + } + console.error('Request failed:', response.status, response.statusText) + return null + } catch (error) { + console.error('Request error:', error) + return null + } +} + +/** + * send a request to our own backend to search for articles that contain the location names returned by the kuesten gazetteer. + * All locations are run through the BM25 algorithm bundled together + * @param {string} name - comma seperated location names + * @returns + */ +async function locationLookup(name) { + const config = await loadConfig() + const url = config.backendEndpoint + 'lookup/location_name' + + const requestData = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ name }), + } + + try { + const response = await fetch(url, requestData) + if (response.ok) { + const data = await response.json() + return data + } + console.error('Request failed:', response.status, response.statusText) + return null + } catch (error) { + console.error('Request error:', error) + return null + } +} + +/** + * Main function to run when user clicks on the map. Requests locations from the Gazetteer and searches locations in articles via + * our backend + * @param {*} geometry + * @param {*} popup_position + * @param {*} mapClass + * @param {*} htmlCreator + * @returns + */ +export const searchAction = async ( + geometry, + popup_position, + mapClass, + htmlCreator +) => { + mapClass.showPopup('Suche nach Ortsnamen...', popup_position) + mapClass.displayUserSelectedPoly(geometry) + let resultJSON = await requestResultJSON(geometry, 1) + console.log('fetched result') + console.log(resultJSON) + if (resultJSON == undefined) { + mapClass.showPopup( + 'Die Suche nach Ortsnamen ist fehlgeschlagen.', + popup_position + ) + return + } + var [locationNamesArray, geometriesList, nameGeometryDict] = + extractNamesAndGeometries(resultJSON.results) + console.log(locationNamesArray) + console.log(geometriesList) + console.log(nameGeometryDict) + if (resultJSON.pages > 1) { + for (let page = 2; page <= resultJSON.pages; page++) { + mapClass.showPopup( + 'Suche nach Ortsnamen... ' + page + ' / ' + resultJSON.pages, + popup_position + ) + resultJSON = await requestResultJSON(geometry, page) + var [locationNamesPages, geometriesPages, nameGeometryDict] = + extractNamesAndGeometries(resultJSON.results) + locationNamesArray = locationNamesArray.concat(locationNamesPages) + geometriesList = geometriesList.concat(geometriesPages) + } + } + + htmlCreator.populateSidebarLocations( + locationNamesArray, + mapClass, + nameGeometryDict + ) + mapClass.popupCloser.onclick() + const locationNames = locationNamesArray.join(',') + console.log('location names: ' + locationNames) + mapClass.displayGeometriesOnMap(geometriesList) + + mapClass.showPopup('Suche nach passenden Texten...', popup_position) + const lookupResults = await locationLookupIndividually(locationNamesArray) + if (lookupResults == null) { + mapClass.showPopup( + 'Die Suche nach passenden Texten ist fehlgeschlagen.', + popup_position + ) + return + } + + titleLocationFreqDict = lookupResults.title_location_freq + + let result_text = + "
Suchergebnisse
Suchbegriffe: " + + locationNames + + '
' + for (const article_title in titleLocationFreqDict) { + console.log(article_title) + result_text += '
' + article_title + '
\n' + } + mapClass.popupCloser.onclick() + htmlCreator.populateSidebarTexts( + titleLocationFreqDict, + nameGeometryDict, + mapClass + ) +} diff --git a/packages/clients/textLocator/package.json b/packages/clients/textLocator/package.json index ffb44a19d..690c32b35 100644 --- a/packages/clients/textLocator/package.json +++ b/packages/clients/textLocator/package.json @@ -24,7 +24,6 @@ "@polar/plugin-address-search": "^1.2.1", "@polar/plugin-attributions": "^1.2.1", "@polar/plugin-draw": "1.1.0", - "@polar/plugin-gfi": "^1.2.2", "@polar/plugin-icon-menu": "^1.2.0", "@polar/plugin-layer-chooser": "^1.2.0", "@polar/plugin-legend": "^1.1.0", diff --git a/packages/clients/textLocator/src/addPlugins.ts b/packages/clients/textLocator/src/addPlugins.ts index 6d385e4c4..16d7ab946 100644 --- a/packages/clients/textLocator/src/addPlugins.ts +++ b/packages/clients/textLocator/src/addPlugins.ts @@ -2,7 +2,6 @@ import { setLayout, NineLayout, NineLayoutTag } from '@polar/core' import AddressSearch from '@polar/plugin-address-search' import Attributions from '@polar/plugin-attributions' import Draw from '@polar/plugin-draw' -import Gfi from '@polar/plugin-gfi' import IconMenu from '@polar/plugin-icon-menu' import LayerChooser from '@polar/plugin-layer-chooser' import Legend from '@polar/plugin-legend' @@ -12,7 +11,8 @@ import Toast from '@polar/plugin-toast' import Zoom from '@polar/plugin-zoom' import Header from './plugins/Header' -import { searchCoastalGazetteer } from './search/coastalGazetteer' +import GeometrySearch from './plugins/GeometrySearch' +import { searchCoastalGazetteer } from './utils/coastalGazetteer/toponymSearch' import { idRegister } from './services' // this is fine for list-like setup functions @@ -28,43 +28,22 @@ export const addPlugins = (core) => { IconMenu({ displayComponent: true, // TODO fix, it's broken ... - initiallyOpen: 'attributions', + initiallyOpen: 'geometrySearch', menus: [ + { + plugin: GeometrySearch({}), + icon: 'fa-solid fa-magnifying-glass-location', + id: 'geometrySearch', + }, { plugin: LayerChooser({}), icon: 'fa-layer-group', id: 'layerChooser', }, - { - plugin: Gfi({ - renderType: 'iconMenu', - coordinateSources: [], - layers: {}, - }), - icon: 'fa-location-pin', - id: 'gfi', - }, { plugin: Zoom({ renderType: 'iconMenu' }), id: 'zoom', }, - { - plugin: Attributions({ - renderType: 'iconMenu', - listenToChanges: [ - 'plugin/zoom/zoomLevel', - 'plugin/layerChooser/activeBackgroundId', - 'plugin/layerChooser/activeMaskIds', - ], - layerAttributions: idRegister.map((id) => ({ - id, - title: `textLocator.attributions.${id}`, - })), - staticAttributions: ['textLocator.attributions.static'], - }), - icon: 'fa-regular fa-copyright', - id: 'attributions', - }, ], layoutTag: NineLayoutTag.TOP_RIGHT, }), @@ -99,6 +78,21 @@ export const addPlugins = (core) => { }, }, }), + Attributions({ + displayComponent: true, + layoutTag: NineLayoutTag.BOTTOM_RIGHT, + initiallyOpen: true, + listenToChanges: [ + 'plugin/zoom/zoomLevel', + 'plugin/layerChooser/activeBackgroundId', + 'plugin/layerChooser/activeMaskIds', + ], + layerAttributions: idRegister.map((id) => ({ + id, + title: `textLocator.attributions.${id}`, + })), + staticAttributions: ['textLocator.attributions.static'], + }), Legend({ displayComponent: true, layoutTag: NineLayoutTag.BOTTOM_RIGHT, diff --git a/packages/clients/textLocator/src/plugins/GeometrySearch/components/GeometrySearch.vue b/packages/clients/textLocator/src/plugins/GeometrySearch/components/GeometrySearch.vue new file mode 100644 index 000000000..be8a89e6b --- /dev/null +++ b/packages/clients/textLocator/src/plugins/GeometrySearch/components/GeometrySearch.vue @@ -0,0 +1,17 @@ + + + + + diff --git a/packages/clients/textLocator/src/plugins/GeometrySearch/components/index.ts b/packages/clients/textLocator/src/plugins/GeometrySearch/components/index.ts new file mode 100644 index 000000000..bf61d94d6 --- /dev/null +++ b/packages/clients/textLocator/src/plugins/GeometrySearch/components/index.ts @@ -0,0 +1 @@ +export { default as GeometrySearch } from './GeometrySearch.vue' diff --git a/packages/clients/textLocator/src/plugins/GeometrySearch/index.ts b/packages/clients/textLocator/src/plugins/GeometrySearch/index.ts new file mode 100644 index 000000000..8e7b6a305 --- /dev/null +++ b/packages/clients/textLocator/src/plugins/GeometrySearch/index.ts @@ -0,0 +1,18 @@ +import Vue from 'vue' +import { PluginOptions } from '@polar/lib-custom-types' +import { GeometrySearch } from './components' +import language from './language' +import { makeStoreModule } from './store' + +interface GeometrySearchConfiguration extends PluginOptions { + thing: string +} + +export default (options: GeometrySearchConfiguration) => (instance: Vue) => + instance.$store.dispatch('addComponent', { + name: 'geometrySearch', + plugin: GeometrySearch, + language, + storeModule: makeStoreModule(), + options, + }) diff --git a/packages/clients/textLocator/src/plugins/GeometrySearch/language.ts b/packages/clients/textLocator/src/plugins/GeometrySearch/language.ts new file mode 100644 index 000000000..7481ff33a --- /dev/null +++ b/packages/clients/textLocator/src/plugins/GeometrySearch/language.ts @@ -0,0 +1,32 @@ +import { LanguageOption } from '@polar/lib-custom-types' + +const language: LanguageOption[] = [ + { + type: 'de', + resources: { + plugins: { + iconMenu: { + hints: { + geometrySearch: 'Geometriesuche', + }, + }, + geometrySearch: {}, + }, + }, + }, + { + type: 'en', + resources: { + plugins: { + iconMenu: { + hints: { + geometrySearch: 'Geometry search', + }, + }, + geometrySearch: {}, + }, + }, + }, +] + +export default language diff --git a/packages/clients/textLocator/src/plugins/GeometrySearch/store/index.ts b/packages/clients/textLocator/src/plugins/GeometrySearch/store/index.ts new file mode 100644 index 000000000..5971690c9 --- /dev/null +++ b/packages/clients/textLocator/src/plugins/GeometrySearch/store/index.ts @@ -0,0 +1,33 @@ +import { + generateSimpleGetters, + generateSimpleMutations, +} from '@repositoryname/vuex-generators' +import { PolarModule } from '@polar/lib-custom-types' +import { GeometrySearchState, GeometrySearchGetters } from '../types' + +const getInitialState = (): GeometrySearchState => ({}) + +// OK for module creation +// eslint-disable-next-line max-lines-per-function +export const makeStoreModule = () => { + const storeModule: PolarModule = { + namespaced: true, + state: getInitialState(), + actions: { + setupModule({ + commit, + dispatch, + getters: { listenToChanges, renderType }, + rootGetters, + }): void {}, + }, + mutations: { + ...generateSimpleMutations(getInitialState()), + }, + getters: { + ...generateSimpleGetters(getInitialState()), + }, + } + + return storeModule +} diff --git a/packages/clients/textLocator/src/plugins/GeometrySearch/types.ts b/packages/clients/textLocator/src/plugins/GeometrySearch/types.ts new file mode 100644 index 000000000..3a13108d5 --- /dev/null +++ b/packages/clients/textLocator/src/plugins/GeometrySearch/types.ts @@ -0,0 +1,3 @@ +export interface GeometrySearchState {} + +export type GeometrySearchGetters = GeometrySearchState diff --git a/packages/clients/textLocator/src/search/byGeometry.ts b/packages/clients/textLocator/src/search/byGeometry.ts deleted file mode 100644 index a75baaf24..000000000 --- a/packages/clients/textLocator/src/search/byGeometry.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO write search by geometry diff --git a/packages/clients/textLocator/src/types.ts b/packages/clients/textLocator/src/types.ts deleted file mode 100644 index 429751982..000000000 --- a/packages/clients/textLocator/src/types.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO if this file stays empty, delete it diff --git a/packages/clients/textLocator/src/utils/coastalGazetteer/geometrySearch.ts b/packages/clients/textLocator/src/utils/coastalGazetteer/geometrySearch.ts new file mode 100644 index 000000000..dbc2454ce --- /dev/null +++ b/packages/clients/textLocator/src/utils/coastalGazetteer/geometrySearch.ts @@ -0,0 +1,3 @@ +// https://mdi-de-dienste.org/GazetteerClient/search?geom=POINT(8.511120645998783 54.10846498210793)&keyword=&searchType=like&lang=-&sdate=0001-01-01&edate=2030-08-16&type=-&page=1 + +// https://mdi-de-dienste.org/GazetteerClient/search?geom=POLYGON%28%288.621975250313447+54.161985993052355%2C8.621975250313447+54.14485476891343%2C8.65430784323856+54.14485476891343%2C8.65430784323856+54.161985993052355%2C8.621975250313447+54.161985993052355%29%29&keyword=&searchType=like&lang=-&sdate=0001-01-01&edate=2030-08-16&type=-&page=1 diff --git a/packages/clients/textLocator/src/utils/coastalGazetteer/makeRequestUrl.ts b/packages/clients/textLocator/src/utils/coastalGazetteer/makeRequestUrl.ts new file mode 100644 index 000000000..153ecbd38 --- /dev/null +++ b/packages/clients/textLocator/src/utils/coastalGazetteer/makeRequestUrl.ts @@ -0,0 +1,47 @@ +import { Geometry } from 'geojson' +import { GeoJSON, WKT } from 'ol/format' + +const geoJson = new GeoJSON() +const wellKnownText = new WKT() + +interface RequestPayload { + keyword: string + searchType: 'like' | 'exact' | 'id' + lang: '-' | string + sdate: string + edate: string + type: '-' | string + page?: string // numerical + geom?: string +} + +const searchRequestDefaultPayload: Partial = { + searchType: 'like', + lang: '-', + sdate: '0001-01-01', + edate: new Date().toJSON().slice(0, 10), + type: '-', +} + +export const makeRequestUrl = ( + url: string, + inputValue: string, + page: string | undefined, + geometry: Geometry | undefined, + epsg: string +): string => + `${url}?${new URLSearchParams({ + ...searchRequestDefaultPayload, + keyword: inputValue ? `*${inputValue}*` : '', + ...(typeof page !== 'undefined' ? { page } : {}), + ...(typeof geometry !== 'undefined' + ? { + geom: wellKnownText.writeGeometry( + geoJson.readGeometry(geometry, { + dataProjection: epsg, + featureProjection: 'EPSG:4326', + }) + ), + } + : {}), + }).toString()}` diff --git a/packages/clients/textLocator/src/search/coastalGazetteer.ts b/packages/clients/textLocator/src/utils/coastalGazetteer/toponymSearch.ts similarity index 90% rename from packages/clients/textLocator/src/search/coastalGazetteer.ts rename to packages/clients/textLocator/src/utils/coastalGazetteer/toponymSearch.ts index 6f7f89de6..65c3d0bd4 100644 --- a/packages/clients/textLocator/src/search/coastalGazetteer.ts +++ b/packages/clients/textLocator/src/utils/coastalGazetteer/toponymSearch.ts @@ -6,6 +6,7 @@ import { Map } from 'ol' import { GeoJSON, WKT } from 'ol/format' import { Store } from 'vuex' import levenshtein from 'js-levenshtein' +import { makeRequestUrl } from './makeRequestUrl' const ignoreIds = { global: ['EuroNat-33'], @@ -20,17 +21,6 @@ const ignoreIds = { const wellKnownText = new WKT() const geoJson = new GeoJSON() -interface RequestPayload { - keyword: string - searchType: 'like' | 'exact' | 'id' - lang: '-' | string - sdate: string - edate: string - type: '-' | string - page?: string // numerical - geom?: string -} - interface ResponseName { Start: string // YYYY-MM-DD Ende: string // YYYY-MM-DD @@ -90,14 +80,6 @@ const sorter = levenshtein(b[sortKey], searchPhrase) } -const searchRequestDefaultPayload: Partial = { - searchType: 'like', - lang: '-', - sdate: '0001-01-01', - edate: new Date().toJSON().slice(0, 10), - type: '-', -} - const getEmptyFeatureCollection = (): FeatureCollection => ({ type: 'FeatureCollection', features: [], @@ -131,14 +113,11 @@ async function getAllPages( signal: AbortSignal, url: string, inputValue: string, - page: string | undefined + page: string | undefined, + epsg: `EPSG:${string}` ): Promise { const response = await fetch( - `${url}?${new URLSearchParams({ - ...searchRequestDefaultPayload, - keyword: `*${inputValue}*`, - ...(typeof page !== 'undefined' ? { page } : {}), - }).toString()}`, + makeRequestUrl(url, inputValue, page, undefined, epsg), { method: 'GET', signal, @@ -164,7 +143,7 @@ async function getAllPages( responsePayload, await Promise.all( Array.from(Array(pages - 1)).map((_, index) => - getAllPages.call(this, signal, url, inputValue, `${index + 2}`) + getAllPages.call(this, signal, url, inputValue, `${index + 2}`, epsg) ) ) ) @@ -175,6 +154,7 @@ const featurify = (feature: ResponseResult): Feature => ({ type: 'Feature', geometry: geoJson.writeGeometryObject( + // NOTE currently only the first geometry is used wellKnownText.readGeometry( feature.geoms.find( (geom) => !ignoreIds.geometries.includes(geom.GeomID) @@ -238,7 +218,8 @@ export async function searchCoastalGazetteer( signal, url, inputValue, - undefined + undefined, + queryParameters.epsg ) } catch (e) { console.error(e) diff --git a/packages/clients/textLocator/src/utils/literatureByToponym.ts b/packages/clients/textLocator/src/utils/literatureByToponym.ts new file mode 100644 index 000000000..e69de29bb