geonetwork-ui 2.11.0-dev.aed5b2e5b → 2.11.0-dev.b75ae786a

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 (49) hide show
  1. package/fesm2022/geonetwork-ui.mjs +3219 -3235
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +113 -83
  4. package/index.d.ts.map +1 -1
  5. package/package.json +2 -1
  6. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +120 -34
  7. package/src/libs/common/domain/src/lib/model/search/filter.model.ts +24 -2
  8. package/src/libs/feature/map/src/index.ts +0 -2
  9. package/src/libs/feature/map/src/lib/feature-map.module.ts +1 -8
  10. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +8 -3
  11. package/src/libs/feature/search/src/index.ts +2 -0
  12. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +3 -0
  13. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +21 -4
  14. package/src/libs/feature/{map/src/lib → search/src/lib/geocoding}/geocoding.service.ts +16 -7
  15. package/src/libs/feature/search/src/lib/location-search/location-search.component.html +8 -0
  16. package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +40 -0
  17. package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +2 -0
  18. package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
  19. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +14 -2
  20. package/src/libs/feature/search/src/lib/utils/service/fields.ts +49 -5
  21. package/src/libs/ui/elements/src/lib/service-capabilities/service-capabilities.component.ts +2 -1
  22. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +10 -1
  23. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +8 -0
  24. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +7 -4
  25. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +12 -9
  26. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.model.ts +2 -2
  27. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +5 -11
  28. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +34 -4
  29. package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +1 -0
  30. package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -0
  31. package/src/libs/util/app-config/src/lib/app-config.ts +20 -0
  32. package/src/libs/util/app-config/src/lib/fixtures.ts +16 -1
  33. package/src/libs/util/app-config/src/lib/model.ts +14 -1
  34. package/src/libs/util/shared/src/lib/links/link-utils.ts +19 -10
  35. package/src/libs/util/shared/src/lib/utils/geojson.ts +24 -0
  36. package/translations/de.json +10 -4
  37. package/translations/en.json +11 -5
  38. package/translations/es.json +10 -4
  39. package/translations/fr.json +11 -5
  40. package/translations/it.json +10 -4
  41. package/translations/nl.json +10 -4
  42. package/translations/pt.json +10 -4
  43. package/translations/sk.json +10 -4
  44. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.css +0 -0
  45. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.html +0 -41
  46. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.ts +0 -112
  47. package/src/libs/feature/map/src/lib/style/index.ts +0 -2
  48. package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
  49. package/src/libs/feature/map/src/lib/style/map-style.service.ts +0 -108
@@ -34,7 +34,12 @@ import {
34
34
  LanguageCode,
35
35
  } from '../../../../../../../libs/common/domain/src/lib/model/record'
36
36
  import { TranslateService } from '@ngx-translate/core'
37
- import { getGeometryBoundingBox } from '../../../../../../../libs/util/shared/src'
37
+ import {
38
+ bboxToPolygon,
39
+ BoundingBox,
40
+ getGeometryBoundingBox,
41
+ isBoundingBox,
42
+ } from '../../../../../../../libs/util/shared/src'
38
43
  import { getLength as getGeodesicLength } from 'ol/sphere.js'
39
44
  import { LineString } from 'ol/geom.js'
40
45
 
