geonetwork-ui 2.7.0-dev.8ac6cd7aa → 2.7.0-dev.9895bd35c

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 (178) hide show
  1. package/esm2022/libs/api/metadata-converter/src/lib/dcat-ap/read-parts.mjs +4 -7
  2. package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.mjs +20 -20
  3. package/esm2022/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.mjs +3 -3
  4. package/esm2022/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.mjs +3 -3
  5. package/esm2022/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.mjs +3 -3
  6. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +4 -4
  7. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/utils/status.mapper.mjs +4 -1
  8. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +8 -4
  9. package/esm2022/libs/api/repository/src/index.mjs +2 -2
  10. package/esm2022/libs/api/repository/src/lib/gn4/auth/auth.service.mjs +5 -5
  11. package/esm2022/libs/api/repository/src/lib/gn4/elasticsearch/constant.mjs +4 -6
  12. package/esm2022/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.mjs +55 -12
  13. package/esm2022/libs/api/repository/src/lib/gn4/gn4-repository.mjs +3 -3
  14. package/esm2022/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.mjs +3 -3
  15. package/esm2022/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.mjs +3 -3
  16. package/esm2022/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.mjs +20 -6
  17. package/esm2022/libs/api/repository/src/lib/metadata-language.token.mjs +3 -0
  18. package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +15 -1
  19. package/esm2022/libs/feature/catalog/src/lib/sources/sources.service.mjs +3 -3
  20. package/esm2022/libs/feature/dataviz/src/lib/service/data.service.mjs +2 -2
  21. package/esm2022/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.mjs +2 -2
  22. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-inspire-field/form-field-inspire-theme.component.mjs +79 -0
  23. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.mjs +8 -3
  24. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/index.mjs +2 -1
  25. package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +77 -1
  26. package/esm2022/libs/feature/map/src/lib/utils/map-utils.service.mjs +3 -5
  27. package/esm2022/libs/feature/record/src/lib/map-view/map-view.component.mjs +4 -2
  28. package/esm2022/libs/feature/router/src/lib/default/state/router.facade.mjs +3 -2
  29. package/esm2022/libs/feature/search/src/lib/state/reducer.mjs +5 -2
  30. package/esm2022/libs/ui/elements/src/lib/api-card/api-card.component.mjs +3 -3
  31. package/esm2022/libs/ui/elements/src/lib/downloads-list/downloads-list.component.mjs +5 -4
  32. package/esm2022/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.mjs +5 -4
  33. package/esm2022/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.mjs +1 -4
  34. package/esm2022/libs/ui/elements/src/lib/metadata-quality-item/metadata-quality-item.component.mjs +7 -7
  35. package/esm2022/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.mjs +5 -4
  36. package/esm2022/libs/util/app-config/src/lib/app-config.mjs +3 -1
  37. package/esm2022/libs/util/app-config/src/lib/fixtures.mjs +2 -2
  38. package/esm2022/libs/util/app-config/src/lib/model.mjs +1 -1
  39. package/esm2022/libs/util/app-config/src/lib/parse-utils.mjs +5 -4
  40. package/esm2022/libs/util/i18n/src/lib/language-codes.mjs +50 -9
  41. package/esm2022/libs/util/shared/src/lib/links/link-utils.mjs +4 -2
  42. package/esm2022/libs/util/shared/src/lib/record/quality-score.util.mjs +9 -8
  43. package/esm2022/libs/util/shared/src/lib/utils/geojson.mjs +58 -1
  44. package/esm2022/libs/util/shared/src/lib/utils/index.mjs +2 -1
  45. package/esm2022/libs/util/shared/src/lib/utils/mobile-screen.mjs +9 -0
  46. package/esm2022/translations/de.json +41 -7
  47. package/esm2022/translations/en.json +40 -6
  48. package/esm2022/translations/es.json +40 -6
  49. package/esm2022/translations/fr.json +41 -7
  50. package/esm2022/translations/it.json +40 -6
  51. package/esm2022/translations/nl.json +40 -6
  52. package/esm2022/translations/pt.json +40 -6
  53. package/esm2022/translations/sk.json +40 -6
  54. package/fesm2022/geonetwork-ui.mjs +3005 -2836
  55. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  56. package/libs/api/metadata-converter/src/lib/dcat-ap/read-parts.d.ts.map +1 -1
  57. package/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.d.ts +1 -1
  58. package/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.d.ts.map +1 -1
  59. package/libs/api/metadata-converter/src/lib/iso19139/utils/status.mapper.d.ts.map +1 -1
  60. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
  61. package/libs/api/repository/src/index.d.ts +1 -1
  62. package/libs/api/repository/src/index.d.ts.map +1 -1
  63. package/libs/api/repository/src/lib/gn4/elasticsearch/constant.d.ts.map +1 -1
  64. package/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.d.ts +1 -2
  65. package/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.d.ts.map +1 -1
  66. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts +2 -0
  67. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts.map +1 -1
  68. package/libs/api/repository/src/lib/metadata-language.token.d.ts +5 -0
  69. package/libs/api/repository/src/lib/metadata-language.token.d.ts.map +1 -0
  70. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +4 -0
  71. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  72. package/libs/feature/catalog/src/lib/sources/sources.service.d.ts.map +1 -1
  73. package/libs/feature/dataviz/src/lib/service/data.service.d.ts.map +1 -1
  74. package/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.d.ts.map +1 -1
  75. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-inspire-field/form-field-inspire-theme.component.d.ts +28 -0
  76. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-inspire-field/form-field-inspire-theme.component.d.ts.map +1 -0
  77. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts +1 -0
  78. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts.map +1 -1
  79. package/libs/feature/editor/src/lib/components/record-form/form-field/index.d.ts +1 -0
  80. package/libs/feature/editor/src/lib/components/record-form/form-field/index.d.ts.map +1 -1
  81. package/libs/feature/editor/src/lib/fields.config.d.ts +8 -1
  82. package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
  83. package/libs/feature/map/src/lib/utils/map-utils.service.d.ts +2 -2
  84. package/libs/feature/map/src/lib/utils/map-utils.service.d.ts.map +1 -1
  85. package/libs/feature/record/src/lib/map-view/map-view.component.d.ts.map +1 -1
  86. package/libs/feature/router/src/lib/default/state/router.facade.d.ts.map +1 -1
  87. package/libs/feature/search/src/lib/state/reducer.d.ts.map +1 -1
  88. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts +1 -0
  89. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts.map +1 -1
  90. package/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.d.ts +1 -0
  91. package/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.d.ts.map +1 -1
  92. package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts +0 -1
  93. package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts.map +1 -1
  94. package/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.d.ts +1 -0
  95. package/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.d.ts.map +1 -1
  96. package/libs/util/app-config/src/lib/app-config.d.ts.map +1 -1
  97. package/libs/util/app-config/src/lib/model.d.ts +1 -0
  98. package/libs/util/app-config/src/lib/model.d.ts.map +1 -1
  99. package/libs/util/app-config/src/lib/parse-utils.d.ts.map +1 -1
  100. package/libs/util/i18n/src/lib/language-codes.d.ts +91 -31
  101. package/libs/util/i18n/src/lib/language-codes.d.ts.map +1 -1
  102. package/libs/util/shared/src/lib/links/link-utils.d.ts +1 -1
  103. package/libs/util/shared/src/lib/links/link-utils.d.ts.map +1 -1
  104. package/libs/util/shared/src/lib/record/quality-score.util.d.ts.map +1 -1
  105. package/libs/util/shared/src/lib/utils/geojson.d.ts +7 -2
  106. package/libs/util/shared/src/lib/utils/geojson.d.ts.map +1 -1
  107. package/libs/util/shared/src/lib/utils/index.d.ts +1 -0
  108. package/libs/util/shared/src/lib/utils/index.d.ts.map +1 -1
  109. package/libs/util/shared/src/lib/utils/mobile-screen.d.ts +2 -0
  110. package/libs/util/shared/src/lib/utils/mobile-screen.d.ts.map +1 -0
  111. package/package.json +11 -10
  112. package/src/libs/api/metadata-converter/src/lib/dcat-ap/read-parts.ts +3 -6
  113. package/src/libs/api/metadata-converter/src/lib/dcat-ap/utils/status.mapper.ts +3 -0
  114. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +47 -22
  115. package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
  116. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +2 -2
  117. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +2 -2
  118. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +3 -3
  119. package/src/libs/api/metadata-converter/src/lib/iso19139/utils/status.mapper.ts +3 -0
  120. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +7 -3
  121. package/src/libs/api/repository/src/index.ts +1 -1
  122. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +4 -4
  123. package/src/libs/api/repository/src/lib/gn4/elasticsearch/constant.ts +3 -5
  124. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +65 -14
  125. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +2 -2
  126. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +2 -2
  127. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +2 -2
  128. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +21 -8
  129. package/src/libs/api/repository/src/lib/metadata-language.token.ts +8 -0
  130. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +20 -0
  131. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +2 -5
  132. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +3 -1
  133. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +1 -4
  134. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-inspire-field/form-field-inspire-theme.component.css +0 -0
  135. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-inspire-field/form-field-inspire-theme.component.html +20 -0
  136. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-inspire-field/form-field-inspire-theme.component.ts +85 -0
  137. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +6 -0
  138. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +5 -0
  139. package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
  140. package/src/libs/feature/editor/src/lib/fields.config.ts +85 -1
  141. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +8 -8
  142. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +3 -1
  143. package/src/libs/feature/router/src/lib/default/state/router.facade.ts +2 -1
  144. package/src/libs/feature/search/src/lib/state/reducer.ts +4 -1
  145. package/src/libs/ui/elements/src/lib/api-card/api-card.component.html +4 -1
  146. package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.html +12 -11
  147. package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.ts +7 -1
  148. package/src/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.html +4 -1
  149. package/src/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.ts +7 -1
  150. package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts +0 -4
  151. package/src/libs/ui/elements/src/lib/metadata-quality-item/metadata-quality-item.component.ts +6 -6
  152. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.html +7 -1
  153. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +3 -1
  154. package/src/libs/util/app-config/src/lib/app-config.ts +2 -0
  155. package/src/libs/util/app-config/src/lib/fixtures.ts +1 -1
  156. package/src/libs/util/app-config/src/lib/model.ts +1 -0
  157. package/src/libs/util/app-config/src/lib/parse-utils.ts +4 -7
  158. package/src/libs/util/i18n/src/lib/language-codes.ts +62 -11
  159. package/src/libs/util/shared/src/lib/links/link-utils.ts +2 -1
  160. package/src/libs/util/shared/src/lib/record/quality-score.util.ts +13 -10
  161. package/src/libs/util/shared/src/lib/utils/geojson.ts +72 -2
  162. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  163. package/src/libs/util/shared/src/lib/utils/mobile-screen.ts +14 -0
  164. package/translations/de.json +41 -7
  165. package/translations/en.json +40 -6
  166. package/translations/es.json +40 -6
  167. package/translations/fr.json +41 -7
  168. package/translations/it.json +40 -6
  169. package/translations/nl.json +40 -6
  170. package/translations/pt.json +40 -6
  171. package/translations/sk.json +40 -6
  172. package/esm2022/libs/api/repository/src/lib/metadata-language.mjs +0 -3
  173. package/esm2022/libs/util/app-config/src/lib/constants.mjs +0 -439
  174. package/libs/api/repository/src/lib/metadata-language.d.ts +0 -3
  175. package/libs/api/repository/src/lib/metadata-language.d.ts.map +0 -1
  176. package/libs/util/app-config/src/lib/constants.d.ts +0 -2
  177. package/libs/util/app-config/src/lib/constants.d.ts.map +0 -1
  178. package/src/libs/api/repository/src/lib/metadata-language.ts +0 -3
