geonetwork-ui 2.11.0-dev.6c8b66d60 → 2.11.0-dev.7f3cfba04

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/fesm2022/geonetwork-ui.mjs +1353 -1212
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +109 -59
  4. package/index.d.ts.map +1 -1
  5. package/package.json +6 -5
  6. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +2 -0
  7. package/src/libs/api/metadata-converter/src/lib/fixtures/mel.records.dataset+featurecatalog.ts +317 -0
  8. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +35 -8
  9. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +19 -0
  10. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +2 -0
  11. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +98 -0
  12. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +7 -0
  13. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +2 -1
  14. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +29 -3
  15. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +5 -34
  16. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +6 -13
  17. package/src/libs/common/domain/src/lib/model/search/aggregation.model.ts +2 -0
  18. package/src/libs/common/domain/src/lib/model/search/filter.model.ts +4 -1
  19. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +1 -1
  20. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +42 -62
  21. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +32 -19
  22. package/src/libs/feature/map/src/index.ts +0 -1
  23. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +8 -3
  24. package/src/libs/feature/record/src/lib/state/mdview.effects.ts +3 -1
  25. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -1
  26. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +7 -1
  27. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +26 -4
  28. package/src/libs/feature/search/src/lib/geocoding/geocoding.service.ts +52 -8
  29. package/src/libs/feature/search/src/lib/location-search/location-search.component.html +11 -1
  30. package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +77 -3
  31. package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +1 -0
  32. package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
  33. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +56 -4
  34. package/src/libs/feature/search/src/lib/utils/service/fields.ts +33 -9
  35. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +2 -2
  36. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.ts +2 -2
  37. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.css +0 -4
  38. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +2 -2
  39. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +3 -3
  40. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +2 -1
  41. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +7 -1
  42. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +26 -3
  43. package/src/libs/util/app-config/src/lib/app-config.ts +34 -0
  44. package/src/libs/util/app-config/src/lib/fixtures.ts +37 -1
  45. package/src/libs/util/app-config/src/lib/model.ts +24 -1
  46. package/translations/de.json +8 -2
  47. package/translations/en.json +8 -2
  48. package/translations/es.json +8 -2
  49. package/translations/fr.json +12 -6
  50. package/translations/it.json +8 -2
  51. package/translations/nl.json +8 -2
  52. package/translations/pt.json +8 -2
  53. package/translations/sk.json +8 -2
  54. package/src/libs/feature/map/src/lib/style/index.ts +0 -2
  55. package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
  56. package/src/libs/feature/map/src/lib/style/map-style.service.ts +0 -108
@@ -17,7 +17,6 @@ import {
17
17
  AssociatedRecord,
18
18
  CatalogRecord,
19
19
  DatasetFeatureCatalog,
20
- DatasetFeatureType,
21
20
  LanguageCode,
22
21
  LinkedRecord,
23
22
  RecordRelation,
@@ -182,43 +181,15 @@ export class Gn4Repository implements RecordsRepositoryInterface {
182
181
  )
183
182
  }
184
183
 