@@ -49,7 +54,10 @@ export class ElasticsearchService {
49
54
 
50
55
  // runtime fields are computed using a Painless script
51
56
  // see: https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-mapping-fields.html
52
- private runtimeFields: Record<string, string> = {}
57
+ private runtimeFields: Record<
58
+ string,
59
+ { script: string; type: 'keyword' | 'date' }
60
+ > = {}
53
61
 
54
62
  // we're using getters in case the defined languages change over time
55
63
  private get metadataLang(): LanguageCode {
@@ -91,8 +99,8 @@ export class ElasticsearchService {
91
99
  const addMapping = (fieldName: string) => {
92
100
  if (!payload.runtime_mappings) payload.runtime_mappings = {}
93
101
  payload.runtime_mappings[fieldName] = {
94
- type: 'keyword',
95
- script: this.runtimeFields[fieldName],
102
+ type: this.runtimeFields[fieldName].type,
103
+ script: this.runtimeFields[fieldName].script,
96
104
  }
97
105
  }
98
106
  const lookForField = (node: unknown) => {
@@ -113,6 +121,14 @@ export class ElasticsearchService {
113
121
  ) {
114
122
  addMapping(runtimeField)
115
123
  }
124
+ if (
125
+ runtimeField in node &&
126
+ typeof node[runtimeField] === 'object' &&
127
+ node[runtimeField] !== null &&
128
+ ('gte' in node[runtimeField] || 'lte' in node[runtimeField])
129
+ ) {
130
+ addMapping(runtimeField)
131
+ }
116
132
  if (
117
133
  'query' in node &&
118
134
  typeof node.query === 'string' &&
@@ -131,8 +147,12 @@ export class ElasticsearchService {
131
147
  return payload
132
148
  }
133
149
 
134
- registerRuntimeField(fieldName: string, expression: string) {
135
- this.runtimeFields[fieldName] = expression
150
+ registerRuntimeField(
151
+ fieldName: string,
152
+ expression: string,
153
+ type: 'keyword' | 'date' = 'keyword'
154
+ ) {
155
+ this.runtimeFields[fieldName] = { script: expression, type }
136
156
  }
137
157
 
138
158
  getMetadataByIdsPayload(uuids: string[]): EsSearchParams {
@@ -264,8 +284,68 @@ export class ElasticsearchService {
264
284
  return this.metadataLang === 'current'
265
285
  }
266
286
 
267
- private filtersToQuery(
287
+ private findSpatialFilterExtent(
268
288
  filters: FieldFilters | FiltersAggregationParams | string
289
+ ): BoundingBox | undefined {
290
+ if (typeof filters === 'string') {
291
+ return undefined
292
+ }
293
+ return Object.values(filters).find(isBoundingBox)
294
+ }
295
+
296
+ // builds a query matching records whose dates intersect the filter range
297
+ private buildDateRangeQuery(searchField: string, dateRange: DateRange) {
298
+ // fields ending with 'DateRange' maps as ES range type
299
+ if (searchField.endsWith('DateRange')) {
300
+ return {
301
+ range: {
302
+ [searchField]: {
303
+ ...(dateRange.start && { gte: formatDate(dateRange.start) }),
304
+ ...(dateRange.end && { lte: formatDate(dateRange.end) }),
305
+ format: 'yyyy-MM-dd',
306
+ relation: 'intersects',
307
+ },
308
+ },
309
+ }
310
+ }
311
+ const minField = `${searchField}Min`
312
+ const maxField = `${searchField}Max`
313
+ if (minField in this.runtimeFields && maxField in this.runtimeFields) {
314
+ // min/max runtime fields form an interval per record; match if it intersects the filter range
315
+ const filter = [
316
+ dateRange.start && {
317
+ range: {
318
+ [maxField]: {
319
+ gte: formatDate(dateRange.start),
320
+ format: 'yyyy-MM-dd',
321
+ },
322
+ },
323
+ },
324
+ dateRange.end && {
325
+ range: {
326
+ [minField]: {
327
+ lte: formatDate(dateRange.end),
328
+ format: 'yyyy-MM-dd',
329
+ },
330
+ },
331
+ },
332
+ ].filter(Boolean)
333
+ return { bool: { filter } }
334
+ }
335
+ return {
336
+ range: {
337
+ [searchField]: {
338
+ ...(dateRange.start && { gte: formatDate(dateRange.start) }),
339
+ ...(dateRange.end && { lte: formatDate(dateRange.end) }),
340
+ format: 'yyyy-MM-dd',
341
+ },
342
+ },
343
+ }
344
+ }
345
+
346
+ private filtersToQuery(
347
+ filters: FieldFilters | FiltersAggregationParams | string,
348
+ spatialFilterExtent = this.findSpatialFilterExtent(filters)
269
349
  ): FilterQuery {
270
350
  const addQuote = (key: string) => (/^\/.+\/$/.test(key) ? key : `"${key}"`)
271
351
  const makeQuery = (filter: FieldFilter): string => {
@@ -286,7 +366,9 @@ export class ElasticsearchService {
286
366
  ? filters
287
367
  : Object.keys(filters)
288
368
  .filter((fieldname) => fieldname !== 'gn-ui-crossFieldFilter')
369
+ .filter((fieldname) => !isBoundingBox(filters[fieldname]))
289
370
  .filter((fieldname) => !isDateRange(filters[fieldname]))
371
+ .filter((fieldname) => !Array.isArray(filters[fieldname]))
290
372
  .filter(
291
373
  (fieldname) =>
292
374
  filters[fieldname] &&
@@ -299,37 +381,33 @@ export class ElasticsearchService {
299
381
  if (filters['gn-ui-crossFieldFilter']) {
300
382
  queryString = `${queryString} AND (${filters['gn-ui-crossFieldFilter']})`
301
383
  }
302
- const queryRange = Object.entries(filters)
303
- .filter(([, value]) => isDateRange(value))
304
- .map(([searchField, dateRange]) => {
305
- return {
306
- searchField,
307
- dateRange,
308
- } as {
309
- searchField: string
310
- dateRange: DateRange
311
- }
312
- })[0]
384
+ const queryRanges = Object.entries(filters).filter(([, value]) =>
385
+ isDateRange(value)
386
+ ) as [string, DateRange][]
313
387
  const queryParts = [
314
388
  queryString && {
315
389
  query_string: {
316
390
  query: queryString,
317
391
  },
318
392
  },
319
- queryRange &&
320
- queryRange.dateRange && {
321
- range: {
322
- [queryRange.searchField]: {
323
- ...(queryRange.dateRange.start && {
324
- gte: formatDate(queryRange.dateRange.start),
325
- }),
326
- ...(queryRange.dateRange.end && {
327
- lte: formatDate(queryRange.dateRange.end),
328
- }),
329
- format: 'yyyy-MM-dd',
393
+ ...queryRanges.map(([searchField, dateRange]) =>
394
+ this.buildDateRangeQuery(searchField, dateRange)
395
+ ),
396
+ spatialFilterExtent && {
397
+ geo_shape: {
398
+ geom: {
399
+ shape: {
400
+ type: 'envelope',
401
+ // spatialFilterExtent is [minX, minY, maxX, maxY]; envelope coordinates are [top-left, bottom-right]
402
+ coordinates: [
403
+ [spatialFilterExtent[0], spatialFilterExtent[3]],
404
+ [spatialFilterExtent[2], spatialFilterExtent[1]],
405
+ ],
330
406
  },
407
+ relation: 'intersects',
331
408
  },
332
409
  },
410
+ },
333
411
  ].filter(Boolean)
334
412
  return queryParts.length > 0 ? (queryParts as FilterQuery) : undefined
335
413
  }
@@ -368,7 +446,12 @@ export class ElasticsearchService {
368
446
  },
369
447
  })
370
448
  }
371
- const queryFilters = this.filtersToQuery(fieldSearchFilters)
449
+ // a spatial extent filter takes precedence over the preference geometry for boosting
450
+ const spatialFilterExtent = this.findSpatialFilterExtent(fieldSearchFilters)
451
+ const queryFilters = this.filtersToQuery(
452
+ fieldSearchFilters,
453
+ spatialFilterExtent
454
+ )
372
455
  if (queryFilters) {
373
456
  filter.push(...queryFilters)
374
457
  }
@@ -379,7 +462,10 @@ export class ElasticsearchService {
379
462
  },
380
463
  })
381
464
  }
382
- if (geometry) {
465
+ const boostGeometry = spatialFilterExtent
466
+ ? bboxToPolygon(spatialFilterExtent)
467
+ : geometry
468
+ if (boostGeometry) {
383
469
  // boosts applied using the filter geometry:
384
470
  // * records completely within the geometry receive a boost of 5
385
471
  // * records intersecting the geometry receive a boost of 2
@@ -389,7 +475,7 @@ export class ElasticsearchService {
389
475
  {
390
476
  geo_shape: {
391
477
  geom: {
392
- shape: geometry,
478
+ shape: boostGeometry,
393
479
  relation: 'within',
394
480
  },
395
481
  boost: 5.0,
@@ -398,7 +484,7 @@ export class ElasticsearchService {
398
484
  {
399
485
  geo_shape: {
400
486
  geom: {
401
- shape: geometry,
487
+ shape: boostGeometry,
402
488
  relation: 'intersects',
403
489
  },
404
490
  boost: 2.0,
@@ -409,7 +495,7 @@ export class ElasticsearchService {
409
495
  // this will boost the results variably depending on their distance from the given geometry
410
496
  // note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
411
497
  // combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
412
- const bbox = getGeometryBoundingBox(geometry)
498
+ const bbox = getGeometryBoundingBox(boostGeometry)
413
499
  const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2]
414
500
  const northToCenter = new LineString([
415
501
  [center[0], bbox[3]],
@@ -6,17 +6,39 @@ export type FieldFilterByRange = {
6
6
  start?: Date
7
7
  end?: Date
8
8
  }
9
+ // matches util/shared's BoundingBox; duplicated here to avoid a circular
10
+ // dependency (util/shared already depends on common/domain)
11
+ export type FieldFilterByBoundingBox = [number, number, number, number]
9
12
 
10
13
  export type FieldFilter =
11
14
  | FieldFilterByExpression
12
15
  | FieldFilterByValues
13
16
  | FieldFilterByRange
17
+ | FieldFilterByBoundingBox
14
18
  export type FieldFilters = Record<FieldName, FieldFilter>
15
19
 
16
20
  export type QueryString = {
17
21
  query_string: string
18
22
  }
19
23
  export type QueryRange = {
20
- range: Record<string, { format: string; gte: string; lte: string }>
24
+ range: Record<
25
+ string,
26
+ { format: string; gte?: string; lte?: string; relation?: string }
27
+ >
21
28
  }
22
- export type FilterQuery = Array<QueryString | QueryRange>
29
+ export type QueryGeoShape = {
30
+ geo_shape: {
31
+ geom: {
32
+ shape: { type: string; coordinates: number[][] }
33
+ relation: string
34
+ }
35
+ }
36
+ }
37
+ export type QueryBool = {
38
+ bool: {
39
+ filter: QueryRange[]
40
+ }
41
+ }
42
+ export type FilterQuery = Array<
43
+ QueryString | QueryRange | QueryGeoShape | QueryBool
44
+ >
@@ -6,8 +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'
13
- export * from './lib/geocoding/geocoding.component'
@@ -2,18 +2,11 @@ import { NgModule } from '@angular/core'
2
2
  import { StoreModule } from '@ngrx/store'
3
3
  import * as fromMap from './+state/map.reducer'
4
4
  import { MapFacade } from './+state/map.facade'
5
- import { GEOCODING_PROVIDER, GeocodingProvider } from './geocoding.service'
6
5
 
7
6
  @NgModule({
8
7
  imports: [
9
8
  StoreModule.forFeature(fromMap.MAP_FEATURE_KEY, fromMap.mapReducer),
10
9
  ],
11
- providers: [
12
- MapFacade,
13
- {
14
- provide: GEOCODING_PROVIDER,
15
- useValue: ['geonames', { maxRows: 5 }] as GeocodingProvider,
16
- },
17
- ],
10
+ providers: [MapFacade],
18
11
  })
19
12
  export class FeatureMapModule {}
@@ -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()),
@@ -1,4 +1,6 @@
1
1
  export * from './lib/feature-search.module'
2
+ export * from './lib/geocoding/geocoding.service'
3
+ export * from './lib/location-search/location-search.component'
2
4
  export * from './lib/filter-geometry.token'
3
5
  export * from './lib/record-url.token'
4
6
  export * from './lib/state/actions'
@@ -3,13 +3,16 @@
3
3
  [title]="title"
4
4
  [dateRange]="(selectedDateRange$ | async) ?? {}"
5
5
  (dateRangeChange)="onDateRangeChange($event)"
6
+ [attr.data-cy-field]="fieldName"
6
7
  ></gn-ui-date-range-dropdown>
7
8
  } @else if (fieldType === 'spatialExtent') {
8
9
  <gn-ui-spatial-extent-dropdown
9
10
  [title]="title"
11
+ [initialBbox]="(selectedBoundingBox$ | async) ?? null"
10
12
  [maxFileSizeMb]="spatialExtentMaxFileSize"
11
13
  (bboxChange)="onBboxChange($event)"
12
14
  (errorChange)="onSpatialExtentError($event)"
15
+ [attr.data-cy-field]="fieldName"
13
16
  ></gn-ui-spatial-extent-dropdown>
14
17
  } @else {
15
18
  <gn-ui-dropdown-multiselect
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  ChangeDetectionStrategy,
3
3
  Component,
4
+ InjectionToken,
4
5
  Input,
5
6
  OnInit,
6
7
  inject,
@@ -27,7 +28,12 @@ import { CommonModule } from '@angular/common'
27
28
  import { BoundingBox } from '../../../../../../libs/util/shared/src'
28
29
  import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
29
30
  import { TranslateService } from '@ngx-translate/core'
30
- import { getOptionalSearchConfig } from '../../../../../../libs/util/app-config/src'
31
+
32
+ // in MB, null means no limit
33
+ export const SPATIAL_EXTENT_MAX_FILE_SIZE = new InjectionToken<number | null>(
34
+ 'spatialExtentMaxFileSize',
35
+ { factory: () => null }
36
+ )
31
37
 
32
38
  @Component({
33
39
  selector: 'gn-ui-filter-dropdown',
@@ -52,8 +58,7 @@ export class FilterDropdownComponent implements OnInit {
52
58
  @Input() fieldName: string
53
59
  @Input() title: string
54
60
 
55
- spatialExtentMaxFileSize =
56
- getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE
61
+ spatialExtentMaxFileSize = inject(SPATIAL_EXTENT_MAX_FILE_SIZE)
57
62
 
58
63
  fieldType: FieldType
59
64
  choices$: Observable<Choice[]>
@@ -71,6 +76,14 @@ export class FilterDropdownComponent implements OnInit {
71
76
  map((selected) => (Array.isArray(selected) ? {} : (selected as DateRange)))
72
77
  ) as Observable<DateRange>
73
78
 
79
+ selectedBoundingBox$ = this.selected$.pipe(
80
+ map((selected) =>
81
+ Array.isArray(selected) && selected.length > 0
82
+ ? (selected as BoundingBox)
83
+ : null
84
+ )
85
+ ) as Observable<BoundingBox | null>
86
+
74
87
  onSelectedValues(values: unknown[]) {
75
88
  this.fieldsService
76
89
  .buildFiltersFromFieldValues({ [this.fieldName]: values as FieldValue[] })
@@ -80,7 +93,11 @@ export class FilterDropdownComponent implements OnInit {
80
93
  private spatialExtentErrorNotificationId: number | null = null
81
94
 
82
95
  onBboxChange(bbox: BoundingBox | null) {
83
- console.log(bbox)
96
+ this.fieldsService
97
+ .buildFiltersFromFieldValues({
98
+ [this.fieldName]: bbox,
99
+ })
100
+ .subscribe((filters) => this.searchService.updateFilters(filters))
84
101
  this.clearSpatialExtentErrorNotification()
85
102
  }
86
103
 
@@ -5,22 +5,31 @@ 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
- 'geocoding-provider'
28
+ 'geocoding-provider',
29
+ {
30
+ providedIn: 'root',
31
+ factory: (): GeocodingProvider => DEFAULT_GEOCODING_PROVIDER,
32
+ }
24
33
  )
25
34
 
26
35
  @Injectable({
@@ -42,9 +51,9 @@ export class GeocodingService {
42
51
  queryGeonames(text, this.provider[1] as GeonamesOptions)
43
52
  )
44
53
  break
45
- case 'data-gouv-fr':
54
+ case 'geoplateforme':
46
55
  queryObservable = from(
47
- queryDataGouvFr(text, this.provider[1] as DataGouvFrOptions)
56
+ queryGeoplateforme(text, this.provider[1] as GeoplateformeOptions)
48
57
  )
49
58
  break
50
59
  default:
@@ -0,0 +1,8 @@
1
+ <gn-ui-autocomplete
2
+ [action]="searchAction"
3
+ [displayWithFn]="displayWithFn"
4
+ [placeholder]="placeholder"
5
+ [displayWithTemplate]="displayWithTemplate"
6
+ (itemSelected)="handleItemSelected($event)"
7
+ >
8
+ </gn-ui-autocomplete>
@@ -0,0 +1,40 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ EventEmitter,
5
+ inject,
6
+ Input,
7
+ Output,
8
+ TemplateRef,
9
+ } from '@angular/core'
10
+ import { Observable } from 'rxjs'
11
+ import { GeocodingResult } from '@geospatial-sdk/geocoding'
12
+ import {
13
+ AutocompleteComponent,
14
+ AutocompleteItem,
15
+ } from '../../../../../../libs/ui/inputs/src'
16
+ import { GeocodingService } from '../geocoding/geocoding.service'
17
+
18
+ @Component({
19
+ selector: 'gn-ui-location-search',
20
+ templateUrl: './location-search.component.html',
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ standalone: true,
23
+ imports: [AutocompleteComponent],
24
+ })
25
+ export class LocationSearchComponent {
26
+ private geocodingService = inject(GeocodingService)
27
+
28
+ @Input() placeholder = ''
29
+ @Input() displayWithTemplate?: TemplateRef<{ $implicit: GeocodingResult }>
30
+ @Output() resultSelected = new EventEmitter<GeocodingResult>()
31
+
32
+ displayWithFn = (item: AutocompleteItem) => (item as GeocodingResult).label
33
+
34
+ searchAction = (text: string): Observable<AutocompleteItem[]> =>
35
+ this.geocodingService.query(text)
36
+
37
+ handleItemSelected(item: AutocompleteItem) {
38
+ this.resultSelected.emit(item as GeocodingResult)
39
+ }
40
+ }
@@ -20,6 +20,8 @@ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
20
20
 
21
21
  marker('search.filters.summaryLabel.user')
22
22
  marker('search.filters.summaryLabel.changeDate')
23
+ marker('search.filters.summaryLabel.resourceCreationRevisionDate')
24
+ marker('search.filters.summaryLabel.temporalExtent')
23
25
 
24
26
  const OPEN_BOUND = '…'
25
27
 
@@ -8,7 +8,6 @@ import {
8
8
  SortByField,
9
9
  } from '../../../../../../libs/common/domain/src/lib/model/search'
10
10
  import { DEFAULT_PAGE_SIZE, FIELDS_SUMMARY } from '../constants'
11
- import { getOptionalSearchConfig } from '../../../../../../libs/util/app-config/src'
12
11
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
13
12
 
14
13
  export const SEARCH_FEATURE_KEY = 'searchState'
@@ -56,9 +55,7 @@ export const initSearch = (): SearchStateSearch => {
56
55
  },
57
56
  params: {
58
57
  filters: {},
59
- pageSize: getOptionalSearchConfig()?.LIMIT
60
- ? getOptionalSearchConfig().LIMIT
61
- : DEFAULT_PAGE_SIZE,
58
+ pageSize: DEFAULT_PAGE_SIZE,
62
59
  currentPage: 0,
63
60
  favoritesOnly: false,
64
61
  useSpatialFilter: true,
@@ -2,6 +2,7 @@ import { Injectable, Injector, inject } from '@angular/core'
2
2
  import {
3
3
  AbstractSearchField,
4
4
  AvailableServicesField,
5
+ BoundingBoxSearchField,
5
6
  DateRangeSearchField,
6
7
  FieldValue,
7
8
  FullTextSearchField,
@@ -10,12 +11,12 @@ import {
10
11
  MultilingualSearchField,
11
12
  OrganizationSearchField,
12
13
  OwnerSearchField,
14
+ ResourceCreationRevisionDateSearchField,
13
15
  ResourceTypeLegacyField,
14
16
  SimpleSearchField,
15
17
  TranslatedSearchField,
16
18
  RecordKindField,
17
19
  UserSearchField,
18
- SpatialExtentSearchField,
19
20
  } from './fields'
20
21
  import { forkJoin, Observable, of } from 'rxjs'
21
22
  import { map } from 'rxjs/operators'
@@ -43,6 +44,8 @@ marker('search.filters.producerOrg')
43
44
  marker('search.filters.publisherOrg')
44
45
  marker('search.filters.user')
45
46
  marker('search.filters.changeDate')
47
+ marker('search.filters.resourceCreationRevisionDate')
48
+ marker('search.filters.temporalExtent')
46
49
  marker('search.filters.spatialExtent')
47
50
  @Injectable({
48
51
  providedIn: 'root',
@@ -95,8 +98,17 @@ export class FieldsService {
95
98
  ),
96
99
  user: new UserSearchField(this.injector),
97
100
  changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
101
+ resourceCreationRevisionDate: new ResourceCreationRevisionDateSearchField(
102
+ this.injector,
103
+ 'desc'
104
+ ),
105
+ temporalExtent: new DateRangeSearchField(
106
+ 'resourceTemporalExtentDateRange',
107
+ this.injector,
108
+ 'desc'
109
+ ),
98
110
  availableServices: new AvailableServicesField(this.injector),
99
- spatialExtent: new SpatialExtentSearchField(this.injector),
111
+ spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
100
112
  } as Record<string, AbstractSearchField>
101
113
 
102
114
  get supportedFields() {