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 @@
+
+ test
+
+
+
+
+
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