185
- private mapEmbeddedFeatureCatalog(
186
- featureTypes: Array<DatasetFeatureType>
187
- ): DatasetFeatureCatalog {
188
- return {
189
- featureTypes: featureTypes.map((featureType) => ({
190
- name: featureType.typeName || '',
191
- definition: featureType.definition || '',
192
- attributes: Array.isArray(featureType.attributeTable)
193
- ? featureType.attributeTable.map((attr) => {
194
- const values = attr.values
195
- ?.filter((v) => v.code || v.label)
196
- .map((v) => ({
197
- code: v.code,
198
- label: v.label,
199
- }))
200
- return {
201
- name: attr.name,
202
- code: attr.code,
203
- definition: attr.definition,
204
- type: attr.type,
205
- ...(values?.length > 0 ? { values } : {}),
206
- }
207
- })
208
- : [],
209
- })),
210
- }
211
- }
212
- getFeatureCatalog(
184
+ queryLinkedFeatureCatalog(
213
185
  record: CatalogRecord,
214
186
  visited: Set<string> = new Set() // prevent looping
215
187
  ): Observable<DatasetFeatureCatalog | null> {
216
188
  if (
217
- record.extras?.['featureTypes'] &&
218
- Array.isArray(record.extras['featureTypes']) &&
219
- record.extras['featureTypes'].length > 0
189
+ 'featureTypeDescriptions' in record &&
190
+ record.featureTypeDescriptions?.length > 0
220
191
  ) {
221
- return of(this.mapEmbeddedFeatureCatalog(record.extras['featureTypes']))
192
+ return of({ featureTypes: record.featureTypeDescriptions })
222
193
  }
223
194
 
224
195
  const featureCatalogIdentifier = record.extras[
@@ -228,7 +199,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
228
199
  visited.add(featureCatalogIdentifier)
229
200
  return this.getRecord(featureCatalogIdentifier).pipe(
230
201
  switchMap((record) =>
231
- record ? this.getFeatureCatalog(record, visited) : of(null)
202
+ record ? this.queryLinkedFeatureCatalog(record, visited) : of(null)
232
203
  )
233
204
  )
234
205
  }
@@ -316,6 +316,7 @@ export interface DatasetRecord extends BaseRecord {
316
316
  spatialExtents: Array<DatasetSpatialExtent>
317
317
  temporalExtents: Array<DatasetTemporalExtent>
318
318
  spatialRepresentation?: SpatialRepresentationType
319
+ featureTypeDescriptions?: DatasetFeatureCatalog['featureTypes']
319
320
  }
320
321
 
321
322
  export interface ServiceEndpoint {
@@ -350,30 +351,22 @@ export interface ReuseRecord extends BaseRecord {
350
351
  export type DatasetFeatureCatalog = {
351
352
  featureTypes: Array<{
352
353
  name: string
353
- definition: string
354
+ description?: string
354
355
  attributes: DatasetFeatureAttribute[]
355
356
  }>
356
357
  }
357
358
  export interface DatasetFeatureAttributeValue {
358
359
  code?: string
359
- definition?: string
360
+ description?: string
360
361
  label?: string
361
362
  }
362
363
  export interface DatasetFeatureAttribute {
363
364
  name: string
364
- definition: string
365
- type: string
366
- code: string
365
+ description?: string
366
+ type?: string
367
+ code?: string
367
368
  values?: Array<DatasetFeatureAttributeValue>
368
369
  }
369
- export interface DatasetFeatureType {
370
- aliases: string
371
- code: string
372
- isAbstract: string
373
- typeName: string
374
- definition: string
375
- attributeTable: Array<DatasetFeatureAttribute>
376
- }
377
370
 
378
371
  export type ReuseType = 'application' | 'map' | 'other'
379
372
 
@@ -7,6 +7,8 @@ export interface TermsAggregationParams {
7
7
  limit: number
8
8
  sort: AggregationSort
9
9
  filter?: string
10
+ includeValues?: string[]
11
+ excludeValues?: string[]
10
12
  }
11
13
  export interface HistogramAggregationParams {
12
14
  type: 'histogram'
@@ -21,7 +21,10 @@ export type QueryString = {
21
21
  query_string: string
22
22
  }
23
23
  export type QueryRange = {
24
- range: Record<string, { format: string; gte: string; lte: string }>
24
+ range: Record<
25
+ string,
26
+ { format: string; gte?: string; lte?: string; relation?: string }
27
+ >
25
28
  }
26
29
  export type QueryGeoShape = {
27
30
  geo_shape: {
@@ -17,7 +17,7 @@ export abstract class RecordsRepositoryInterface {
17
17
  abstract search(params: SearchParams): Observable<SearchResults>
18
18
  abstract getMatchesCount(filters: FieldFilters): Observable<number>
19
19
  abstract getRecord(uniqueIdentifier: string): Observable<CatalogRecord | null>
20
- abstract getFeatureCatalog(
20
+ abstract queryLinkedFeatureCatalog(
21
21
  record: CatalogRecord
22
22
  ): Observable<DatasetFeatureCatalog | null>
23
23
  abstract aggregate(params: AggregationsParams): Observable<Aggregations>
@@ -164,69 +164,49 @@ As such, **it is not very interesting at all.**`,
164
164
  extras: {
165
165
  isPublishedToAll: true,
166
166
  edit: true,
167
- featureTypes: [
168
- {
169
- attributeTable: [
170
- {
171
- code: 'OBJECTID',
172
- name: 'OBJECTID',
173
- link: '',
174
- definition: 'Object identifier',
175
- type: 'OID',
176
- },
177
- {
178
- code: 'NOM',
179
- name: 'Nom',
180
- link: '',
181
- definition: 'Nom de la rue',
182
- type: 'String (48)',
183
- values: [
184
- {
185
- code: 'Pomme',
186
- label: 'Les Pommiers',
187
- description: 'Sous les pommiers',
188
- },
189
- {
190
- code: 'Cotton',
191
- label: 'Rue Cotton',
192
- description: 'Rue Cotton',
193
- },
194
- { code: "Passage de l'échiquier" },
195
- { description: 'Rue du Charlieu' },
196
- ],
197
- },
198
- {
199
- code: 'RUE',
200
- name: 'Rue',
201
- link: '',
202
- definition: '',
203
- type: 'String (50)',
204
- },
205
- ],
206
- code: '',
207
- aliases: '',
208
- typeName: "Catalogue d'attributs N°1",
209
- definition: 'Définition du catalogue d attributs N°1',
210
- isAbstract: 'false',
211
- },
212
- {
213
- attributeTable: [
214
- {
215
- code: 'UniqueObject',
216
- name: 'unique object ',
217
- link: '',
218
- definition: 'this is the only object of this catalog',
219
- type: 'String (50)',
220
- },
221
- ],
222
- code: '',
223
- aliases: '',
224
- typeName: "Catalogue d'attributs N°2",
225
- definition: 'Définition du catalogue d attributs N°2',
226
- isAbstract: 'false',
227
- },
228
- ],
229
167
  },
168
+ featureTypeDescriptions: [
169
+ {
170
+ name: "Catalogue d'attributs N°1",
171
+ description: 'Définition du catalogue d attributs N°1',
172
+ attributes: [
173
+ {
174
+ name: 'OBJECTID',
175
+ description: 'Object identifier',
176
+ code: 'OBJECTID',
177
+ type: 'OID',
178
+ },
179
+ {
180
+ name: 'Nom',
181
+ description: 'Nom de la rue',
182
+ code: 'NOM',
183
+ type: 'String (48)',
184
+ values: [
185
+ { code: 'Pomme', label: 'Les Pommiers' },
186
+ { code: 'Cotton', label: 'Rue Cotton' },
187
+ { code: "Passage de l'échiquier" },
188
+ ],
189
+ },
190
+ {
191
+ name: 'Rue',
192
+ code: 'RUE',
193
+ type: 'String (50)',
194
+ },
195
+ ],
196
+ },
197
+ {
198
+ name: "Catalogue d'attributs N°2",
199
+ description: 'Définition du catalogue d attributs N°2',
200
+ attributes: [
201
+ {
202
+ name: 'unique object ',
203
+ description: 'this is the only object of this catalog',
204
+ code: 'UniqueObject',
205
+ type: 'String (50)',
206
+ },
207
+ ],
208
+ },
209
+ ],
230
210
  },
231
211
  {
232
212
  uniqueIdentifier: '7d002c4c-92ef-4b9f-a568-d732f740b99e',
@@ -209,7 +209,8 @@ export class DataService {
209
209
  ogcApiLink: DatasetServiceDistribution
210
210
  ): Promise<DatasetOnlineResource[]> {
211
211
  const collectionInfo = await this.getDownloadUrlsFromOgcApi(
212
- ogcApiLink.url.href
212
+ ogcApiLink.url.href,
213
+ ogcApiLink.name
213
214
  )
214
215
 
215
216
  return Object.keys(collectionInfo.bulkDownloadLinks).map((mimeType) => {
@@ -229,31 +230,41 @@ export class DataService {
229
230
  })
230
231
  }
231
232
 
232
- async getDownloadUrlsFromOgcApi(url: string): Promise<OgcApiCollectionInfo> {
233
+ private async getTargetCollectionId(
234
+ endpoint: OgcApiEndpoint,
235
+ collectionId?: string
236
+ ): Promise<string | undefined> {
237
+ const collections = await endpoint.featureCollections
238
+ return collections.length === 1 && !collectionId
239
+ ? collections[0]
240
+ : collectionId
241
+ }
242
+
243
+ async getDownloadUrlsFromOgcApi(
244
+ url: string,
245
+ collectionId?: string
246
+ ): Promise<OgcApiCollectionInfo> {
233
247
  const endpoint = new OgcApiEndpoint(url)
234
- return await endpoint.featureCollections
235
- .then((collections) => {
236
- return endpoint.getCollectionInfo(collections[0])
237
- })
238
- .catch((error) => {
239
- throw new Error(`ogc.unreachable.unknown`)
240
- })
248
+ try {
249
+ const id = await this.getTargetCollectionId(endpoint, collectionId)
250
+ return await endpoint.getCollectionInfo(id)
251
+ } catch {
252
+ throw new Error('ogc.unreachable.unknown')
253
+ }
241
254
  }
242
255
 
243
256
  async getItemsFromOgcApi(
244
257
  url: string,
258
+ collectionId?: string,
245
259
  limit?: number
246
260
  ): Promise<OgcApiRecord[]> {
247
261
  const endpoint = new OgcApiEndpoint(url)
248
- return await endpoint.featureCollections
249
- .then((collections) => {
250
- return collections.length
251
- ? endpoint.getCollectionItems(collections[0], limit)
252
- : null
253
- })
254
- .catch(() => {
255
- throw new Error(`ogc.unreachable.unknown`)
256
- })
262
+ try {
263
+ const id = await this.getTargetCollectionId(endpoint, collectionId)
264
+ return await endpoint.getCollectionItems(id, limit)
265
+ } catch {
266
+ throw new Error('ogc.unreachable.unknown')
267
+ }
257
268
  }
258
269
 
259
270
  async getItemsFromStacApi(
@@ -381,7 +392,9 @@ export class DataService {
381
392
  link.type === 'service' &&
382
393
  link.accessServiceProtocol === 'ogcFeatures'
383
394
  ) {
384
- return from(this.getDownloadUrlsFromOgcApi(link.url.href)).pipe(
395
+ return from(
396
+ this.getDownloadUrlsFromOgcApi(link.url.href, link.name)
397
+ ).pipe(
385
398
  switchMap((collectionInfo) => {
386
399
  const isMimeTypeJson = (mimeType: string): boolean => {
387
400
  return mimeType.toLowerCase().indexOf('json') > -1
@@ -6,7 +6,6 @@ export * from './lib/feature-map.module'
6
6
  export * from './lib/map-state-container/map-state-container.component'
7
7
  export * from './lib/constant'
8
8
  export * from './lib/utils'
9
- export * from './lib/style'
10
9
  export * from './lib/layers-panel/layers-panel.component'
11
10
  export * from './lib/add-layer-from-catalog/add-layer-from-catalog.component'
12
11
  export * from './lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component'
@@ -33,7 +33,6 @@ import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain
33
33
  import { NOT_KNOWN_CONSTRAINT } from '../../../../../../libs/feature/editor/src'
34
34
  import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
35
35
  import { ButtonComponent, TextInputComponent } from '../../../../../../libs/ui/inputs/src'
36
- import { getOptionalEditorConfig } from '../../../../../../libs/util/app-config/src'
37
36
  import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
38
37
  import { iconoirAppWindow, iconoirPlusCircle } from '@ng-icons/iconoir'
39
38
  import { matCloseOutline } from '@ng-icons/material-icons/outline'
@@ -48,6 +47,10 @@ marker('notify.reuse.form.error.title')
48
47
  marker('notify.reuse.form.error.body')
49
48
 
50
49
  export const REUSE_FORM_URL = new InjectionToken<string>('reuseFormUrl')
50
+ // language assigned to the created reuse record; falls back to the current UI language
51
+ export const NEW_RECORD_DEFAULT_LANGUAGE = new InjectionToken<string>(
52
+ 'newRecordDefaultLanguage'
53
+ )
51
54
 
52
55
  @Component({
53
56
  selector: 'gn-ui-notify-reuse-form',
@@ -86,6 +89,9 @@ export class NotifyReuseFormComponent implements OnDestroy {
86
89
  private readonly notificationsService = inject(NotificationsService)
87
90
  private readonly platformServiceInterface = inject(PlatformServiceInterface)
88
91
  reuseFormUrl = inject(REUSE_FORM_URL, { optional: true })
92
+ private newRecordDefaultLanguage = inject(NEW_RECORD_DEFAULT_LANGUAGE, {
93
+ optional: true,
94
+ })
89
95
  private locationStrategy = inject(LocationStrategy)
90
96
 
91
97
  me = toSignal(this.platformServiceInterface.getMe())
@@ -158,8 +164,7 @@ export class NotifyReuseFormComponent implements OnDestroy {
158
164
  submit() {
159
165
  if (!this.isFormValid()) return
160
166
  const defaultLang =
161
- getOptionalEditorConfig()?.NEW_RECORD_DEFAULT_LANGUAGE ??
162
- this.translate.currentLang
167
+ this.newRecordDefaultLanguage ?? this.translate.currentLang
163
168
  const onlineResource: OnlineLinkResource = {
164
169
  type: 'link',
165
170
  url: new URL(this.url()),
@@ -40,7 +40,9 @@ export class MdViewEffects {
40
40
  this.actions$.pipe(
41
41
  ofType(MdViewActions.loadFullMetadataSuccess),
42
42
  filter(({ full }) => full !== undefined),
43
- switchMap(({ full }) => this.recordsRepository.getFeatureCatalog(full)),
43
+ switchMap(({ full }) =>
44
+ this.recordsRepository.queryLinkedFeatureCatalog(full)
45
+ ),
44
46
  map((featureCatalog) =>
45
47
  MdViewActions.loadFeatureCatalogSuccess({
46
48
  datasetCatalog: featureCatalog,
@@ -162,7 +162,7 @@ export class MdViewFacade {
162
162
  link.accessServiceProtocol === 'ogcFeatures'
163
163
  ) {
164
164
  return from(
165
- this.dataService.getItemsFromOgcApi(link.url.href, 1)
165
+ this.dataService.getItemsFromOgcApi(link.url.href, link.name, 1)
166
166
  ).pipe(
167
167
  map((collectionRecords: OgcApiRecord[]) => {
168
168
  return collectionRecords &&
@@ -13,7 +13,13 @@
13
13
  (bboxChange)="onBboxChange($event)"
14
14
  (errorChange)="onSpatialExtentError($event)"
15
15
  [attr.data-cy-field]="fieldName"
16
- ></gn-ui-spatial-extent-dropdown>
16
+ >
17
+ <gn-ui-location-search
18
+ [placeholder]="'search.filters.spatialExtent.searchLocation' | translate"
19
+ (bboxSelected)="onLocationSelected($event)"
20
+ data-cy="locationSearch"
21
+ ></gn-ui-location-search>
22
+ </gn-ui-spatial-extent-dropdown>
17
23
  } @else {
18
24
  <gn-ui-dropdown-multiselect
19
25
  class="w-full"
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  ChangeDetectionStrategy,
3
3
  Component,
4
+ InjectionToken,
4
5
  Input,
5
6
  OnInit,
7
+ ViewChild,
6
8
  inject,
7
9
  } from '@angular/core'
8
10
  import {
@@ -26,8 +28,20 @@ import { DateRange } from '../../../../../../libs/api/repository/src'
26
28
  import { CommonModule } from '@angular/common'
27
29
  import { BoundingBox } from '../../../../../../libs/util/shared/src'
28
30
  import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
29
- import { TranslateService } from '@ngx-translate/core'
30
- import { getOptionalSearchConfig } from '../../../../../../libs/util/app-config/src'
31
+ import { TranslatePipe, TranslateService } from '@ngx-translate/core'
32
+ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
33
+ import {
34
+ LocationBbox,
35
+ LocationSearchComponent,
36
+ } from '../location-search/location-search.component'
37
+
38
+ marker('search.filters.spatialExtent.searchLocation')
39
+
40
+ // in MB, null means no limit
41
+ export const SPATIAL_EXTENT_MAX_FILE_SIZE = new InjectionToken<number | null>(
42
+ 'spatialExtentMaxFileSize',
43
+ { factory: () => null }
44
+ )
31
45
 
32
46
  @Component({
33
47
  selector: 'gn-ui-filter-dropdown',
@@ -40,6 +54,8 @@ import { getOptionalSearchConfig } from '../../../../../../libs/util/app-config/
40
54
  DateRangeDropdownComponent,
41
55
  DropdownMultiselectComponent,
42
56
  SpatialExtentDropdownComponent,
57
+ LocationSearchComponent,
58
+ TranslatePipe,
43
59
  ],
44
60
  })
45
61
  export class FilterDropdownComponent implements OnInit {
@@ -52,8 +68,10 @@ export class FilterDropdownComponent implements OnInit {
52
68
  @Input() fieldName: string
53
69
  @Input() title: string
54
70
 
55
- spatialExtentMaxFileSize =
56
- getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE
71
+ @ViewChild(SpatialExtentDropdownComponent)
72
+ spatialExtentDropdown: SpatialExtentDropdownComponent
73
+
74
+ spatialExtentMaxFileSize = inject(SPATIAL_EXTENT_MAX_FILE_SIZE)
57
75
 
58
76
  fieldType: FieldType
59
77
  choices$: Observable<Choice[]>
@@ -96,6 +114,10 @@ export class FilterDropdownComponent implements OnInit {
96
114
  this.clearSpatialExtentErrorNotification()
97
115
  }
98
116
 
117
+ onLocationSelected({ bbox, label }: LocationBbox) {
118
+ this.spatialExtentDropdown.setLocationBbox(bbox, label)
119
+ }
120
+
99
121
  onSpatialExtentError(error: SpatialExtentDropdownError) {
100
122
  this.clearSpatialExtentErrorNotification()
101
123
  this.spatialExtentErrorNotificationId =
@@ -5,28 +5,60 @@ import {
5
5
  GeocodingResult,
6
6
  queryGeonames,
7
7
  GeonamesOptions,
8
- DataGouvFrOptions,
9
- queryDataGouvFr,
8
+ GeoplateformeOptions,
9
+ queryGeoplateforme,
10
10
  } from '@geospatial-sdk/geocoding'
11
11
  import { from, Observable, throwError } from 'rxjs'
12
12
  import { catchError } from 'rxjs/operators'
13
13
 
14
14
  type GeoadminGeocodingProvider = ['geoadmin', GeoadminOptions]
15
15
  type GeonamesGeocodingProvider = ['geonames', GeonamesOptions]
16
- type DataGouvFrGeocodingProvider = ['data-gouv-fr', DataGouvFrOptions]
16
+ type GeoplateformeGeocodingProvider = ['geoplateforme', GeoplateformeOptions]
17
17
  export type GeocodingProvider =
18
18
  | GeoadminGeocodingProvider
19
19
  | GeonamesGeocodingProvider
20
- | DataGouvFrGeocodingProvider
20
+ | GeoplateformeGeocodingProvider
21
+
22
+ export const DEFAULT_GEOCODING_PROVIDER = [
23
+ 'geonames',
24
+ { maxRows: 5 },
25
+ ] as GeocodingProvider
21
26
 
22
27
  export const GEOCODING_PROVIDER = new InjectionToken<GeocodingProvider>(
23
28
  'geocoding-provider',
24
29
  {
25
30
  providedIn: 'root',
26
- factory: () => ['geonames', { maxRows: 5 }] as GeocodingProvider,
31
+ factory: (): GeocodingProvider => DEFAULT_GEOCODING_PROVIDER,
27
32
  }
28
33
  )
29
34
 
35
+ // these options are arrays in the providers, but they can be configured
36
+ // with a single value for simplicity
37
+ const GEOCODING_PROVIDER_ARRAY_OPTIONS: {
38
+ geoadmin: (keyof GeoadminOptions)[]
39
+ geoplateforme: (keyof GeoplateformeOptions)[]
40
+ } = {
41
+ geoadmin: ['origins', 'features'],
42
+ geoplateforme: [
43
+ 'index',
44
+ 'category',
45
+ 'type',
46
+ 'postCode',
47
+ 'cityCode',
48
+ 'depCode',
49
+ ],
50
+ }
51
+
52
+ function wrapArrayOptions<T extends object>(options: T, keys: (keyof T)[]): T {
53
+ const wrapped = { ...options }
54
+ for (const key of keys) {
55
+ if (typeof wrapped[key] === 'string') {
56
+ wrapped[key] = [wrapped[key]] as T[keyof T]
57
+ }
58
+ }
59
+ return wrapped
60
+ }
61
+
30
62
  @Injectable({
31
63
  providedIn: 'root',
32
64
  })
@@ -38,7 +70,13 @@ export class GeocodingService {
38
70
  switch (this.provider[0]) {
39
71
  case 'geoadmin':
40
72
  queryObservable = from(
41
- queryGeoadmin(text, this.provider[1] as GeoadminOptions)
73
+ queryGeoadmin(
74
+ text,
75
+ wrapArrayOptions(
76
+ this.provider[1] as GeoadminOptions,
77
+ GEOCODING_PROVIDER_ARRAY_OPTIONS.geoadmin
78
+ )
79
+ )
42
80
  )
43
81
  break
44
82
  case 'geonames':
@@ -46,9 +84,15 @@ export class GeocodingService {
46
84
  queryGeonames(text, this.provider[1] as GeonamesOptions)
47
85
  )
48
86
  break
49
- case 'data-gouv-fr':
87
+ case 'geoplateforme':
50
88
  queryObservable = from(
51
- queryDataGouvFr(text, this.provider[1] as DataGouvFrOptions)
89
+ queryGeoplateforme(text, {
90
+ ...wrapArrayOptions(
91
+ this.provider[1] as GeoplateformeOptions,
92
+ GEOCODING_PROVIDER_ARRAY_OPTIONS.geoplateforme
93
+ ),
94
+ returnTrueGeometry: true,
95
+ })
52
96
  )
53
97
  break
54
98
  default:
@@ -2,7 +2,17 @@
2
2
  [action]="searchAction"
3
3
  [displayWithFn]="displayWithFn"
4
4
  [placeholder]="placeholder"
5
- [displayWithTemplate]="displayWithTemplate"
5
+ [displayWithTemplate]="defaultItemTpl"
6
+ [clearOnSelection]="true"
6
7
  (itemSelected)="handleItemSelected($event)"
7
8
  >
8
9
  </gn-ui-autocomplete>
10
+
11
+ <ng-template #defaultItemTpl let-result>
12
+ <div class="py-2">
13
+ @if (getSecondaryLabel(result)) {
14
+ <div class="gn-ui-card-detail">{{ getSecondaryLabel(result) }}</div>
15
+ }
16
+ <div class="gn-ui-card-title">{{ getDisplayLabel(result) }}</div>
17
+ </div>
18
+ </ng-template>