geonetwork-ui 2.10.0-dev.f143bfdfa → 2.10.0-dev.fb2893f44

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 +321 -5767
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +78 -63
  4. package/index.d.ts.map +1 -1
  5. package/material-styles.css +483 -0
  6. package/package.json +23 -33
  7. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +2 -2
  8. package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
  9. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +4 -4
  10. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
  11. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
  12. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
  13. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +2 -2
  14. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +0 -5
  15. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
  16. package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
  17. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +4 -1
  18. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +6 -7
  19. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  20. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +0 -34
  21. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
  22. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +5 -1
  23. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +35 -19
  24. package/src/libs/feature/editor/src/lib/fields.config.ts +60 -47
  25. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
  26. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
  27. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
  28. package/src/libs/ui/dataviz/src/index.ts +0 -1
  29. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
  30. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
  31. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
  32. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
  33. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
  34. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +4 -54
  35. package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
  36. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
  37. package/src/libs/util/app-config/src/lib/app-config.ts +21 -9
  38. package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
  39. package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
  40. package/src/libs/util/i18n/src/index.ts +0 -1
  41. package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
  42. package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
  43. package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
  44. package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
  45. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  46. package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
  47. package/style.css +0 -1
  48. package/tailwind.base.css +2 -0
  49. package/translations/de.json +19 -19
  50. package/translations/en.json +19 -19
  51. package/translations/es.json +19 -19
  52. package/translations/fr.json +19 -19
  53. package/translations/it.json +19 -19
  54. package/translations/nl.json +19 -19
  55. package/translations/pt.json +19 -19
  56. package/translations/sk.json +19 -19
@@ -2,9 +2,9 @@ import {
2
2
  ChangeDetectionStrategy,
3
3
  Component,
4
4
  EventEmitter,
5
+ inject,
5
6
  Input,
6
7
  Output,
7
- inject,
8
8
  } from '@angular/core'
