diff --git a/apps/map-viewer/src/app/app.component.html b/apps/map-viewer/src/app/app.component.html
index e46ab441a3..08409ca81a 100644
--- a/apps/map-viewer/src/app/app.component.html
+++ b/apps/map-viewer/src/app/app.component.html
@@ -4,4 +4,5 @@
class="absolute"
style="top: 20px; left: 20px; bottom: 20px"
>
+
diff --git a/jest.preset.js b/jest.preset.js
index a7b8a3c70c..6fa1ea73a1 100644
--- a/jest.preset.js
+++ b/jest.preset.js
@@ -4,7 +4,9 @@ module.exports = {
...nxPreset,
coverageReporters: ['text'],
setupFiles: ['jest-canvas-mock'],
- transformIgnorePatterns: ['node_modules/(?!(color-*|ol|@mapbox|.*.mjs$))'],
+ transformIgnorePatterns: [
+ 'node_modules/(?!(color-*|ol|@mapbox|@geospatial-sdk|.*.mjs$))',
+ ],
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
diff --git a/libs/feature/map/src/lib/feature-map.module.ts b/libs/feature/map/src/lib/feature-map.module.ts
index c4d5f7c333..aad9e19541 100644
--- a/libs/feature/map/src/lib/feature-map.module.ts
+++ b/libs/feature/map/src/lib/feature-map.module.ts
@@ -23,6 +23,7 @@ import { UiInputsModule } from '@geonetwork-ui/ui/inputs'
import { AddLayerFromWmsComponent } from './add-layer-from-wms/add-layer-from-wms.component'
import { AddLayerFromFileComponent } from './add-layer-from-file/add-layer-from-file.component'
import { AddLayerFromWfsComponent } from './add-layer-from-wfs/add-layer-from-wfs.component'
+import { GeocodingComponent } from './geocoding/geocoding.component'
@NgModule({
declarations: [
@@ -35,6 +36,7 @@ import { AddLayerFromWfsComponent } from './add-layer-from-wfs/add-layer-from-wf
AddLayerFromWmsComponent,
AddLayerFromFileComponent,
AddLayerFromWfsComponent,
+ GeocodingComponent,
],
exports: [
MapContextComponent,
@@ -42,6 +44,7 @@ import { AddLayerFromWfsComponent } from './add-layer-from-wfs/add-layer-from-wf
LayersPanelComponent,
AddLayerFromCatalogComponent,
MapContainerComponent,
+ GeocodingComponent,
],
imports: [
CommonModule,
diff --git a/libs/feature/map/src/lib/geocoding/geocoding.component.css b/libs/feature/map/src/lib/geocoding/geocoding.component.css
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/feature/map/src/lib/geocoding/geocoding.component.html b/libs/feature/map/src/lib/geocoding/geocoding.component.html
new file mode 100644
index 0000000000..4e788277de
--- /dev/null
+++ b/libs/feature/map/src/lib/geocoding/geocoding.component.html
@@ -0,0 +1,39 @@
+
+
+
+ -
+
+ {{ result.label }}
+
+
diff --git a/libs/feature/map/src/lib/geocoding/geocoding.component.spec.ts b/libs/feature/map/src/lib/geocoding/geocoding.component.spec.ts
new file mode 100644
index 0000000000..de77aa93e0
--- /dev/null
+++ b/libs/feature/map/src/lib/geocoding/geocoding.component.spec.ts
@@ -0,0 +1,115 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing'
+import { GeocodingComponent } from './geocoding.component'
+import { MapManagerService } from '../manager/map-manager.service'
+import { NO_ERRORS_SCHEMA } from '@angular/core'
+import Map from 'ol/Map'
+import TileLayer from 'ol/layer/Tile'
+import XYZ from 'ol/source/XYZ'
+import VectorLayer from 'ol/layer/Vector'
+import VectorSource from 'ol/source/Vector'
+import GeoJSON from 'ol/format/GeoJSON'
+import { FEATURE_COLLECTION_POINT_FIXTURE_4326 } from '@geonetwork-ui/common/fixtures'
+import Feature from 'ol/Feature'
+import { Geometry } from 'ol/geom'
+import { TranslateModule } from '@ngx-translate/core'
+
+const vectorLayer = new VectorLayer({
+ source: new VectorSource({
+ features: new GeoJSON().readFeatures(
+ FEATURE_COLLECTION_POINT_FIXTURE_4326,
+ {
+ featureProjection: 'EPSG:3857',
+ dataProjection: 'EPSG:4326',
+ }
+ ),
+ }) as VectorSource>,
+})
+
+const mapMock = new Map({
+ layers: [
+ new TileLayer({
+ source: new XYZ({
+ url: 'http://test',
+ }),
+ }),
+ vectorLayer,
+ ],
+})
+
+const mapManagerMock = {
+ map: mapMock,
+}
+
+describe('GeocodingComponent', () => {
+ let component: GeocodingComponent
+ let fixture: ComponentFixture
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot()],
+ declarations: [GeocodingComponent],
+ providers: [{ provide: MapManagerService, useValue: mapManagerMock }],
+ schemas: [NO_ERRORS_SCHEMA],
+ }).compileComponents()
+
+ fixture = TestBed.createComponent(GeocodingComponent)
+ component = fixture.componentInstance
+ fixture.detectChanges()
+ })
+
+ it('should create', () => {
+ expect(component).toBeTruthy()
+ expect(component.searchText).toBe('')
+ expect(component.results).toEqual([])
+ })
+
+ describe('On Search Change', () => {
+ describe('when search text is empty', () => {
+ beforeEach(() => {
+ component.onSearchChange('')
+ })
+ it('should not show any results', () => {
+ expect(component.searchText).toEqual('')
+ expect(component.results).toEqual([])
+ })
+ })
+ describe('when search text is not empty', () => {
+ beforeEach(() => {
+ component.searchText = 'test'
+ })
+ it('should show results', () => {
+ expect(component.searchText).toEqual('test')
+ expect(component.results).toEqual([])
+ })
+ })
+ })
+
+ describe('zoomToLocation', () => {
+ it('should zoom to the location of the result', () => {
+ const result = {
+ geom: {
+ coordinates: [[0, 0]],
+ },
+ }
+ const viewMock = {
+ fit: jest.fn(),
+ }
+ mapMock.getView = jest.fn().mockReturnValue(viewMock)
+ component.zoomToLocation(result)
+ expect(viewMock.fit).toHaveBeenCalled()
+ })
+ })
+ describe('onEnterPress', () => {
+ it('should zoom to the location of the first result', () => {
+ const result = {
+ geom: {
+ coordinates: [[0, 0]],
+ },
+ }
+ component.results = [result]
+ const zoomToLocationSpy = jest.spyOn(component, 'zoomToLocation')
+ component.onEnterPress()
+ expect(zoomToLocationSpy).toHaveBeenCalledWith(result)
+ })
+ })
+})
diff --git a/libs/feature/map/src/lib/geocoding/geocoding.component.ts b/libs/feature/map/src/lib/geocoding/geocoding.component.ts
new file mode 100644
index 0000000000..ec39013453
--- /dev/null
+++ b/libs/feature/map/src/lib/geocoding/geocoding.component.ts
@@ -0,0 +1,83 @@
+import { Component, OnDestroy } from '@angular/core'
+import { queryGeoadmin, GeoadminOptions } from '@geospatial-sdk/geocoding'
+import { catchError, from, Subject, takeUntil } from 'rxjs'
+import { debounceTime, switchMap } from 'rxjs/operators'
+import { MapManagerService } from '../manager/map-manager.service'
+import { fromLonLat } from 'ol/proj'
+import { Polygon } from 'ol/geom'
+
+@Component({
+ selector: 'gn-ui-geocoding',
+ templateUrl: './geocoding.component.html',
+ styleUrls: ['./geocoding.component.css'],
+})
+export class GeocodingComponent implements OnDestroy {
+ searchText = ''
+ results: any[] = []
+ searchTextChanged = new Subject()
+ destroy$ = new Subject()
+
+ constructor(private mapManager: MapManagerService) {
+ this.searchTextChanged
+ .pipe(
+ debounceTime(300),
+ switchMap((searchText) => {
+ const options: GeoadminOptions = {
+ origins: ['zipcode', 'gg25', 'address'],
+ limit: 6,
+ }
+ return from(queryGeoadmin(searchText, options)).pipe(
+ catchError((error) => {
+ console.error(error)
+ return []
+ })
+ )
+ }),
+ takeUntil(this.destroy$)
+ )
+ .subscribe((results) => {
+ this.results = results
+ })
+ }
+
+ ngOnDestroy() {
+ this.destroy$.next()
+ this.destroy$.complete()
+ }
+
+ onSearchChange(searchText: string) {
+ if (!searchText) {
+ this.clearSearch()
+ return
+ } else {
+ this.searchTextChanged.next(searchText)
+ }
+ }
+
+ clearSearch() {
+ this.searchText = ''
+ this.results = []
+ }
+
+ zoomToLocation(result) {
+ const map = this.mapManager.map
+ const view = map.getView()
+ const geometry = result.geom
+
+ const polygonCoords = geometry.coordinates
+ const transformedCoords = polygonCoords[0].map((coord) => fromLonLat(coord))
+
+ const polygon = new Polygon([transformedCoords])
+
+ view.fit(polygon, {
+ duration: 100,
+ maxZoom: 12,
+ })
+ }
+
+ onEnterPress() {
+ if (this.results && this.results.length > 0) {
+ this.zoomToLocation(this.results[0])
+ }
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index 9ad43926af..e22d2fa78c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,7 @@
"@bartholomej/ngx-translate-extract": "^8.0.2",
"@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
"@camptocamp/ogc-client": "^0.4.0",
+ "@geospatial-sdk/geocoding": "^0.0.5-alpha.1",
"@ltd/j-toml": "~1.35.2",
"@messageformat/core": "^3.0.1",
"@nestjs/common": "10.1.3",
@@ -4310,6 +4311,11 @@
"integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==",
"dev": true
},
+ "node_modules/@geospatial-sdk/geocoding": {
+ "version": "0.0.5-alpha.1",
+ "resolved": "https://registry.npmjs.org/@geospatial-sdk/geocoding/-/geocoding-0.0.5-alpha.1.tgz",
+ "integrity": "sha512-LM1aKG1hl2hnJFLouyjUpCwwT2ToQXeUlExHUvGi/cq1vy2z4AeygLL9etPkEnCPz70B6713gN4mKsmDWdaDiQ=="
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
diff --git a/package.json b/package.json
index 7bf165852a..b0116a5b0f 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
"@bartholomej/ngx-translate-extract": "^8.0.2",
"@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
"@camptocamp/ogc-client": "^0.4.0",
+ "@geospatial-sdk/geocoding": "^0.0.5-alpha.1",
"@ltd/j-toml": "~1.35.2",
"@messageformat/core": "^3.0.1",
"@nestjs/common": "10.1.3",
diff --git a/translations/de.json b/translations/de.json
index 3f3d708988..4326601202 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -177,6 +177,7 @@
"map.add.layer.wfs": "Aus WFS",
"map.add.layer.wms": "Aus WMS",
"map.addFromFile.placeholder": "Klicke hier oder ziehe eine Datei herein",
+ "map.geocoding.placeholder": "",
"map.help.addFromFile": "Klicke oder ziehe eine Datei herein um eine Karte hinzuzufügen (momentan wird nur das GeoJSON-Format unterstützt).",
"map.layer.add": "Hinzufügen",
"map.layers.available": "Verfügbare Layer",
diff --git a/translations/en.json b/translations/en.json
index ce1f5bdf86..bf70f15d11 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -177,6 +177,7 @@
"map.add.layer.wfs": "From WFS",
"map.add.layer.wms": "From WMS",
"map.addFromFile.placeholder": "Click or drop a file here",
+ "map.geocoding.placeholder": "Search for a place",
"map.help.addFromFile": "Click or drag and drop a file to add to the map (currently supports GeoJSON format only).",
"map.layer.add": "Add",
"map.layers.available": "Available Layers",
diff --git a/translations/es.json b/translations/es.json
index 8923c4b162..973148906c 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -177,6 +177,7 @@
"map.add.layer.wfs": "",
"map.add.layer.wms": "",
"map.addFromFile.placeholder": "",
+ "map.geocoding.placeholder": "",
"map.help.addFromFile": "",
"map.layer.add": "",
"map.layers.available": "",
diff --git a/translations/fr.json b/translations/fr.json
index dfbbedfd0a..b50ca355d0 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -177,6 +177,7 @@
"map.add.layer.wfs": "",
"map.add.layer.wms": "",
"map.addFromFile.placeholder": "",
+ "map.geocoding.placeholder": "",
"map.help.addFromFile": "",
"map.layer.add": "",
"map.layers.available": "",
diff --git a/translations/it.json b/translations/it.json
index 7116c0dbce..4939f68bf1 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -177,6 +177,7 @@
"map.add.layer.wfs": "Da un WFS",
"map.add.layer.wms": "Da un WMS",
"map.addFromFile.placeholder": "",
+ "map.geocoding.placeholder": "",
"map.help.addFromFile": "",
"map.layer.add": "",
"map.layers.available": "",
diff --git a/translations/nl.json b/translations/nl.json
index 61e72a4db8..8ac625884a 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -177,6 +177,7 @@
"map.add.layer.wfs": "",
"map.add.layer.wms": "",
"map.addFromFile.placeholder": "",
+ "map.geocoding.placeholder": "",
"map.help.addFromFile": "",
"map.layer.add": "",
"map.layers.available": "",
diff --git a/translations/pt.json b/translations/pt.json
index ae7c49754b..7cec1d606a 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -177,6 +177,7 @@
"map.add.layer.wfs": "",
"map.add.layer.wms": "",
"map.addFromFile.placeholder": "",
+ "map.geocoding.placeholder": "",
"map.help.addFromFile": "",
"map.layer.add": "",
"map.layers.available": "",
diff --git a/translations/sk.json b/translations/sk.json
index 875937f552..1ee8dbcd9a 100644
--- a/translations/sk.json
+++ b/translations/sk.json
@@ -177,6 +177,7 @@
"map.add.layer.wfs": "Z WFS",
"map.add.layer.wms": "Z WMS",
"map.addFromFile.placeholder": "",
+ "map.geocoding.placeholder": "",
"map.help.addFromFile": "",
"map.layer.add": "",
"map.layers.available": "",