@@ -10,14 +10,12 @@ export const ES_SOURCE_SUMMARY = [
10
10
  'codelist_status_text',
11
11
  'link',
12
12
  'linkProtocol',
13
- 'contactForResource.organisation',
14
- 'contact.organisation',
15
- 'contact.email',
13
+ 'contactForResource*.organisation*',
14
+ 'contact*.organisation*',
15
+ 'contact*.email',
16
16
  'userSavedCount',
17
- 'updateFrequency',
18
17
  'cl_topic',
19
18
  'cl_maintenanceAndUpdateFrequency',
20
- 'tag',
21
19
  'MD_LegalConstraints*Object',
22
20
  'qualityScore',
23
21
  'allKeywords',
@@ -1,5 +1,5 @@
1
1
  import { Injectable, Injector } from '@angular/core'
2
- import { Geometry } from 'geojson'
2
+ import type { Geometry } from 'geojson'
3
3
  import {
4
4
  ES_QUERY_FIELDS_PRIORITY,
5
5
  ES_SOURCE_SUMMARY,
@@ -15,7 +15,7 @@ import {
15
15
  FiltersAggregationParams,
16
16
  SortByField,
17
17
  } from '../../../../../../../libs/common/domain/src/lib/model/search'
18
- import { METADATA_LANGUAGE } from '../../metadata-language'
18
+ import { METADATA_LANGUAGE } from '../../metadata-language.token'
19
19
  import {
20
20
  AggregationResult,
21
21
  EsSearchParams,
@@ -26,10 +26,16 @@ import {
26
26
  SortParams,
27
27
  TermsAggregationResult,
28
28
  } from '../../../../../../../libs/api/metadata-converter/src'
29
- import { getLang3FromLang2 } from '../../../../../../../libs/util/i18n/src'
29
+ import { toLang3 } from '../../../../../../../libs/util/i18n/src'
30
30
  import { formatDate, isDateRange } from './date-range.utils'
31
- import { CatalogRecord } from '../../../../../../../libs/common/domain/src/lib/model/record'
31
+ import {
32
+ CatalogRecord,
33
+ LanguageCode,
34
+ } from '../../../../../../../libs/common/domain/src/lib/model/record'
32
35
  import { TranslateService } from '@ngx-translate/core'
36
+ import { getGeometryBoundingBox } from '../../../../../../../libs/util/shared/src'
37
+ import { getLength as getGeodesicLength } from 'ol/sphere'
38
+ import { LineString } from 'ol/geom'
33
39
 
34
40
  export type DateRange = { start?: Date; end?: Date }
35
41
 
@@ -42,11 +48,9 @@ export class ElasticsearchService {
42
48
  private runtimeFields: Record<string, string> = {}
43
49
 
44
50
  // we're using getters in case the defined languages change over time
45
- private get lang3() {
46
- return getLang3FromLang2(this.translateService.currentLang)
47
- }
48
- private get metadataLang() {
49
- return this.injector.get(METADATA_LANGUAGE, null)
51
+ private get metadataLang(): LanguageCode {
52
+ const mdLangValue = this.injector.get(METADATA_LANGUAGE, null)
53
+ return typeof mdLangValue === 'function' ? mdLangValue() : mdLangValue
50
54
  }
51
55
 
52
56
  constructor(
@@ -230,9 +234,12 @@ export class ElasticsearchService {
230
234
 
231
235
  private getQueryLang(): string {
232
236
  if (this.metadataLang) {
233
- return this.isCurrentSearchLang()
234
- ? `lang${this.lang3}`
235
- : `lang${this.metadataLang}`
237
+ const lang3 = toLang3(
238
+ this.isCurrentSearchLang()
239
+ ? this.translateService.currentLang
240
+ : this.metadataLang
241
+ )
242
+ return `lang${lang3}`
236
243
  } else return '*'
237
244
  }
238
245
  private isCurrentSearchLang() {
@@ -351,6 +358,11 @@ export class ElasticsearchService {
351
358
  })
352
359
  }
353
360
  if (geometry) {
361
+ // boosts applied using the filter geometry:
362
+ // * records completely within the geometry receive a boost of 5
363
+ // * records intersecting the geometry receive a boost of 2
364
+ // * records close to the geometry center receive a boost of 5 (based on the `location` field)
365
+ // * records on the outskirt of the geometry receive a boost of 2.5
354
366
  should.push(
355
367
  {
356
368
  geo_shape: {
@@ -358,7 +370,7 @@ export class ElasticsearchService {
358
370
  shape: geometry,
359
371
  relation: 'within',
360
372
  },
361
- boost: 10.0,
373
+ boost: 5.0,
362
374
  },
363
375
  },
364
376
  {
@@ -367,10 +379,49 @@ export class ElasticsearchService {
367
379
  shape: geometry,
368
380
  relation: 'intersects',
369
381
  },
370
- boost: 7.0,
382
+ boost: 2.0,
371
383
  },
372
384
  }
373
385
  )
386
+
387
+ // this will boost the results variably depending on their distance from the given geometry
388
+ // note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
389
+ // combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
390
+ const bbox = getGeometryBoundingBox(geometry)
391
+ const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2]
392
+ const northToCenter = new LineString([
393
+ [center[0], bbox[3]],
394
+ center,
395
+ ]).transform('EPSG:4326', 'EPSG:3857')
396
+ const southToCenter = new LineString([
397
+ [center[0], bbox[1]],
398
+ center,
399
+ ]).transform('EPSG:4326', 'EPSG:3857')
400
+ const westToCenter = new LineString([
401
+ [bbox[0], center[1]],
402
+ center,
403
+ ]).transform('EPSG:4326', 'EPSG:3857')
404
+ const eastToCenter = new LineString([
405
+ [bbox[2], center[1]],
406
+ center,
407
+ ]).transform('EPSG:4326', 'EPSG:3857')
408
+ // cutoff distance is the distance from where the boost will only be half of the max value
409
+ // it is an average of the "size" of the bounding box in every direction, in meters
410
+ const cutoffDistance =
411
+ (getGeodesicLength(northToCenter) +
412
+ getGeodesicLength(southToCenter) +
413
+ getGeodesicLength(westToCenter) +
414
+ getGeodesicLength(eastToCenter)) /
415
+ 4
416
+
417
+ should.push({
418
+ distance_feature: {
419
+ field: 'location',
420
+ pivot: `${Math.round(cutoffDistance).toFixed(0)}m`,
421
+ origin: center,
422
+ boost: 5.0,
423
+ },
424
+ })
374
425
  }
375
426
 
376
427
  return {
@@ -48,7 +48,7 @@ import {
48
48
  import { catchError, map, tap } from 'rxjs/operators'
49
49
  import { lt } from 'semver'
50
50
  import { ElasticsearchService } from './elasticsearch'
51
- import { getLang2FromLang3 } from '../../../../../../libs/util/i18n/src'
51
+ import { toLang2 } from '../../../../../../libs/util/i18n/src'
52
52
  import { Gn4SettingsService } from './settings/gn4-settings.service'
53
53
 
54
54
  const minPublicationApiVersion = '4.2.5'
@@ -562,7 +562,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
562
562
  .pipe(
563
563
  map((languages) =>
564
564
  languages
565
- .map((lang) => getLang2FromLang3(lang.id))
565
+ .map((lang) => toLang2(lang.id))
566
566
  .filter((code): code is string => !!code)
567
567
  )
568
568
  )
@@ -7,7 +7,7 @@ import {
7
7
  import { forkJoin, Observable, of } from 'rxjs'
8
8
  import { map, shareReplay } from 'rxjs/operators'
9
9
  import { TranslateService } from '@ngx-translate/core'
10
- import { getLang3FromLang2 } from '../../../../../../../libs/util/i18n/src'
10
+ import { toLang3 } from '../../../../../../../libs/util/i18n/src'
11
11
  import { FieldFilters } from '../../../../../../../libs/common/domain/src/lib/model/search'
12
12
  import {
13
13
  CatalogRecord,
@@ -52,7 +52,7 @@ export class OrganizationsFromGroupsService
52
52
  organisationsCount$ = this.organisations$.pipe(map((orgs) => orgs.length))
53
53
 
54
54
  private get lang3() {
55
- return getLang3FromLang2(this.translateService.currentLang)
55
+ return toLang3(this.translateService.currentLang)
56
56
  }
57
57
 
58
58
  constructor(
@@ -33,7 +33,7 @@ import {
33
33
  tap,
34
34
  withLatestFrom,
35
35
  } from 'rxjs/operators'
36
- import { getLocalizedIndexKey } from '../../../../../../../libs/util/i18n/src'
36
+ import { toLang3 } from '../../../../../../../libs/util/i18n/src'
37
37
  import { PlatformServiceInterface } from '../../../../../../../libs/common/domain/src/lib/platform.service.interface'
38
38
  import { coerce, satisfies, valid } from 'semver'
39
39
  import { TranslateService } from '@ngx-translate/core'
@@ -126,7 +126,7 @@ export class OrganizationsFromMetadataService
126
126
  ) {}
127
127
 
128
128
  private get langIndex() {
129
- return getLocalizedIndexKey(this.translateService.currentLang)
129
+ return `lang${toLang3(this.translateService.currentLang)}`
130
130
  }
131
131
 
132
132
  equalsNormalizedStrings(
@@ -45,8 +45,7 @@ import {
45
45
  throwError,
46
46
  } from 'rxjs'
47
47
  import { TranslateService } from '@ngx-translate/core'
48
- import { getLang3FromLang2 } from '../../../../../../../libs/util/i18n/src'
49
- import { DatavizConfigModel } from '../../../../../../../libs/common/domain/src/lib/model/dataviz/dataviz-configuration.model'
48
+ import { toLang3 } from '../../../../../../../libs/util/i18n/src'
50
49
 
51
50
  const minApiVersion = '4.2.2'
52
51
 
@@ -56,6 +55,7 @@ export class Gn4PlatformService implements PlatformServiceInterface {
56
55
  private readonly me$: Observable<UserModel>
57
56
  private readonly users$: Observable<UserModel[]>
58
57
  private readonly isUserAnonymous$: Observable<boolean>
58
+ private readonly gnParseVersion = '4.2.5'
59
59
 
60
60
  private keyTranslations$ = this.toolsApiService
61
61
  .getTranslationsPackage1('gnui')
@@ -98,7 +98,7 @@ export class Gn4PlatformService implements PlatformServiceInterface {
98
98
  private keywordsByThesauri: Record<string, Observable<Keyword[]>> = {}
99
99
 
100
100
  private get lang3() {
101
- return getLang3FromLang2(this.translateService.currentLang)
101
+ return toLang3(this.translateService.currentLang)
102
102
  }
103
103
 
104
104
  constructor(
@@ -390,22 +390,35 @@ export class Gn4PlatformService implements PlatformServiceInterface {
390
390
  })
391
391
  )
392
392
  }
393
-
394
393
  getFileContent(url: URL | string): Observable<any> {
395
- return this.httpClient.get(url.toString(), { responseType: 'text' }).pipe(
396
- map((text) => {
394
+ return combineLatest([
395
+ this.httpClient.get(url.toString(), { responseType: 'text' }),
396
+ this.getApiVersion(),
397
+ ]).pipe(
398
+ map(([text, version]) => {
397
399
  const parsed = JSON.parse(text)
398
400
 
399
- if (typeof parsed === 'object') {
401
+ if (version > this.gnParseVersion) {
400
402
  return parsed
401
403
  }
402
404
 
403
- const decoded = atob(parsed)
405
+ const decoded = this.decodeBase64(parsed)
404
406
  return JSON.parse(decoded)
405
407
  })
406
408
  )
407
409
  }
408
410
 
411
+ decodeBase64(base64) {
412
+ const text = atob(base64)
413
+ const length = text.length
414
+ const bytes = new Uint8Array(length)
415
+ for (let i = 0; i < length; i++) {
416
+ bytes[i] = text.charCodeAt(i)
417
+ }
418
+ const decoder = new TextDecoder()
419
+ return decoder.decode(bytes)
420
+ }
421
+
409
422
  attachFileToRecord(
410
423
  recordUuid: string,
411
424
  file: File,
@@ -0,0 +1,8 @@
1
+ import { InjectionToken } from '@angular/core'
2
+ import { LanguageCode } from '../../../../../libs/common/domain/src/lib/model/record'
3
+
4
+ export type LanguageCodeFactory = () => LanguageCode
5
+
6
+ export const METADATA_LANGUAGE = new InjectionToken<
7
+ LanguageCode | LanguageCodeFactory
8
+ >('metadata-language')
@@ -69,6 +69,8 @@ marker('domain.record.status.ongoing')
69
69
  marker('domain.record.status.under_development')
70
70
  marker('domain.record.status.deprecated')
71
71
  marker('domain.record.status.removed')
72
+ marker('domain.record.status.planned')
73
+ marker('domain.record.status.required')
72
74
 
73
75
  export const RecordStatusValues = [
74
76
  'completed',
@@ -76,6 +78,8 @@ export const RecordStatusValues = [
76
78
  'under_development',
77
79
  'deprecated',
78
80
  'removed',
81
+ 'planned',
82
+ 'required',
79
83
  ]
80
84
  export type RecordStatus = (typeof RecordStatusValues)[number]
81
85
 
@@ -104,6 +108,11 @@ export interface Keyword {
104
108
  translations?: KeywordTranslations
105
109
  }
106
110
 
111
+ export interface INSPIRE_theme {
112
+ value: string
113
+ label: string
114
+ }
115
+
107
116
  export interface BaseRecord {
108
117
  uniqueIdentifier: Uuid
109
118
  ownerOrganization: Organization
@@ -156,6 +165,17 @@ export type ServiceProtocol =
156
165
  | 'maplibre-style'
157
166
  | 'other'
158
167
 
168
+ marker('record.metadata.api.accessServiceProtocol.wms')
169
+ marker('record.metadata.api.accessServiceProtocol.wfs')
170
+ marker('record.metadata.api.accessServiceProtocol.wps')
171
+ marker('record.metadata.api.accessServiceProtocol.wmts')
172
+ marker('record.metadata.api.accessServiceProtocol.esriRest')
173
+ marker('record.metadata.api.accessServiceProtocol.ogcFeatures')
174
+ marker('record.metadata.api.accessServiceProtocol.GPFDL')
175
+ marker('record.metadata.api.accessServiceProtocol.tms')
176
+ marker('record.metadata.api.accessServiceProtocol.maplibre-style')
177
+ marker('record.metadata.api.accessServiceProtocol.other')
178
+
159
179
  export type OnlineResourceType = 'service' | 'download' | 'link' | 'endpoint'
160
180
 
161
181
  export interface DatasetServiceDistribution {
@@ -4,7 +4,7 @@ import { Observable } from 'rxjs'
4
4
  import { filter, map, shareReplay } from 'rxjs/operators'
5
5
  import { CatalogSource } from './sources.model'
6
6
  import { TranslateService } from '@ngx-translate/core'
7
- import { getLang3FromLang2 } from '../../../../../../libs/util/i18n/src'
7
+ import { toLang3 } from '../../../../../../libs/util/i18n/src'
8
8
 
9
9
  @Injectable({
10
10
  providedIn: 'root',
@@ -23,10 +23,7 @@ export class SourcesService {
23
23
  return this.sources$.pipe(
24
24
  map((sources) => sources.filter((source) => source.uuid === uuid)[0]),
25
25
  filter((source) => !!source),
26
- map(
27
- (source) =>
28
- source.label[getLang3FromLang2(this.translateService.currentLang)]
29
- )
26
+ map((source) => source.label[toLang3(this.translateService.currentLang)])
30
27
  )
31
28
  }
32
29
  }
@@ -237,7 +237,9 @@ export class DataService {
237
237
  tmsLink: DatasetServiceDistribution,
238
238
  keepOriginalLink = false
239
239
  ): Promise<DatasetServiceDistribution[]> {
240
- const endpoint = new TmsEndpoint(tmsLink.url.toString())
240
+ const endpoint = new TmsEndpoint(
241
+ tmsLink.url.toString().replace(/\/?$/, `/${tmsLink.name}`)
242
+ )
241
243
  const tileMaps = await endpoint.allTileMaps.catch(() => {
242
244
  throw new Error(`ogc.unreachable.unknown`)
243
245
  })
@@ -55,10 +55,7 @@ export class MetadataQualityPanelComponent implements OnChanges {
55
55
  const fieldsByPage = this.editorConfig.pages.map((page) =>
56
56
  page.sections.flatMap((section) =>
57
57
  section.fields
58
- .filter(
59
- (field) =>
60
- this.propsToValidate.includes(field.model) && !field.hidden
61
- )
58
+ .filter((field) => this.propsToValidate.includes(field.model))
62
59
  .map((field) => field.model as ValidatorMapperKeys)
63
60
  )
64
61
  )
@@ -0,0 +1,20 @@
1
+ <div class="flex flex-col gap-3">
2
+ <div class="flex flex-col gap-4">
3
+ <gn-ui-autocomplete
4
+ [placeholder]="'editor.record.form.inspire.placeholder' | translate"
5
+ [displayWithFn]="displayWithFn"
6
+ [action]="autoCompleteAction"
7
+ (itemSelected)="handleItemSelection($event)"
8
+ [minCharacterCount]="1"
9
+ [clearOnSelection]="true"
10
+ ></gn-ui-autocomplete>
11
+ <div class="flex gap-2 flex-wrap">
12
+ <gn-ui-badge
13
+ *ngFor="let theme of themes"
14
+ [removable]="true"
15
+ (badgeRemoveClicked)="removeTheme(theme)"
16
+ >{{ getTranslatedTheme(theme) }}
17
+ </gn-ui-badge>
18
+ </div>
19
+ </div>
20
+ </div>
@@ -0,0 +1,85 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core'
2
+ import { CommonModule } from '@angular/common'
3
+ import { AutocompleteComponent, BadgeComponent } from '../../../../../../../../../libs/ui/inputs/src'
4
+ import { NgIconComponent } from '@ng-icons/core'
5
+ import { INSPIRE_THEMES } from '../../../../fields.config'
6
+ import { TranslatePipe, TranslateService } from '@ngx-translate/core'
7
+ import { of } from 'rxjs'
8
+
9
+ type AutocompleteItem = { title: string; value: string }
10
+
11
+ @Component({
12
+ selector: 'gn-ui-form-field-inspire-theme',
13
+ standalone: true,
14
+ imports: [
15
+ CommonModule,
16
+ AutocompleteComponent,
17
+ NgIconComponent,
18
+ BadgeComponent,
19
+ TranslatePipe,
20
+ ],
21
+ templateUrl: './form-field-inspire-theme.component.html',
22
+ styleUrl: './form-field-inspire-theme.component.css',
23
+ })
24
+ export class FormFieldInspireThemeComponent {
25
+ themes = []
26
+ @Input() set value(themes: string[]) {
27
+ this.themes = themes
28
+ }
29
+ @Output() valueChange: EventEmitter<string[]> = new EventEmitter()
30
+ availableThemes = INSPIRE_THEMES
31
+
32
+ displayWithFn = (item: AutocompleteItem) => {
33
+ return item?.title ? `${item.title}` : ''
34
+ }
35
+
36
+ autoCompleteAction = (query: string) => {
37
+ return of(
38
+ this.availableThemes
39
+ .filter((theme) =>
40
+ theme.value.toLowerCase().includes(query.toLowerCase())
41
+ )
42
+ .sort((a, b) => {
43
+ const aStarts = a.value.startsWith(query)
44
+ const bStarts = b.value.startsWith(query)
45
+
46
+ if (aStarts && !bStarts) return -1
47
+ if (!aStarts && bStarts) return 1
48
+ return 0
49
+ })
50
+ .map((theme) => {
51
+ return {
52
+ title: this.translateService.instant(theme.label),
53
+ value: theme.value,
54
+ }
55
+ })
56
+ )
57
+ }
58
+
59
+ constructor(private translateService: TranslateService) {}
60
+
61
+ handleItemSelection(item: AutocompleteItem) {
62
+ this.addTheme(item.value)
63
+ }
64
+
65
+ addTheme(theme: string) {
66
+ const duplicatedTheme = this.themes.find((t) => t === theme)
67
+ if (!duplicatedTheme) {
68
+ this.themes = [...this.themes, theme]
69
+ this.valueChange.emit(this.themes)
70
+ }
71
+ }
72
+
73
+ removeTheme(theme: string) {
74
+ console.log(this.themes)
75
+ this.themes = this.themes.filter((t) => t !== theme)
76
+ this.valueChange.emit(this.themes)
77
+ }
78
+
79
+ getTranslatedTheme(theme: string) {
80
+ const themeKey = this.availableThemes.find(
81
+ (avail) => avail.value === theme
82
+ )?.label
83
+ return themeKey ? this.translateService.instant(themeKey) : ''
84
+ }
85
+ }
@@ -109,6 +109,12 @@
109
109
  (valueChange)="valueChange.emit($event)"
110
110
  ></gn-ui-form-field-keywords>
111
111
  </ng-container>
112
+ <ng-container *ngSwitchCase="'topics'">
113
+ <gn-ui-form-field-inspire-theme
114
+ [value]="valueAsInspireTheme"
115
+ (valueChange)="valueChange.emit($event)"
116
+ ></gn-ui-form-field-inspire-theme>
117
+ </ng-container>
112
118
  <ng-container *ngSwitchCase="'licenses'">
113
119
  <gn-ui-form-field-license
114
120
  [label]="config.labelKey! | translate"
@@ -45,6 +45,7 @@ import { FormFieldConstraintsShortcutsComponent } from './form-field-constraints
45
45
  import { FormFieldConstraintsComponent } from './form-field-constraints/form-field-constraints.component'
46
46
  import { TextFieldModule } from '@angular/cdk/text-field'
47
47
  import { FormFieldSpatialToggleComponent } from './form-field-spatial-toggle/form-field-spatial-toggle.component'
48
+ import { FormFieldInspireThemeComponent } from './form-field-inspire-field/form-field-inspire-theme.component'
48
49
 
49
50
  @Component({
50
51
  selector: 'gn-ui-form-field',
@@ -73,6 +74,7 @@ import { FormFieldSpatialToggleComponent } from './form-field-spatial-toggle/for
73
74
  FormFieldConstraintsComponent,
74
75
  FormFieldConstraintsShortcutsComponent,
75
76
  FormFieldSpatialToggleComponent,
77
+ FormFieldInspireThemeComponent,
76
78
  TextFieldModule,
77
79
  ],
78
80
  })
@@ -128,6 +130,9 @@ export class FormFieldComponent {
128
130
  get valueAsKeywords() {
129
131
  return this.value as Array<Keyword>
130
132
  }
133
+ get valueAsInspireTheme() {
134
+ return this.value as Array<string>
135
+ }
131
136
  get valueAsConstraints() {
132
137
  return this.value as Array<Constraint>
133
138
  }
@@ -10,3 +10,4 @@ export * from './form-field-array/form-field-array.component'
10
10
  export * from './form-field-spatial-extent/form-field-spatial-extent.component'
11
11
  export * from './form-field.component'
12
12
  export * from './form-field-constraints-shortcuts/constraints.utils'
13
+ export * from './form-field-inspire-field/form-field-inspire-theme.component'
@@ -4,7 +4,10 @@ import {
4
4
  EditorField,
5
5
  EditorSection,
6
6
  } from './models/editor-config.model'
7
- import { Keyword } from '../../../../../libs/common/domain/src/lib/model/record'
7
+ import {
8
+ INSPIRE_theme,
9
+ Keyword,
10
+ } from '../../../../../libs/common/domain/src/lib/model/record'
8
11
 
9
12
  /**
10
13
  * This file contains the configuration of the fields that will be displayed in the editor.
@@ -64,6 +67,11 @@ export const RECORD_KEYWORDS_FIELD: EditorField = {
64
67
  formFieldConfig: {},
65
68
  }
66
69
 
70
+ export const RECORD_TOPICS_FIELD: EditorField = {
71
+ model: 'topics',
72
+ formFieldConfig: {},
73
+ }
74
+
67
75
  export const RECORD_RESOURCE_CREATED_FIELD: EditorField = {
68
76
  model: 'resourceCreated',
69
77
  formFieldConfig: {
@@ -235,6 +243,13 @@ export const CLASSIFICATION_SECTION: EditorSection = {
235
243
  fields: [RECORD_KEYWORDS_FIELD],
236
244
  }
237
245
 
246
+ export const INSPIRE_SECTION: EditorSection = {
247
+ labelKey: marker('editor.record.form.section.inspire.label'),
248
+ descriptionKey: marker('editor.record.form.section.inspire.description'),
249
+ hidden: false,
250
+ fields: [RECORD_TOPICS_FIELD],
251
+ }
252
+
238
253
  export const USE_AND_ACCESS_CONDITIONS_SECTION: EditorSection = {
239
254
  labelKey: marker('editor.record.form.section.useAndAccessConditions.label'),
240
255
  hidden: false,
@@ -274,6 +289,7 @@ export const DEFAULT_CONFIGURATION: EditorConfig = {
274
289
  sections: [
275
290
  TITLE_SECTION,
276
291
  CLASSIFICATION_SECTION,
292
+ INSPIRE_SECTION,
277
293
  ABOUT_SECTION,
278
294
  GEOGRAPHICAL_COVERAGE_SECTION,
279
295
  ],
@@ -341,3 +357,71 @@ export const SPATIAL_SCOPES: Keyword[] = [
341
357
  type: 'theme',
342
358
  },
343
359
  ]
360
+
361
+ /************************************************************
362
+ *************** INSPIRE THEMES **************
363
+ ************************************************************
364
+ */
365
+
366
+ export const INSPIRE_THEMES: INSPIRE_theme[] = [
367
+ { value: 'biota', label: 'editor.record.form.inspire.biota' },
368
+ { value: 'boundaries', label: 'editor.record.form.inspire.boundaries' },
369
+ {
370
+ value: 'climatologyMeteorologyAtmosphere',
371
+ label: 'editor.record.form.inspire.climatology',
372
+ },
373
+ { value: 'economy', label: 'editor.record.form.inspire.economy' },
374
+ { value: 'elevation', label: 'editor.record.form.inspire.elevation' },
375
+ { value: 'environment', label: 'editor.record.form.inspire.environnement' },
376
+ { value: 'farming', label: 'editor.record.form.inspire.farming' },
377
+ {
378
+ value: 'geoscientific information',
379
+ label: 'editor.record.form.inspire.geoscientific',
380
+ },
381
+ { value: 'health', label: 'editor.record.form.inspire.health' },
382
+ {
383
+ value: 'imageryBaseMapsEarthCover',
384
+ label: 'editor.record.form.inspire.imagery',
385
+ },
386
+ {
387
+ value: 'intelligenceMilitary',
388
+ label: 'editor.record.form.inspire.intelligence',
389
+ },
390
+ { value: 'Location', label: 'editor.record.form.inspire.location' },
391
+ { value: 'Oceans', label: 'editor.record.form.inspire.oceans' },
392
+ {
393
+ value: 'planningCadastre',
394
+ label: 'editor.record.form.inspire.planning',
395
+ },
396
+ { value: 'Society', label: 'editor.record.form.inspire.society' },
397
+ { value: 'Structure', label: 'editor.record.form.inspire.structure' },
398
+ {
399
+ value: 'Transportation',
400
+ label: 'editor.record.form.inspire.transportation',
401
+ },
402
+ {
403
+ value: 'utilitiesCommunication',
404
+ label: 'editor.record.form.inspire.utilities',
405
+ },
406
+ { value: 'inlandWaters', label: 'editor.record.form.inspire.waters' },
407
+ ]
408
+
409
+ marker('editor.record.form.inspire.biota')
410
+ marker('editor.record.form.inspire.boundaries')
411
+ marker('editor.record.form.inspire.climatology')
412
+ marker('editor.record.form.inspire.economy')
413
+ marker('editor.record.form.inspire.elevation')
414
+ marker('editor.record.form.inspire.environnement')
415
+ marker('editor.record.form.inspire.farming')
416
+ marker('editor.record.form.inspire.geoscientific')
417
+ marker('editor.record.form.inspire.health')
418
+ marker('editor.record.form.inspire.imagery')
419
+ marker('editor.record.form.inspire.intelligence')
420
+ marker('editor.record.form.inspire.location')
421
+ marker('editor.record.form.inspire.oceans')
422
+ marker('editor.record.form.inspire.planning')
423
+ marker('editor.record.form.inspire.society')
424
+ marker('editor.record.form.inspire.structure')
425
+ marker('editor.record.form.inspire.transportation')
426
+ marker('editor.record.form.inspire.utilities')
427
+ marker('editor.record.form.inspire.waters')