9
9
  import {
10
10
  DateAdapter,
@@ -52,6 +52,6 @@ export class DatePickerComponent {
52
52
  @Output() dateChange = new EventEmitter<Date>()
53
53
 
54
54
  constructor() {
55
- this.dateAdapter.setLocale(this.translate.currentLang)
55
+ this.dateAdapter.setLocale(this.translate.getCurrentLang())
56
56
  }
57
57
  }
@@ -1,18 +1,11 @@
1
1
  import { ChangeDetectorRef, Component, inject, Input } from '@angular/core'
2
2
  import { CommonModule } from '@angular/common'
3
- import { Geometry } from 'geojson'
4
- import { GeoJSONFeatureCollection } from 'ol/format/GeoJSON.js'
5
- import GeoJSON from 'ol/format/GeoJSON.js'
6
- import { Polygon } from 'ol/geom.js'
7
- import {
8
- createViewFromLayer,
9
- MapContext,
10
- MapContextLayer,
11
- } from '@geospatial-sdk/core'
3
+ import { createViewFromLayer, MapContext } from '@geospatial-sdk/core'
12
4
  import { MapContainerComponent } from '../map-container/map-container.component'
13
5
  import { BehaviorSubject, from, Observable, of } from 'rxjs'
14
6
  import { map, switchMap, tap } from 'rxjs/operators'
15
7
  import { DatasetSpatialExtent } from '../../../../../../../libs/common/domain/src/lib/model/record'
8
+ import { createSpatialExtentLayer } from '../../map-utils'
16
9
 
17
10
  @Component({
18
11
  selector: 'gn-ui-spatial-extent',
@@ -30,39 +23,10 @@ export class SpatialExtentComponent {
30
23
  spatialExtents$ = new BehaviorSubject<DatasetSpatialExtent[]>([])
31
24
  mapContext$: Observable<MapContext> = this.spatialExtents$.pipe(
32
25
  switchMap((extents) => {
33
- if (extents.length === 0) {
26
+ const layer = createSpatialExtentLayer(extents)
27
+ if (!layer) {
34
28
  return of(null)
35
29
  }
36
- const featureCollection: GeoJSONFeatureCollection = {
37
- type: 'FeatureCollection',
38
- features: [],
39
- }
40
- extents.forEach((extent) => {
41
- if (extent.geometry) {
42
- featureCollection.features.push({
43
- type: 'Feature',
44
- properties: {},
45
- geometry: extent.geometry,
46
- })
47
- } else if (extent.bbox?.length >= 0) {
48
- featureCollection.features.push({
49
- type: 'Feature',
50
- properties: {},
51
- geometry: this.bboxCoordsToGeometry(extent.bbox),
52
- })
53
- }
54
- })
55
-
56
- const layer: MapContextLayer = {
57
- type: 'geojson',
58
- data: featureCollection,
59
- label: 'Spatial extents',
60
- style: {
61
- 'stroke-color': 'black',
62
- 'stroke-width': 2,
63
- 'fill-color': 'rgba(153, 153, 153, 0.3)',
64
- },
65
- }
66
30
  return from(createViewFromLayer(layer)).pipe(
67
31
  map((view) => ({ view, layers: [layer] }) as MapContext),
68
32
  tap(() => this._cdr.markForCheck())
@@ -71,18 +35,4 @@ export class SpatialExtentComponent {
71
35
  )
72
36
 
73
37
  error = ''
74
-
75
- bboxCoordsToGeometry(bbox: [number, number, number, number]): Geometry {
76
- const geometry = new Polygon([
77
- [
78
- [bbox[0], bbox[1]],
79
- [bbox[0], bbox[3]],
80
- [bbox[2], bbox[3]],
81
- [bbox[2], bbox[1]],
82
- [bbox[0], bbox[1]],
83
- ],
84
- ])
85
-
86
- return new GeoJSON().writeGeometryObject(geometry)
87
- }
88
38
  }
@@ -12,6 +12,54 @@ import {
12
12
  MouseWheelZoom,
13
13
  } from 'ol/interaction.js'
14
14
  import MapBrowserEvent from 'ol/MapBrowserEvent.js'
15
+ import type { DatasetSpatialExtent } from '../../../../../libs/common/domain/src/lib/model/record'
16
+ import type {
17
+ MapContextLayer,
18
+ MapContextLayerGeojson,
19
+ } from '@geospatial-sdk/core'
20
+ import { spatialExtentsToFeatureCollection } from '../../../../../libs/util/shared/src'
21
+
22
+ export type SpatialExtentLayerStyle = NonNullable<
23
+ MapContextLayerGeojson['style']
24
+ >
25
+
26
+ /**
27
+ * Default style for a spatial-extent layer: a solid black outline over a
28
+ * translucent grey fill.
29
+ */
30
+ export const DEFAULT_SPATIAL_EXTENT_STYLE: SpatialExtentLayerStyle = {
31
+ 'stroke-color': 'black',
32
+ 'stroke-width': 2,
33
+ 'fill-color': 'rgba(153, 153, 153, 0.3)',
34
+ }
35
+
36
+ /**
37
+ * Builds a GeoJSON map layer drawing the given spatial extents: each extent is
38
+ * rendered from its own geometry or, when only a bounding box is available,
39
+ * from a polygon derived from that box.
40
+ *
41
+ * @returns the layer, or `null` when none of the extents can be represented.
42
+ */
43
+ export function createSpatialExtentLayer(
44
+ extents: DatasetSpatialExtent[],
45
+ overrides?: {
46
+ label?: string
47
+ clickable?: boolean
48
+ style?: SpatialExtentLayerStyle
49
+ }
50
+ ): MapContextLayer | null {
51
+ const data = spatialExtentsToFeatureCollection(extents)
52
+ if (data.features.length === 0) {
53
+ return null
54
+ }
55
+ return {
56
+ type: 'geojson',
57
+ data,
58
+ label: 'Spatial extents',
59
+ style: DEFAULT_SPATIAL_EXTENT_STYLE,
60
+ ...overrides,
61
+ }
62
+ }
15
63
 
16
64
  export function prioritizePageScroll(interactions: Collection<Interaction>) {
17
65
  interactions.clear()
@@ -52,7 +52,7 @@ export class RecordPreviewFeedComponent extends RecordPreviewComponent {
52
52
  protected elementRef: ElementRef
53
53
  private translate = inject(TranslateService)
54
54
 
55
- timeFormat = new Duration(this.translate.currentLang, {})
55
+ timeFormat = new Duration(this.translate.getCurrentLang(), {})
56
56
 
57
57
  constructor() {
58
58
  const elementRef = inject(ElementRef)
@@ -18,10 +18,15 @@ import {
18
18
  SearchConfig,
19
19
  ThemeConfig,
20
20
  } from './model'
21
- import { TranslateCompiler, TranslateLoader } from '@ngx-translate/core'
21
+ import {
22
+ TranslateCompiler,
23
+ TranslateLoader,
24
+ TranslateModuleConfig,
25
+ } from '@ngx-translate/core'
22
26
  import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'
23
- import { HttpClient } from '@angular/common/http'
24
27
  import { FileWithOverridesTranslateLoader } from './i18n/file-with-overrides.translate.loader'
28
+ import { TRANSLATE_HTTP_LOADER_CONFIG } from '@ngx-translate/http-loader'
29
+ import { HttpClient } from '@angular/common/http'
25
30
 
26
31
  const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
27
32
  This error might show up in case of an invalid/malformed configuration file.
@@ -361,16 +366,23 @@ export function _reset() {
361
366
  customTranslations = null
362
367
  }
363
368
 
364
- export const TRANSLATE_WITH_OVERRIDES_CONFIG = {
369
+ export const TRANSLATE_WITH_OVERRIDES_CONFIG: TranslateModuleConfig = {
365
370
  compiler: {
366
371
  provide: TranslateCompiler,
367
372
  useClass: TranslateMessageFormatCompiler,
368
373
  },
369
- loader: {
370
- provide: TranslateLoader,
371
- useFactory: function HttpLoaderFactory(http: HttpClient) {
372
- return new FileWithOverridesTranslateLoader(http, './assets/i18n/')
374
+ loader: [
375
+ {
376
+ provide: TRANSLATE_HTTP_LOADER_CONFIG,
377
+ useValue: {
378
+ prefix: './assets/i18n/',
379
+ suffix: '.json',
380
+ },
373
381
  },
374
- deps: [HttpClient],
375
- },
382
+ {
383
+ provide: TranslateLoader,
384
+ useClass: FileWithOverridesTranslateLoader,
385
+ deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
386
+ },
387
+ ],
376
388
  }
@@ -1,4 +1,3 @@
1
- import { BaseReader } from './base'
2
1
  import { DataItem, DatasetInfo, PropertyInfo } from '../model'
3
2
  import { getJsonDataItemsProxy, jsonToGeojsonFeature } from '../utils'
4
3
  import { generateSqlQuery } from '../sql-utils'
@@ -58,7 +57,7 @@ export class BaseFileReader extends BaseCacheReader {
58
57
  this.groupedBy,
59
58
  this.aggregations
60
59
  )
61
- const result = await import('alasql').then((module) =>
60
+ const result: any[] = await import('alasql').then((module) =>
62
61
  module.default(query, [jsonItems])
63
62
  )
64
63
  return result.map(jsonToGeojsonFeature)
@@ -1,10 +1,13 @@
1
1
  import { EndpointError, WfsEndpoint, WfsVersion } from '@camptocamp/ogc-client'
2
2
  import { DataItem, DatasetInfo, FetchError, PropertyInfo } from '../model'
3
- import { fetchDataAsText } from '../utils'
3
+ import {
4
+ fetchDataAsText,
5
+ getJsonDataItemsProxy,
6
+ jsonToGeojsonFeature,
7
+ } from '../utils'
4
8
  import { GmlReader, parseGml } from './gml'
5
9
  import { GeojsonReader, parseGeojson } from './geojson'
6
10
  import { BaseCacheReader } from './base-cache'
7
- import { getJsonDataItemsProxy, jsonToGeojsonFeature } from '../utils'
8
11
  import { generateSqlQuery } from '../sql-utils'
9
12
 
10
13
  export async function getWfsEndpoint(wfsUrl: string): Promise<WfsEndpoint> {
@@ -167,7 +170,7 @@ export class WfsReader extends BaseCacheReader {
167
170
  this.groupedBy,
168
171
  this.aggregations
169
172
  )
170
- const result = await import('alasql').then((module) =>
173
+ const result: any[] = await import('alasql').then((module) =>
171
174
  module.default(query, [jsonItems])
172
175
  )
173
176
  return result.map(jsonToGeojsonFeature)
@@ -2,6 +2,5 @@ export * from './lib/i18n.providers'
2
2
  export * from './lib/i18n.constants'
3
3
  export * from './lib/i18n.interceptor'
4
4
  export * from './lib/file.translate.loader'
5
- export * from './lib/embedded.translate.loader'
6
5
  export * from './lib/translate.loader.utils'
7
6
  export * from './lib/language-codes'
@@ -1,6 +1,4 @@
1
- import { HttpClient } from '@angular/common/http'
2
1
  import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'
3
- import { FileTranslateLoader } from './file.translate.loader'
4
2
  import {
5
3
  TranslateCompiler,
6
4
  TranslateDefaultParser,
@@ -9,26 +7,34 @@ import {
9
7
  TranslateParser,
10
8
  } from '@ngx-translate/core'
11
9
  import { Injectable } from '@angular/core'
10
+ import { TRANSLATE_HTTP_LOADER_CONFIG } from '@ngx-translate/http-loader'
11
+ import { HttpClient } from '@angular/common/http'
12
+ import { FileTranslateLoader } from './file.translate.loader'
12
13
 
13
14
  export const DEFAULT_LANG = 'en'
14
15
 
15
16
  // Caution: changing this can break language selection from third parties!
16
17
  export const LANGUAGE_STORAGE_KEY = 'geonetwork-ui-language'
17
18
 
18
- export function HttpLoaderFactory(http: HttpClient) {
19
- return new FileTranslateLoader(http, './assets/i18n/')
20
- }
21
-
22
19
  export const TRANSLATE_DEFAULT_CONFIG: TranslateModuleConfig = {
23
20
  compiler: {
24
21
  provide: TranslateCompiler,
25
22
  useClass: TranslateMessageFormatCompiler,
26
23
  },
27
- loader: {
28
- provide: TranslateLoader,
29
- useFactory: HttpLoaderFactory,
30
- deps: [HttpClient],
31
- },
24
+ loader: [
25
+ {
26
+ provide: TRANSLATE_HTTP_LOADER_CONFIG,
27
+ useValue: {
28
+ prefix: './assets/i18n/',
29
+ suffix: '.json',
30
+ },
31
+ },
32
+ {
33
+ provide: TranslateLoader,
34
+ useClass: FileTranslateLoader,
35
+ deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
36
+ },
37
+ ],
32
38
  }
33
39
 
34
40
  @Injectable()
@@ -6,7 +6,7 @@ import {
6
6
  } from '@angular/common/http'
7
7
  import { DEFAULT_LANG } from './i18n.constants'
8
8
  import { Observable } from 'rxjs'
9
- import { Injectable, Injector, inject } from '@angular/core'
9
+ import { inject, Injectable, Injector } from '@angular/core'
10
10
  import { TranslateService } from '@ngx-translate/core'
11
11
 
12
12
  @Injectable()
@@ -21,7 +21,7 @@ export class I18nInterceptor implements HttpInterceptor {
21
21
  const translate = this.injector.get(TranslateService)
22
22
  request = request.clone({
23
23
  setHeaders: {
24
- 'Accept-Language': translate.currentLang || DEFAULT_LANG,
24
+ 'Accept-Language': translate.getCurrentLang() || DEFAULT_LANG,
25
25
  },
26
26
  })
27
27
  return next.handle(request)
@@ -1,7 +1,8 @@
1
1
  import {
2
- APP_INITIALIZER,
3
2
  EnvironmentProviders,
3
+ inject,
4
4
  makeEnvironmentProviders,
5
+ provideAppInitializer,
5
6
  Provider,
6
7
  } from '@angular/core'
7
8
  import {
@@ -22,17 +23,16 @@ import {
22
23
  } from '@angular/common/http'
23
24
  import { I18nInterceptor } from './i18n.interceptor'
24
25
 
25
- function i18nInitializerFromLocalStorageFactory(translate: TranslateService) {
26
- return () => {
27
- translate.setDefaultLang(DEFAULT_LANG)
28
- let storageLang = null
29
- try {
30
- storageLang = localStorage.getItem(LANGUAGE_STORAGE_KEY)
31
- } catch (error) {
32
- console.warn(error)
33
- }
34
- translate.use(storageLang || translate.getBrowserLang() || DEFAULT_LANG)
26
+ function i18nInitializerFromLocalStorageFactory() {
27
+ const translate = inject(TranslateService)
28
+ translate.setFallbackLang(DEFAULT_LANG)
29
+ let storageLang = null
30
+ try {
31
+ storageLang = localStorage.getItem(LANGUAGE_STORAGE_KEY)
32
+ } catch (error) {
33
+ console.warn(error)
35
34
  }
35
+ translate.use(storageLang || translate.getBrowserLang() || DEFAULT_LANG)
36
36
  }
37
37
 
38
38
  export function provideI18n(
@@ -56,12 +56,9 @@ export function provideI18n(
56
56
  provideTranslateService(usedConfig),
57
57
  ]
58
58
  if (useLocalStorage) {
59
- providers.push({
60
- provide: APP_INITIALIZER,
61
- useFactory: i18nInitializerFromLocalStorageFactory,
62
- deps: [TranslateService],
63
- multi: true,
64
- })
59
+ providers.push(
60
+ provideAppInitializer(i18nInitializerFromLocalStorageFactory)
61
+ )
65
62
  }
66
63
 
67
64
  return makeEnvironmentProviders(providers)
@@ -0,0 +1,48 @@
1
+ import { inject, Injectable, InjectionToken } from '@angular/core'
2
+ import {
3
+ provideTranslateService,
4
+ TranslateCompiler,
5
+ TranslateLoader,
6
+ TranslationObject,
7
+ } from '@ngx-translate/core'
8
+ import { Observable, of } from 'rxjs'
9
+ import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'
10
+
11
+ /**
12
+ * This translate loader is used for testing purposes, it allows to provide translations directly in the test file without having to create JSON files
13
+ * Taken from https://github.com/mwootendev/ngx-translate-plugins/issues/87
14
+ */
15
+
16
+ const TestTranslations = new InjectionToken<Record<string, TranslationObject>>(
17
+ 'test-translations'
18
+ )
19
+
20
+ @Injectable()
21
+ class TestTranslateLoader implements TranslateLoader {
22
+ translations = inject(TestTranslations)
23
+ getTranslation(lang: string): Observable<TranslationObject> {
24
+ return of(this.translations[lang] as TranslationObject)
25
+ }
26
+ }
27
+
28
+ export function provideTranslateTestingService(
29
+ translations: Record<string, TranslationObject>
30
+ ) {
31
+ return provideTranslateService({
32
+ loader: [
33
+ {
34
+ provide: TestTranslations,
35
+ useValue: translations,
36
+ },
37
+ {
38
+ provide: TranslateLoader,
39
+ useClass: TestTranslateLoader,
40
+ },
41
+ ],
42
+ fallbackLang: 'en',
43
+ compiler: {
44
+ provide: TranslateCompiler,
45
+ useClass: TranslateMessageFormatCompiler,
46
+ },
47
+ })
48
+ }
@@ -1,4 +1,4 @@
1
- import { Injectable, inject } from '@angular/core'
1
+ import { inject, Injectable } from '@angular/core'
2
2
  import { TranslateService } from '@ngx-translate/core'
3
3
  import { type Locale } from 'date-fns/locale'
4
4
  import { formatDistance } from 'date-fns/formatDistance'
@@ -30,13 +30,13 @@ export class DateService {
30
30
  locale: string
31
31
  dateObj: Date
32
32
  } {
33
- const locale = this.translateService.currentLang || DEFAULT_LANGUAGE
33
+ const locale = this.translateService.getCurrentLang() || DEFAULT_LANGUAGE
34
34
  const dateObj = this.getDateObject(date)
35
35
  return { locale, dateObj }
36
36
  }
37
37
 
38
38
  private async getDateLocale(): Promise<Locale> {
39
- const lang = this.translateService.currentLang || DEFAULT_LANGUAGE
39
+ const lang = this.translateService.getCurrentLang() || DEFAULT_LANGUAGE
40
40
  const locales = await this.dateLocales
41
41
  return locales[lang]
42
42
  }
@@ -1,4 +1,11 @@
1
- import type { Feature, FeatureCollection, Geometry, Position } from 'geojson'
1
+ import type {
2
+ Feature,
3
+ FeatureCollection,
4
+ Geometry,
5
+ Polygon,
6
+ Position,
7
+ } from 'geojson'
8
+ import type { DatasetSpatialExtent } from '../../../../../../libs/common/domain/src/lib/model/record'
2
9
 
3
10
  /**
4
11
  * @returns The geometry if available, otherwise null.
@@ -94,3 +101,53 @@ export function getGeometryBoundingBox(geometry: Geometry): BoundingBox {
94
101
  return coordinatesReducer(emptyExtent, geometry.coordinates)
95
102
  }
96
103
  }
104
+
105
+ /**
106
+ * Builds the closed GeoJSON Polygon matching the given bounding box.
107
+ */
108
+ export function bboxToPolygon(bbox: BoundingBox): Polygon {
109
+ const [minX, minY, maxX, maxY] = bbox
110
+ return {
111
+ type: 'Polygon',
112
+ coordinates: [
113
+ [
114
+ [minX, minY],
115
+ [minX, maxY],
116
+ [maxX, maxY],
117
+ [maxX, minY],
118
+ [minX, minY],
119
+ ],
120
+ ],
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Returns the geometry carried by a spatial extent, falling back to the polygon
126
+ * derived from its bounding box. Returns null when the extent has neither.
127
+ */
128
+ export function spatialExtentToGeometry(
129
+ extent: DatasetSpatialExtent
130
+ ): Geometry | null {
131
+ if (extent.geometry) return extent.geometry
132
+ if (extent.bbox) return bboxToPolygon(extent.bbox)
133
+ return null
134
+ }
135
+
136
+ /**
137
+ * Converts a list of dataset spatial extents into a GeoJSON FeatureCollection,
138
+ * e.g. to be used as the data source of a map layer.
139
+ */
140
+ export function spatialExtentsToFeatureCollection(
141
+ extents: DatasetSpatialExtent[]
142
+ ): FeatureCollection {
143
+ return {
144
+ type: 'FeatureCollection',
145
+ features: extents.reduce<Feature[]>((features, extent) => {
146
+ const geometry = spatialExtentToGeometry(extent)
147
+ if (geometry) {
148
+ features.push({ type: 'Feature', properties: {}, geometry })
149
+ }
150
+ return features
151
+ }, []),
152
+ }
153
+ }
package/style.css CHANGED
@@ -1,4 +1,3 @@
1
1
  @import 'tippy.js/dist/tippy.css';
2
2
  @import 'ol/ol.css';
3
- @import '@angular/material/prebuilt-themes/indigo-pink.css';
4
3
  @import './tailwind.base.css';
package/tailwind.base.css CHANGED
@@ -1,3 +1,5 @@
1
+ @import './material-styles.css';
2
+
1
3
  @tailwind base;
2
4
  @tailwind components;
3
5
  @tailwind utilities;
@@ -243,25 +243,25 @@
243
243
  "editor.record.form.temporalExtents.addRange": "Zeitraum",
244
244
  "editor.record.form.temporalExtents.date": "Datum",
245
245
  "editor.record.form.temporalExtents.range": "Datumsbereich",
246
- "editor.record.form.topics.inspire.biota": "Biologie",
247
- "editor.record.form.topics.inspire.boundaries": "Grenzen",
248
- "editor.record.form.topics.inspire.climatology": "Klimatologie/Meteorologie/Atmosphäre",
249
- "editor.record.form.topics.inspire.economy": "Wirtschaft",
250
- "editor.record.form.topics.inspire.elevation": "Höhenangaben",
251
- "editor.record.form.topics.inspire.environnement": "Umwelt",
252
- "editor.record.form.topics.inspire.farming": "Landwirtschaft",
253
- "editor.record.form.topics.inspire.geoscientific": "Geowissenschaften",
254
- "editor.record.form.topics.inspire.health": "Gesundheitswesen",
255
- "editor.record.form.topics.inspire.imagery": "Bilddaten/Basiskarten/Landbedeckung",
256
- "editor.record.form.topics.inspire.intelligence": "Aufklärung/Militär",
257
- "editor.record.form.topics.inspire.location": "Ortsangaben",
258
- "editor.record.form.topics.inspire.oceans": "Meere",
259
- "editor.record.form.topics.inspire.planning": "Planungsunterlagen/Kataster",
260
- "editor.record.form.topics.inspire.society": "Gesellschaft",
261
- "editor.record.form.topics.inspire.structure": "Bauwerke",
262
- "editor.record.form.topics.inspire.transportation": "Verkehrswesen",
263
- "editor.record.form.topics.inspire.utilities": "Ver- und Entsorgung/Nachrichtenwesen",
264
- "editor.record.form.topics.inspire.waters": "Binnengewässer",
246
+ "editor.record.form.topics.iso.biota": "Biologie",
247
+ "editor.record.form.topics.iso.boundaries": "Grenzen",
248
+ "editor.record.form.topics.iso.climatologyMeteorologyAtmosphere": "Klimatologie/Meteorologie/Atmosphäre",
249
+ "editor.record.form.topics.iso.economy": "Wirtschaft",
250
+ "editor.record.form.topics.iso.elevation": "Höhenangaben",
251
+ "editor.record.form.topics.iso.environment": "Umwelt",
252
+ "editor.record.form.topics.iso.farming": "Landwirtschaft",
253
+ "editor.record.form.topics.iso.geoscientificInformation": "Geowissenschaften",
254
+ "editor.record.form.topics.iso.health": "Gesundheitswesen",
255
+ "editor.record.form.topics.iso.imageryBaseMapsEarthCover": "Bilddaten/Basiskarten/Landbedeckung",
256
+ "editor.record.form.topics.iso.intelligenceMilitary": "Aufklärung/Militär",
257
+ "editor.record.form.topics.iso.location": "Ortsangaben",
258
+ "editor.record.form.topics.iso.oceans": "Meere",
259
+ "editor.record.form.topics.iso.planningCadastre": "Planungsunterlagen/Kataster",
260
+ "editor.record.form.topics.iso.society": "Gesellschaft",
261
+ "editor.record.form.topics.iso.structure": "Bauwerke",
262
+ "editor.record.form.topics.iso.transportation": "Verkehrswesen",
263
+ "editor.record.form.topics.iso.utilitiesCommunication": "Ver- und Entsorgung/Nachrichtenwesen",
264
+ "editor.record.form.topics.iso.inlandWaters": "Binnengewässer",
265
265
  "editor.record.form.topics.placeholder": "Ein oder mehrere Themen auswählen",
266
266
  "editor.record.form.updateFrequency.planned": "Dieses Dataset wird regelmäßig aktualisiert.",
267
267
  "editor.record.importFromExternalFile.failure.body": "Beim Importieren des Datensatzes ist ein Fehler aufgetreten:",
@@ -243,25 +243,25 @@
243
243
  "editor.record.form.temporalExtents.addRange": "Time period",
244
244
  "editor.record.form.temporalExtents.date": "Date",
245
245
  "editor.record.form.temporalExtents.range": "Date range",
246
- "editor.record.form.topics.inspire.biota": "Biota",
247
- "editor.record.form.topics.inspire.boundaries": "Boundaries",
248
- "editor.record.form.topics.inspire.climatology": "Climatology / Meteorology / Atmosphere",
249
- "editor.record.form.topics.inspire.economy": "Economy",
250
- "editor.record.form.topics.inspire.elevation": "Elevation",
251
- "editor.record.form.topics.inspire.environnement": "Environnement",
252
- "editor.record.form.topics.inspire.farming": "Farming",
253
- "editor.record.form.topics.inspire.geoscientific": "Geoscientific Information",
254
- "editor.record.form.topics.inspire.health": "Health",
255
- "editor.record.form.topics.inspire.imagery": "Imagery / Base Maps / Earth Cover",
256
- "editor.record.form.topics.inspire.intelligence": "Intelligence / Military",
257
- "editor.record.form.topics.inspire.location": "Location",
258
- "editor.record.form.topics.inspire.oceans": "Oceans",
259
- "editor.record.form.topics.inspire.planning": "Planning / Cadastre",
260
- "editor.record.form.topics.inspire.society": "Society",
261
- "editor.record.form.topics.inspire.structure": "Structure",
262
- "editor.record.form.topics.inspire.transportation": "Transportation",
263
- "editor.record.form.topics.inspire.utilities": "Utilities / Communication",
264
- "editor.record.form.topics.inspire.waters": "Inland Waters",
246
+ "editor.record.form.topics.iso.biota": "Biota",
247
+ "editor.record.form.topics.iso.boundaries": "Boundaries",
248
+ "editor.record.form.topics.iso.climatologyMeteorologyAtmosphere": "Climatology / Meteorology / Atmosphere",
249
+ "editor.record.form.topics.iso.economy": "Economy",
250
+ "editor.record.form.topics.iso.elevation": "Elevation",
251
+ "editor.record.form.topics.iso.environment": "Environnement",
252
+ "editor.record.form.topics.iso.farming": "Farming",
253
+ "editor.record.form.topics.iso.geoscientificInformation": "Geoscientific Information",
254
+ "editor.record.form.topics.iso.health": "Health",
255
+ "editor.record.form.topics.iso.imageryBaseMapsEarthCover": "Imagery / Base Maps / Earth Cover",
256
+ "editor.record.form.topics.iso.intelligenceMilitary": "Intelligence / Military",
257
+ "editor.record.form.topics.iso.location": "Location",
258
+ "editor.record.form.topics.iso.oceans": "Oceans",
259
+ "editor.record.form.topics.iso.planningCadastre": "Planning / Cadastre",
260
+ "editor.record.form.topics.iso.society": "Society",
261
+ "editor.record.form.topics.iso.structure": "Structure",
262
+ "editor.record.form.topics.iso.transportation": "Transportation",
263
+ "editor.record.form.topics.iso.utilitiesCommunication": "Utilities / Communication",
264
+ "editor.record.form.topics.iso.inlandWaters": "Inland Waters",
265
265
  "editor.record.form.topics.placeholder": "Select one or several topics",
266
266
  "editor.record.form.updateFrequency.planned": "This dataset is updated regularly",
267
267
  "editor.record.importFromExternalFile.failure.body": "Failure",