Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9757271
feat: add nominatim support to addressSearch and reverseGeocoder
oeninghe-dataport Mar 23, 2026
41544aa
Merge remote-tracking branch 'origin/next' into vue3/add-nominatim-su…
oeninghe-dataport May 20, 2026
b944581
docs: use non-question sentences for attribute typedoc
oeninghe-dataport May 26, 2026
3221cd5
refactor(reverseGeocoder): simplify return object for nominatim
oeninghe-dataport May 26, 2026
6a58368
chore: remove TODO and model idea as feature request
oeninghe-dataport Jun 1, 2026
9cae6a1
refactor: replace functional implementation with simpler procedural
oeninghe-dataport Jun 1, 2026
169d26e
refactor: introduce generic transform util for geometries
oeninghe-dataport Jun 1, 2026
d19d95a
refactor: type response of nominatim feature search
oeninghe-dataport Jun 1, 2026
687b39c
test(snowbox): remove optional queryParameters for nominatim
oeninghe-dataport Jun 1, 2026
4aed172
Merge branch 'next' into vue3/add-nominatim-support
oeninghe-dataport Jun 2, 2026
5c87397
docs: fix typo for transformGeometry
oeninghe-dataport Jun 17, 2026
e3e9c13
Merge branch 'next' into vue3/add-nominatim-support
oeninghe-dataport Jul 10, 2026
b902a4f
fix(reverseGeocoder): allow reverse-geocoding WPS with differing EPSG
oeninghe-dataport Jul 10, 2026
ad67371
refactor(reverseGeocoder): type nominatim response
oeninghe-dataport Jul 13, 2026
20d34b6
refactor(reverseGeocoder): more intuitive syntax for typing fetch res…
oeninghe-dataport Jul 14, 2026
d312765
Merge branch 'next' into vue3/add-nominatim-support
oeninghe-dataport Jul 29, 2026
ca44a6b
refactor(reverseGeocoder): more verbosity about serviceEpsg
oeninghe-dataport Jul 30, 2026
f250d7f
refactor(reverseGeocoder): simplify local function signature
oeninghe-dataport Jul 30, 2026
de4b588
feat(reverseGeocoder): use geojson response instead of jsonv2
oeninghe-dataport Jul 31, 2026
f3dc637
docs(reverseGeocoder): use inter-linking for plugin options
oeninghe-dataport Jul 31, 2026
a5606c2
refactor(reverseGeocoder): be even more explicit on serviceEpsg
oeninghe-dataport Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/iceberg/components/IcebergMap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ watch(map, (map) => {
toZoomLevel: 7,
}),
pluginReverseGeocoder({
type: 'wps',
url: 'https://geodienste.hamburg.de/HH_WPS',
coordinateSources: [
{
Expand Down
11 changes: 10 additions & 1 deletion examples/snowbox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
colorScheme,
startCenter: [565874, 5934140],
layers: [
// TODO: Add internalization to snowbox

Check warning on line 95 in examples/snowbox/index.js

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: Add internalization to snowbox'
{
id: basemapId,
visibility: true,
Expand Down Expand Up @@ -359,7 +359,10 @@
addPlugin(
map,
pluginReverseGeocoder({
url: 'https://geodienste.hamburg.de/HH_WPS',
// type: 'wps',
// url: 'https://geodienste.hamburg.de/HH_WPS',
type: 'nominatim',
url: 'https://polar.dataport.de/nominatim/reverse',
coordinateSources: [
{
plugin: 'pins',
Expand Down Expand Up @@ -509,6 +512,7 @@
displayComponent: true,
layoutTag: 'TOP_LEFT',
searchMethods: [
/*
{
queryParameters: {
searchStreets: true,
Expand All @@ -517,6 +521,11 @@
type: 'mpapi',
url: 'https://geodienste.hamburg.de/HH_WFS_GAGES?service=WFS&request=GetFeature&version=2.0.0',
},
*/
{
type: 'nominatim',
url: 'https://polar.dataport.de/nominatim/search',
},
],
minLength: 3,
waitMs: 300,
Expand Down
2 changes: 1 addition & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function addPlugins(map: typeof PolarContainer, enabledPlugins: string[]) {
layoutTag: 'BOTTOM_LEFT',
}),
enabledPlugins.includes('reverseGeocoder') &&
ReverseGeocoder({ url: '' }),
ReverseGeocoder({ url: '', type: 'wps' }),
enabledPlugins.includes('scale') &&
Scale({
displayComponent: true,
Expand Down
17 changes: 6 additions & 11 deletions src/lib/getFeatures/bkg.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { FeatureCollection, Point } from 'geojson'

import { toMerged } from 'es-toolkit'
import { transform as transformCoordinates } from 'ol/proj'

import type { PolarGeoJsonFeature, PolarGeoJsonFeatureCollection } from '@/core'

import type { BKGParameters } from './types'

import { transformGeometry } from '../transformGeometry'
import { errorCheck } from './errorCheck'

function getRequestUrlQuery(
Expand Down Expand Up @@ -58,16 +58,11 @@ export default async function (
features: featureCollection.features.map(
(feature) =>
toMerged(feature, {
geometry: toMerged(feature.geometry, {
coordinates:
queryParameters.epsg === 'EPSG:4326'
? (feature.geometry as Point).coordinates
: transformCoordinates(
(feature.geometry as Point).coordinates,
'EPSG:4326',
queryParameters.epsg
),
}),
geometry: transformGeometry(
feature.geometry,
'EPSG:4326',
queryParameters.epsg
),
Comment thread
dopenguin marked this conversation as resolved.
// @ts-expect-error | It is always defined in this case
title: feature.properties.text,
}) as PolarGeoJsonFeature<Point>
Expand Down
45 changes: 45 additions & 0 deletions src/lib/getFeatures/nominatim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { GeoJSONFeatureCollection } from 'ol/format/GeoJSON'

import type { PolarGeoJsonFeatureCollection } from '@/core'

import type { NominatimParameters } from './types'

import { transformGeometry } from '../transformGeometry'

export default async function (
signal: AbortSignal,
url: string,
inputValue: string,
queryParameters: NominatimParameters
): Promise<PolarGeoJsonFeatureCollection> {
const { epsg, maxFeatures, ...native } = queryParameters

const fetchUrl = new URL(url)
for (const [key, value] of Object.entries(native)) {
fetchUrl.searchParams.set(
key,
Array.isArray(value) ? value.join(',') : String(value)
)
}
if (maxFeatures) {
fetchUrl.searchParams.set('limit', String(maxFeatures))
}
fetchUrl.searchParams.set('q', inputValue)
fetchUrl.searchParams.set('format', 'geojson')
// Setting this to 1 is currently not supported well by addressSearch plugin. The plugin expects a point geometry.
fetchUrl.searchParams.set('polygon_geojson', '0')
fetchUrl.searchParams.set('polygon_threshold', '5')

return await fetch(fetchUrl, {
signal,
})
.then(async (response) => <GeoJSONFeatureCollection>await response.json())
.then((featureCollection) => ({
...featureCollection,
features: featureCollection.features.map((feature) => ({
...feature,
title: feature.properties?.display_name,
geometry: transformGeometry(feature.geometry, 'EPSG:4326', epsg),
})),
}))
}
84 changes: 83 additions & 1 deletion src/lib/getFeatures/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export interface WfsParameters extends QueryParameters {
/** XML feature prefix from xmlns namespace without :; e.g. 'ave'. */
featurePrefix: string

/** Feature type to search for by name (see ?service=wfs&request=DescribeFeatureType). */
/** Feature type to search for by name (see `?service=wfs&request=DescribeFeatureType`). */
typeName: string

/** XML namespace of feature type to use in search. */
Expand Down Expand Up @@ -288,3 +288,85 @@ export interface WfsParameters extends QueryParameters {
*/
useRightHandWildcard?: boolean
}

export interface NominatimParameters extends QueryParameters {
/**
* Whether to include address details in the result.
*/
addressdetails?: boolean

/**
* Name and/or type of POI.
*/
amenity?: string

/**
* Whether to consider the viewbox as exact instead of fuzzy filter.
*/
bounded?: boolean

/**
* City.
*/
city?: string

/**
* Country.
*/
country?: string

/**
* Country code (ISO 3166-1alpha2).
*/
countrycodes?: string[]

/**
* County.
*/
Comment thread
dopenguin marked this conversation as resolved.
county?: string

/**
* Whether to include entrances to buildings in the results.
*/
entrances?: boolean

/**
* Whether to add extra tags to the result.
*/
extratags?: boolean

/**
* Which feature types to include.
*/
featureType?: 'country' | 'state' | 'city' | 'settlement'

/**
* Which type(s) of results to return.
*/
layer?: ('address' | 'poi' | 'railway' | 'natural' | 'manmade')[]

/**
* Whether to include more names in alternate languages.
*/
namedetails?: boolean

/**
* Postal code.
*/
postalcode?: string

/**
* State.
*/
state?: string
Comment thread
dopenguin marked this conversation as resolved.

/**
* House number and street name.
*/
street?: string

/**
* Viewbox to prefer for searching the given term.
*/
viewbox?: [number, number, number, number]
}
16 changes: 7 additions & 9 deletions src/lib/getFeatures/wfs/parse.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { toMerged } from 'es-toolkit'
import { GeoJSON, WFS } from 'ol/format'
import { transform as transformCoordinates } from 'ol/proj'

import type { PolarGeoJsonFeature, PolarGeoJsonFeatureCollection } from '@/core'

import { transformGeometry } from '@/lib/transformGeometry'
Comment thread
dopenguin marked this conversation as resolved.

import { getFeatureTitleFromPattern } from './getFeatureTitleFromPattern'

const parser = new WFS()
Expand Down Expand Up @@ -53,13 +53,11 @@ export async function parseWfsResponse(
}
}
if (epsgCode) {
featureObject.geometry = toMerged(featureObject.geometry, {
coordinates: transformCoordinates(
featureObject.geometry.coordinates,
`EPSG:${epsgCode}`,
epsg
),
})
featureObject.geometry = transformGeometry(
featureObject.geometry,
`EPSG:${epsgCode}`,
epsg
)
}
features.push(featureObject)
})
Expand Down
40 changes: 40 additions & 0 deletions src/lib/transformGeometry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Geometry, Point, Polygon } from 'geojson'

import { transform as transformCoordinates } from 'ol/proj'

/**
* Transforms the coordinates of a GeoJSON geometry from one EPSG projection to another.
*
* @remarks
* Currently supports only Point and Polygon geometries.
Comment thread
oeninghe-dataport marked this conversation as resolved.
Outdated
* For unsupported geometry types, an error is thrown.
*
* @param geometry - The GeoJSON geometry to transform.
* @param sourceEpsg - The EPSG code of the source projection.
* @param targetEpsg - The EPSG code of the target projection.
* @returns The transformed GeoJSON geometry.
*/
export function transformGeometry(
geometry: Geometry,
sourceEpsg: string,
targetEpsg: string
): Point | Polygon {
if (geometry.type === 'Point') {
return {
...geometry,
coordinates: transformCoordinates(
geometry.coordinates,
sourceEpsg,
targetEpsg
),
}
} else if (geometry.type === 'Polygon') {
return {
...geometry,
coordinates: geometry.coordinates.map((ring) =>
ring.map((coord) => transformCoordinates(coord, sourceEpsg, targetEpsg))
),
}
}
throw new Error(`Unsupported geometry type: ${geometry.type}`)
}
4 changes: 2 additions & 2 deletions src/plugins/addressSearch/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export interface AddressSearchPluginOptions extends PluginOptions {

/** Possible search methods by type. */
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
export type SearchType = 'bkg' | 'wfs' | 'mpapi' | string
export type SearchType = 'bkg' | 'wfs' | 'mpapi' | 'nominatim' | string

export type SearchDisplayMode = 'mixed' | 'categorized'

Expand Down Expand Up @@ -177,7 +177,7 @@ export interface SearchMethodConfiguration {

/**
* The object further describes details for the search request.
* Its contents vary by service type, see {@link BKGParameters}, {@link MpapiParameters} or {@link WfsParameters}.
* Its contents vary by service type, see {@link BKGParameters}, {@link MpapiParameters}, {@link WfsParameters} or {@link NominatimParameters}.
*/
queryParameters?: QueryParameters

Expand Down
3 changes: 2 additions & 1 deletion src/plugins/addressSearch/utils/methodContainer.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import bkg from '@/lib/getFeatures/bkg'
import mpapi from '@/lib/getFeatures/mpapi'
import nominatim from '@/lib/getFeatures/nominatim'
import { getWfsFeatures } from '@/lib/getFeatures/wfs'

import type { SearchMethodFunction } from '../types'

export function getMethodContainer() {
const methods = { bkg, mpapi, wfs: getWfsFeatures }
const methods = { bkg, mpapi, nominatim, wfs: getWfsFeatures }

return {
registerSearchMethods: (
Expand Down
Loading
Loading