geonetwork-ui 2.2.0-dev.bea8410b → 2.2.0-dev.ca028047

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 (159) hide show
  1. package/esm2022/libs/api/metadata-converter/src/lib/gn4/atomic-operations.mjs +2 -1
  2. package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.mjs +36 -2
  3. package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.mjs +2 -1
  4. package/esm2022/libs/api/repository/src/lib/gn4/index.mjs +3 -1
  5. package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
  6. package/esm2022/libs/feature/dataviz/src/lib/service/data.service.mjs +5 -4
  7. package/esm2022/libs/feature/editor/src/lib/record-form/record-form.component.mjs +5 -3
  8. package/esm2022/libs/feature/map/src/index.mjs +2 -1
  9. package/esm2022/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.mjs +1 -1
  10. package/esm2022/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.mjs +106 -0
  11. package/esm2022/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.mjs +64 -0
  12. package/esm2022/libs/feature/map/src/lib/constant/index.mjs +2 -1
  13. package/esm2022/libs/feature/map/src/lib/constant/projections.mjs +2 -0
  14. package/esm2022/libs/feature/map/src/lib/feature-map.module.mjs +23 -3
  15. package/esm2022/libs/feature/map/src/lib/geocoding/geocoding.component.mjs +93 -0
  16. package/esm2022/libs/feature/map/src/lib/geocoding.service.mjs +40 -0
  17. package/esm2022/libs/feature/map/src/lib/layers-panel/layers-panel.component.mjs +5 -3
  18. package/esm2022/libs/feature/map/src/lib/map-context/map-context.service.mjs +3 -1
  19. package/esm2022/libs/feature/map/src/lib/utils/index.mjs +1 -3
  20. package/esm2022/libs/feature/map/src/lib/utils/map-utils.service.mjs +60 -29
  21. package/esm2022/libs/feature/record/src/lib/map-view/map-view.component.mjs +29 -20
  22. package/esm2022/libs/ui/catalog/src/lib/organisation-preview/organisation-preview.component.mjs +1 -1
  23. package/esm2022/libs/ui/elements/src/index.mjs +3 -1
  24. package/esm2022/libs/ui/elements/src/lib/downloads-list/downloads-list.component.mjs +3 -3
  25. package/esm2022/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.mjs +27 -0
  26. package/esm2022/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.mjs +2 -2
  27. package/esm2022/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.mjs +3 -3
  28. package/esm2022/libs/ui/elements/src/lib/related-record-card/related-record-card.component.mjs +3 -3
  29. package/esm2022/libs/ui/elements/src/lib/thumbnail/thumbnail.component.mjs +7 -3
  30. package/esm2022/libs/ui/elements/src/lib/ui-elements.module.mjs +10 -3
  31. package/esm2022/libs/ui/inputs/src/lib/button/button.component.mjs +2 -2
  32. package/esm2022/libs/ui/inputs/src/lib/editable-label/editable-label.directive.mjs +46 -0
  33. package/esm2022/libs/ui/inputs/src/lib/ui-inputs.module.mjs +8 -3
  34. package/esm2022/libs/ui/layout/src/lib/carousel/carousel.component.mjs +2 -2
  35. package/esm2022/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.mjs +1 -1
  36. package/esm2022/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.mjs +1 -1
  37. package/esm2022/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.mjs +1 -1
  38. package/esm2022/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.mjs +1 -1
  39. package/esm2022/libs/ui/search/src/lib/record-preview-title/record-preview-title.component.mjs +1 -1
  40. package/esm2022/libs/ui/search/src/lib/record-table/record-table.component.mjs +3 -3
  41. package/esm2022/libs/util/shared/src/lib/links/link-utils.mjs +29 -13
  42. package/esm2022/translations/de.json +99 -95
  43. package/esm2022/translations/en.json +20 -16
  44. package/esm2022/translations/es.json +4 -0
  45. package/esm2022/translations/fr.json +4 -0
  46. package/esm2022/translations/it.json +4 -0
  47. package/esm2022/translations/nl.json +4 -0
  48. package/esm2022/translations/pt.json +4 -0
  49. package/fesm2022/geonetwork-ui.mjs +673 -235
  50. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  51. package/libs/api/metadata-converter/src/lib/gn4/atomic-operations.d.ts +1 -0
  52. package/libs/api/metadata-converter/src/lib/gn4/atomic-operations.d.ts.map +1 -1
  53. package/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.d.ts.map +1 -1
  54. package/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.d.ts.map +1 -1
  55. package/libs/api/repository/src/lib/gn4/index.d.ts +2 -0
  56. package/libs/api/repository/src/lib/gn4/index.d.ts.map +1 -1
  57. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +10 -5
  58. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  59. package/libs/feature/dataviz/src/lib/service/data.service.d.ts +15 -2
  60. package/libs/feature/dataviz/src/lib/service/data.service.d.ts.map +1 -1
  61. package/libs/feature/editor/src/lib/record-form/record-form.component.d.ts.map +1 -1
  62. package/libs/feature/map/src/index.d.ts +1 -0
  63. package/libs/feature/map/src/index.d.ts.map +1 -1
  64. package/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.d.ts +22 -0
  65. package/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.d.ts.map +1 -0
  66. package/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.d.ts +22 -0
  67. package/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.d.ts.map +1 -0
  68. package/libs/feature/map/src/lib/constant/index.d.ts +1 -0
  69. package/libs/feature/map/src/lib/constant/index.d.ts.map +1 -1
  70. package/libs/feature/map/src/lib/constant/projections.d.ts.map +1 -0
  71. package/libs/feature/map/src/lib/feature-map.module.d.ts +15 -12
  72. package/libs/feature/map/src/lib/feature-map.module.d.ts.map +1 -1
  73. package/libs/feature/map/src/lib/geocoding/geocoding.component.d.ts +25 -0
  74. package/libs/feature/map/src/lib/geocoding/geocoding.component.d.ts.map +1 -0
  75. package/libs/feature/map/src/lib/geocoding.service.d.ts +18 -0
  76. package/libs/feature/map/src/lib/geocoding.service.d.ts.map +1 -0
  77. package/libs/feature/map/src/lib/map-context/map-context.service.d.ts +1 -0
  78. package/libs/feature/map/src/lib/map-context/map-context.service.d.ts.map +1 -1
  79. package/libs/feature/map/src/lib/utils/index.d.ts +0 -2
  80. package/libs/feature/map/src/lib/utils/index.d.ts.map +1 -1
  81. package/libs/feature/map/src/lib/utils/map-utils.service.d.ts +13 -11
  82. package/libs/feature/map/src/lib/utils/map-utils.service.d.ts.map +1 -1
  83. package/libs/feature/record/src/lib/map-view/map-view.component.d.ts +1 -3
  84. package/libs/feature/record/src/lib/map-view/map-view.component.d.ts.map +1 -1
  85. package/libs/ui/dataviz/src/lib/chart/chart.component.d.ts +1 -1
  86. package/libs/ui/elements/src/index.d.ts +2 -0
  87. package/libs/ui/elements/src/index.d.ts.map +1 -1
  88. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts +1 -1
  89. package/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.d.ts +10 -0
  90. package/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.d.ts.map +1 -0
  91. package/libs/ui/elements/src/lib/thumbnail/thumbnail.component.d.ts +3 -2
  92. package/libs/ui/elements/src/lib/thumbnail/thumbnail.component.d.ts.map +1 -1
  93. package/libs/ui/elements/src/lib/ui-elements.module.d.ts +12 -11
  94. package/libs/ui/elements/src/lib/ui-elements.module.d.ts.map +1 -1
  95. package/libs/ui/inputs/src/lib/editable-label/editable-label.directive.d.ts +13 -0
  96. package/libs/ui/inputs/src/lib/editable-label/editable-label.directive.d.ts.map +1 -0
  97. package/libs/ui/inputs/src/lib/ui-inputs.module.d.ts +2 -1
  98. package/libs/ui/inputs/src/lib/ui-inputs.module.d.ts.map +1 -1
  99. package/libs/util/shared/src/lib/links/link-utils.d.ts +19 -7
  100. package/libs/util/shared/src/lib/links/link-utils.d.ts.map +1 -1
  101. package/package.json +4 -1
  102. package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +3 -0
  103. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +43 -0
  104. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.ts +1 -0
  105. package/src/libs/api/repository/src/lib/gn4/index.ts +2 -0
  106. package/src/libs/common/domain/src/lib/index.ts +2 -0
  107. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +12 -7
  108. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +8 -5
  109. package/src/libs/feature/editor/src/lib/record-form/record-form.component.ts +2 -1
  110. package/src/libs/feature/map/src/index.ts +1 -0
  111. package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.css +0 -0
  112. package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +21 -0
  113. package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.ts +107 -0
  114. package/src/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.css +0 -0
  115. package/src/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.html +37 -0
  116. package/src/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.ts +64 -0
  117. package/src/libs/feature/map/src/lib/constant/index.ts +1 -0
  118. package/src/libs/feature/map/src/lib/feature-map.module.ts +12 -0
  119. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.css +0 -0
  120. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.html +39 -0
  121. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.ts +99 -0
  122. package/src/libs/feature/map/src/lib/geocoding.service.ts +59 -0
  123. package/src/libs/feature/map/src/lib/layers-panel/layers-panel.component.html +6 -2
  124. package/src/libs/feature/map/src/lib/map-context/map-context.service.ts +6 -0
  125. package/src/libs/feature/map/src/lib/utils/index.ts +0 -2
  126. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +85 -50
  127. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +18 -3
  128. package/src/libs/ui/elements/src/index.ts +2 -0
  129. package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.html +4 -1
  130. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +0 -0
  131. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +30 -0
  132. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +15 -0
  133. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.css +52 -52
  134. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html +2 -2
  135. package/src/libs/ui/elements/src/lib/related-record-card/related-record-card.component.html +1 -1
  136. package/src/libs/ui/elements/src/lib/thumbnail/thumbnail.component.ts +4 -0
  137. package/src/libs/ui/elements/src/lib/ui-elements.module.ts +4 -0
  138. package/src/libs/ui/inputs/src/lib/button/button.component.css +1 -1
  139. package/src/libs/ui/inputs/src/lib/editable-label/editable-label.directive.ts +48 -0
  140. package/src/libs/ui/inputs/src/lib/ui-inputs.module.ts +3 -0
  141. package/src/libs/ui/layout/src/lib/carousel/carousel.component.css +1 -1
  142. package/src/libs/ui/search/src/lib/record-table/record-table.component.html +2 -2
  143. package/src/libs/util/shared/src/lib/links/link-utils.ts +34 -11
  144. package/translations/de.json +99 -95
  145. package/translations/en.json +20 -16
  146. package/translations/es.json +4 -0
  147. package/translations/fr.json +4 -0
  148. package/translations/it.json +4 -0
  149. package/translations/nl.json +4 -0
  150. package/translations/pt.json +4 -0
  151. package/translations/sk.json +4 -0
  152. package/esm2022/libs/feature/map/src/lib/utils/map-utils-wms.service.mjs +0 -55
  153. package/esm2022/libs/feature/map/src/lib/utils/projections.mjs +0 -2
  154. package/libs/feature/map/src/lib/utils/map-utils-wms.service.d.ts +0 -17
  155. package/libs/feature/map/src/lib/utils/map-utils-wms.service.d.ts.map +0 -1
  156. package/libs/feature/map/src/lib/utils/projections.d.ts.map +0 -1
  157. package/src/libs/feature/map/src/lib/utils/map-utils-wms.service.ts +0 -58
  158. /package/libs/feature/map/src/lib/{utils → constant}/projections.d.ts +0 -0
  159. /package/src/libs/feature/map/src/lib/{utils → constant}/projections.ts +0 -0
@@ -21,6 +21,10 @@ import { AddLayerRecordPreviewComponent } from './add-layer-from-catalog/add-lay
21
21
  import { UiElementsModule } from '../../../../../libs/ui/elements/src'
22
22
  import { UiInputsModule } from '../../../../../libs/ui/inputs/src'
23
23
  import { AddLayerFromWmsComponent } from './add-layer-from-wms/add-layer-from-wms.component'
24
+ import { AddLayerFromFileComponent } from './add-layer-from-file/add-layer-from-file.component'
25
+ import { AddLayerFromWfsComponent } from './add-layer-from-wfs/add-layer-from-wfs.component'
26
+ import { GeocodingComponent } from './geocoding/geocoding.component'
27
+ import { GEOCODING_PROVIDER, GeocodingProvider } from './geocoding.service'
24
28
 
25
29
  @NgModule({
26
30
  declarations: [
@@ -31,6 +35,9 @@ import { AddLayerFromWmsComponent } from './add-layer-from-wms/add-layer-from-wm
31
35
  MapContainerComponent,
32
36
  AddLayerRecordPreviewComponent,
33
37
  AddLayerFromWmsComponent,
38
+ AddLayerFromFileComponent,
39
+ AddLayerFromWfsComponent,
40
+ GeocodingComponent,
34
41
  ],
35
42
  exports: [
36
43
  MapContextComponent,
@@ -38,6 +45,7 @@ import { AddLayerFromWmsComponent } from './add-layer-from-wms/add-layer-from-wm
38
45
  LayersPanelComponent,
39
46
  AddLayerFromCatalogComponent,
40
47
  MapContainerComponent,
48
+ GeocodingComponent,
41
49
  ],
42
50
  imports: [
43
51
  CommonModule,
@@ -58,6 +66,10 @@ import { AddLayerFromWmsComponent } from './add-layer-from-wms/add-layer-from-wm
58
66
  useValue: defaultMapOptions,
59
67
  },
60
68
  MapFacade,
69
+ {
70
+ provide: GEOCODING_PROVIDER,
71
+ useValue: ['geonames', { maxRows: 5 }] as GeocodingProvider,
72
+ },
61
73
  ],
62
74
  })
63
75
  export class FeatureMapModule {}
@@ -0,0 +1,39 @@
1
+ <gn-ui-search-input
2
+ [(value)]="searchText"
3
+ (valueChange)="onSearchChange($event)"
4
+ (keyup.enter)="onEnterPress()"
5
+ [placeholder]="'map.geocoding.placeholder' | translate"
6
+ >
7
+ </gn-ui-search-input>
8
+ <ul
9
+ class="bg-gray-50 border border-gray-200 w-full mt-2 shadow-sm rounded-lg"
10
+ *ngIf="results && results.length"
11
+ >
12
+ <li
13
+ *ngFor="let result of results"
14
+ (click)="zoomToLocation(result)"
15
+ class="flex items-center pl-8 pr-4 py-2 border-b border-gray-200 relative cursor-pointer hover:bg-blue-100 hover:text-gray-800 transition duration-300 ease-in-out"
16
+ >
17
+ <svg
18
+ class="stroke-current text-blue-500 absolute w-5 h-5 left-3 top-3"
19
+ xmlns="http://www.w3.org/2000/svg"
20
+ fill="none"
21
+ viewBox="0 0 24 24"
22
+ stroke="currentColor"
23
+ >
24
+ <path
25
+ stroke-linecap="round"
26
+ stroke-linejoin="round"
27
+ stroke-width="2"
28
+ d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
29
+ />
30
+ <path
31
+ stroke-linecap="round"
32
+ stroke-linejoin="round"
33
+ stroke-width="2"
34
+ d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
35
+ />
36
+ </svg>
37
+ <span class="font-sans font-semibold ml-4">{{ result.label }}</span>
38
+ </li>
39
+ </ul>
@@ -0,0 +1,99 @@
1
+ import { Component, OnDestroy } from '@angular/core'
2
+ import { catchError, from, Subject, takeUntil } from 'rxjs'
3
+ import { debounceTime, switchMap } from 'rxjs/operators'
4
+ import { MapManagerService } from '../manager/map-manager.service'
5
+ import { fromLonLat } from 'ol/proj'
6
+ import { Polygon } from 'ol/geom'
7
+ import { GeocodingService } from '../geocoding.service'
8
+
9
+ @Component({
10
+ selector: 'gn-ui-geocoding',
11
+ templateUrl: './geocoding.component.html',
12
+ styleUrls: ['./geocoding.component.css'],
13
+ })
14
+ export class GeocodingComponent implements OnDestroy {
15
+ searchText = ''
16
+ results: any[] = []
17
+ searchTextChanged = new Subject<string>()
18
+ destroy$ = new Subject<void>()
19
+ errorMessage: string | null = null
20
+
21
+ constructor(
22
+ private mapManager: MapManagerService,
23
+ private geocodingService: GeocodingService
24
+ ) {
25
+ this.searchTextChanged
26
+ .pipe(
27
+ debounceTime(300),
28
+ switchMap((searchText) => {
29
+ return from(this.geocodingService.query(searchText)).pipe(
30
+ catchError((error) => {
31
+ this.errorMessage =
32
+ 'An error occurred while searching. Please try again.'
33
+ console.error(error)
34
+ return []
35
+ })
36
+ )
37
+ }),
38
+ takeUntil(this.destroy$)
39
+ )
40
+ .subscribe((results) => {
41
+ this.results = results
42
+ })
43
+ }
44
+
45
+ ngOnDestroy() {
46
+ this.destroy$.next()
47
+ this.destroy$.complete()
48
+ }
49
+
50
+ onSearchChange(searchText: string) {
51
+ if (!searchText) {
52
+ this.clearSearch()
53
+ return
54
+ } else {
55
+ this.searchTextChanged.next(searchText)
56
+ }
57
+ }
58
+
59
+ clearSearch() {
60
+ this.searchText = ''
61
+ this.results = []
62
+ this.errorMessage = null
63
+ }
64
+
65
+ zoomToLocation(result: any) {
66
+ const map = this.mapManager.map
67
+ const view = map.getView()
68
+ const geometry = result.geom
69
+
70
+ if (geometry.type === 'Point') {
71
+ this.zoomToPoint(geometry.coordinates, view)
72
+ } else if (geometry.type === 'Polygon') {
73
+ this.zoomToPolygon(geometry.coordinates, view)
74
+ } else {
75
+ console.error(`Unsupported geometry type: ${geometry.type}`)
76
+ }
77
+ }
78
+
79
+ zoomToPoint(pointCoords: [number, number], view: any) {
80
+ const transformedCoords = fromLonLat(pointCoords)
81
+ view.setCenter(transformedCoords)
82
+ view.setZoom(12)
83
+ }
84
+
85
+ zoomToPolygon(polygonCoords: [[number, number][]], view: any) {
86
+ const transformedCoords = polygonCoords[0].map((coord) => fromLonLat(coord))
87
+ const polygon = new Polygon([transformedCoords])
88
+ view.fit(polygon, {
89
+ duration: 100,
90
+ maxZoom: 12,
91
+ })
92
+ }
93
+
94
+ onEnterPress() {
95
+ if (this.results && this.results.length > 0) {
96
+ this.zoomToLocation(this.results[0])
97
+ }
98
+ }
99
+ }
@@ -0,0 +1,59 @@
1
+ import { Injectable, Inject, InjectionToken } from '@angular/core'
2
+ import {
3
+ queryGeoadmin,
4
+ GeoadminOptions,
5
+ GeocodingResult,
6
+ queryGeonames,
7
+ GeonamesOptions,
8
+ DataGouvFrOptions,
9
+ queryDataGouvFr,
10
+ } from '@geospatial-sdk/geocoding'
11
+ import { from, Observable, throwError } from 'rxjs'
12
+ import { catchError } from 'rxjs/operators'
13
+
14
+ type GeoadminGeocodingProvider = ['geoadmin', GeoadminOptions]
15
+ type GeonamesGeocodingProvider = ['geonames', GeonamesOptions]
16
+ type DataGouvFrGeocodingProvider = ['data-gouv-fr', DataGouvFrOptions]
17
+ export type GeocodingProvider =
18
+ | GeoadminGeocodingProvider
19
+ | GeonamesGeocodingProvider
20
+ | DataGouvFrGeocodingProvider
21
+
22
+ export const GEOCODING_PROVIDER = new InjectionToken<GeocodingProvider>(
23
+ 'geocoding-provider'
24
+ )
25
+
26
+ @Injectable({
27
+ providedIn: 'root',
28
+ })
29
+ export class GeocodingService {
30
+ constructor(
31
+ @Inject(GEOCODING_PROVIDER) private provider: GeocodingProvider
32
+ ) {}
33
+
34
+ query(text: string): Observable<GeocodingResult[]> {
35
+ let queryObservable: Observable<GeocodingResult[]>
36
+ switch (this.provider[0]) {
37
+ case 'geoadmin':
38
+ queryObservable = from(
39
+ queryGeoadmin(text, this.provider[1] as GeoadminOptions)
40
+ )
41
+ break
42
+ case 'geonames':
43
+ queryObservable = from(
44
+ queryGeonames(text, this.provider[1] as GeonamesOptions)
45
+ )
46
+ break
47
+ case 'data-gouv-fr':
48
+ queryObservable = from(
49
+ queryDataGouvFr(text, this.provider[1] as DataGouvFrOptions)
50
+ )
51
+ break
52
+ default:
53
+ return throwError(
54
+ () => new Error(`Unsupported geocoding provider: ${this.provider[0]}`)
55
+ )
56
+ }
57
+ return queryObservable.pipe(catchError((error) => throwError(error)))
58
+ }
59
+ }
@@ -36,10 +36,14 @@
36
36
  </div>
37
37
  </mat-tab>
38
38
  <mat-tab [label]="'map.add.layer.wfs' | translate" bodyClass="h-full">
39
- <div class="p-3 h-full">Add from WFS</div>
39
+ <div class="p-3">
40
+ <gn-ui-add-layer-from-wfs></gn-ui-add-layer-from-wfs>
41
+ </div>
40
42
  </mat-tab>
41
43
  <mat-tab [label]="'map.add.layer.file' | translate" bodyClass="h-full">
42
- <div class="p-3 h-full">Add from file</div>
44
+ <div class="p-3">
45
+ <gn-ui-add-layer-from-file></gn-ui-add-layer-from-file>
46
+ </div>
43
47
  </mat-tab>
44
48
  </mat-tab-group>
45
49
  </gn-ui-expandable-panel-button>
@@ -40,6 +40,8 @@ export const DEFAULT_VIEW: MapContextViewModel = {
40
40
  zoom: 2,
41
41
  }
42
42
 
43
+ export const WFS_MAX_FEATURES = 10000
44
+
43
45
  @Injectable({
44
46
  providedIn: 'root',
45
47
  })
@@ -111,6 +113,10 @@ export class MapContextService {
111
113
  urlObj.searchParams.set('typename', layerModel.name)
112
114
  urlObj.searchParams.set('srsname', 'EPSG:3857')
113
115
  urlObj.searchParams.set('bbox', `${extent.join(',')},EPSG:3857`)
116
+ urlObj.searchParams.set(
117
+ 'maxFeatures',
118
+ WFS_MAX_FEATURES.toString()
119
+ )
114
120
  return urlObj.toString()
115
121
  },
116
122
  strategy: bboxStrategy,
@@ -1,3 +1 @@
1
1
  export * from './map-utils.service'
2
- export * from './map-utils-wms.service'
3
- export * from './projections'
@@ -2,18 +2,18 @@ import { HttpClient } from '@angular/common/http'
2
2
  import { Injectable } from '@angular/core'
3
3
  import type { FeatureCollection } from 'geojson'
4
4
  import { extend, Extent, isEmpty } from 'ol/extent'
5
- import OlFeature, { FeatureLike } from 'ol/Feature'
5
+ import Feature from 'ol/Feature'
6
6
  import GeoJSON from 'ol/format/GeoJSON'
7
7
  import { Geometry } from 'ol/geom'
8
8
  import Layer from 'ol/layer/Layer'
9
9
  import Map from 'ol/Map'
10
- import { fromLonLat } from 'ol/proj'
10
+ import { transformExtent } from 'ol/proj'
11
11
  import Source from 'ol/source/Source'
12
12
  import ImageWMS from 'ol/source/ImageWMS'
13
13
  import TileWMS from 'ol/source/TileWMS'
14
14
  import VectorSource from 'ol/source/Vector'
15
- import { Options, optionsFromCapabilities } from 'ol/source/WMTS'
16
- import { DragPan, MouseWheelZoom, defaults, Interaction } from 'ol/interaction'
15
+ import { optionsFromCapabilities } from 'ol/source/WMTS'
16
+ import { defaults, DragPan, Interaction, MouseWheelZoom } from 'ol/interaction'
17
17
  import {
18
18
  mouseOnly,
19
19
  noModifierKeys,
@@ -21,45 +21,53 @@ import {
21
21
  primaryAction,
22
22
  } from 'ol/events/condition'
23
23
  import WMTSCapabilities from 'ol/format/WMTSCapabilities'
24
- import { from, Observable, of } from 'rxjs'
24
+ import { from, Observable } from 'rxjs'
25
25
  import { map } from 'rxjs/operators'
26
26
  import {
27
27
  MapContextLayerModel,
28
28
  MapContextLayerTypeEnum,
29
+ MapContextLayerWmsModel,
29
30
  MapContextLayerWmtsModel,
30
31
  } from '../map-context/map-context.model'
31
- import { MapUtilsWMSService } from './map-utils-wms.service'
32
32
  import Collection from 'ol/Collection'
33
33
  import MapBrowserEvent from 'ol/MapBrowserEvent'
34
- import { DatasetDistribution } from '../../../../../../libs/common/domain/src/lib/model/record'
34
+ import {
35
+ CatalogRecord,
36
+ DatasetDistribution,
37
+ } from '../../../../../../libs/common/domain/src/lib/model/record'
38
+ import { ProxyService } from '../../../../../../libs/util/shared/src'
39
+ import { WmsEndpoint } from '@camptocamp/ogc-client'
40
+ import { LONLAT_CRS_CODES } from '../constant/projections'
41
+ import { fromEPSGCode, register } from 'ol/proj/proj4'
42
+ import proj4 from 'proj4/dist/proj4'
35
43
 
36
44
  const FEATURE_PROJECTION = 'EPSG:3857'
37
45
  const DATA_PROJECTION = 'EPSG:4326'
38
46
 
47
+ const GEOJSON = new GeoJSON()
48
+
39
49
  @Injectable({
40
50
  providedIn: 'root',
41
51
  })
42
52
  export class MapUtilsService {
43
- constructor(private http: HttpClient, private wmsUtils: MapUtilsWMSService) {}
53
+ constructor(private http: HttpClient, private proxy: ProxyService) {}
44
54
 
45
55
  createEmptyMap(): Map {
46
- const map = new Map({
56
+ return new Map({
47
57
  controls: [],
48
58
  pixelRatio: 1,
49
59
  })
50
- return map
51
60
  }
52
61
 
53
62
  readFeatureCollection = (
54
63
  featureCollection: FeatureCollection,
55
64
  featureProjection = FEATURE_PROJECTION,
56
65
  dataProjection = DATA_PROJECTION
57
- ): FeatureLike[] => {
58
- const olFeatures = new GeoJSON().readFeatures(featureCollection, {
66
+ ): Feature<Geometry>[] => {
67
+ return GEOJSON.readFeatures(featureCollection, {
59
68
  featureProjection,
60
69
  dataProjection,
61
- })
62
- return olFeatures
70
+ }) as Feature<Geometry>[]
63
71
  }
64
72
 
65
73
  isWMSLayer(layer: Layer<Source>): boolean {
@@ -78,20 +86,14 @@ export class MapUtilsService {
78
86
  ...source.getParams(),
79
87
  INFO_FORMAT: 'application/json',
80
88
  }
81
- const url = source.getFeatureInfoUrl(
82
- coordinate,
83
- resolution,
84
- projection,
85
- params
86
- )
87
- return url
89
+ return source.getFeatureInfoUrl(coordinate, resolution, projection, params)
88
90
  }
89
91
 
90
- getVectorFeaturesFromClick(olMap, event): OlFeature<Geometry>[] {
92
+ getVectorFeaturesFromClick(olMap, event): Feature<Geometry>[] {
91
93
  const features = []
92
94
  const hit = olMap.forEachFeatureAtPixel(
93
95
  event.pixel,
94
- (feature: OlFeature<Geometry>) => {
96
+ (feature: Feature<Geometry>) => {
95
97
  return feature
96
98
  },
97
99
  { layerFilter: (layer) => layer.getSource() instanceof VectorSource }
@@ -103,9 +105,9 @@ export class MapUtilsService {
103
105
  }
104
106
 
105
107
  getGFIFeaturesObservablesFromClick(
106
- olMap,
107
- event
108
- ): Observable<OlFeature<Geometry>[]>[] {
108
+ olMap: Map,
109
+ event: MapBrowserEvent<PointerEvent>
110
+ ): Observable<Feature<Geometry>[]>[] {
109
111
  const wmsLayers = olMap.getLayers().getArray().filter(this.isWMSLayer)
110
112
 
111
113
  if (wmsLayers.length > 0) {
@@ -128,8 +130,8 @@ export class MapUtilsService {
128
130
  /**
129
131
  * Will emit `null` if no extent could be computed
130
132
  */
131
- getLayerExtent(layer: MapContextLayerModel): Observable<Extent | null> {
132
- let geographicExtent: Observable<Extent>
133
+ async getLayerExtent(layer: MapContextLayerModel): Promise<Extent | null> {
134
+ let latLonExtent: Extent
133
135
  if (
134
136
  layer &&
135
137
  layer.type === 'geojson' &&
@@ -138,37 +140,55 @@ export class MapUtilsService {
138
140
  layer.data.features[0] &&
139
141
  layer.data.features[0].geometry
140
142
  ) {
141
- geographicExtent = of(layer.data).pipe(
142
- map((layerData) =>
143
- new GeoJSON()
144
- .readFeatures(layerData)
145
- .map((feature) => feature.getGeometry())
146
- .filter((geom) => !!geom)
147
- .reduce(
148
- (prev, curr) =>
149
- prev ? extend(prev, curr.getExtent()) : curr.getExtent(),
150
- null as Extent
151
- )
143
+ latLonExtent = new GeoJSON()
144
+ .readFeatures(layer.data)
145
+ .map((feature) => feature.getGeometry())
146
+ .filter((geom) => !!geom)
147
+ .reduce(
148
+ (prev, curr) =>
149
+ prev ? extend(prev, curr.getExtent()) : curr.getExtent(),
150
+ null as Extent
152
151
  )
153
- )
154
152
  } else if (layer && layer.type === 'wms') {
155
- geographicExtent = this.wmsUtils.getLayerLonLatBBox(layer)
153
+ latLonExtent = await this.getWmsLayerExtent(layer)
156
154
  } else if (layer && layer.type === 'wmts') {
157
155
  if (layer.extent) {
158
- geographicExtent = of(layer.extent)
156
+ latLonExtent = layer.extent
159
157
  } else {
160
- return of(layer.options.tileGrid.getExtent())
158
+ return layer.options.tileGrid.getExtent()
161
159
  }
162
160
  } else {
163
- return of(null)
161
+ return null
162
+ }
163
+ if (!latLonExtent || isEmpty(latLonExtent)) {
164
+ return null
164
165
  }
165
- return geographicExtent.pipe(
166
- map((extent) => [
167
- ...fromLonLat([extent[0], extent[1]], 'EPSG:3857'),
168
- ...fromLonLat([extent[2], extent[3]], 'EPSG:3857'),
169
- ]),
170
- map((extent) => (isEmpty(extent) ? null : extent))
166
+ return transformExtent(latLonExtent, 'EPSG:4326', 'EPSG:3857')
167
+ }
168
+
169
+ async getWmsLayerExtent(
170
+ layer: MapContextLayerWmsModel
171
+ ): Promise<Extent | null> {
172
+ const endpoint = await new WmsEndpoint(
173
+ this.proxy.getProxiedUrl(layer.url)
174
+ ).isReady()
175
+ const { boundingBoxes } = endpoint.getLayerByName(layer.name)
176
+ if (!Object.keys(boundingBoxes).length) {
177
+ return null
178
+ }
179
+ const lonLatCRS = Object.keys(boundingBoxes)?.find((crs) =>
180
+ LONLAT_CRS_CODES.includes(crs)
171
181
  )
182
+ if (lonLatCRS) {
183
+ return boundingBoxes[lonLatCRS].map(parseFloat)
184
+ } else {
185
+ const availableEPSGCode = Object.keys(boundingBoxes)[0]
186
+ register(proj4)
187
+ const proj = await fromEPSGCode(availableEPSGCode)
188
+ const bboxWithFiniteNumbers =
189
+ boundingBoxes[availableEPSGCode].map(parseFloat)
190
+ return transformExtent(bboxWithFiniteNumbers, proj, 'EPSG:4326')
191
+ }
172
192
  }
173
193
 
174
194
  getWmtsLayerFromCapabilities(
@@ -235,6 +255,21 @@ ${e.stack || e.message || e}`)
235
255
  .getArray()
236
256
  )
237
257
  }
258
+
259
+ getRecordExtent(record: Partial<CatalogRecord>): Extent {
260
+ if (!('spatialExtents' in record)) {
261
+ return null
262
+ }
263
+ // transform an array of geojson geometries into a bbox
264
+ const totalExtent = record.spatialExtents.reduce(
265
+ (prev, curr) => {
266
+ const geom = GEOJSON.readGeometry(curr.geometry)
267
+ return extend(prev, geom.getExtent())
268
+ },
269
+ [Infinity, Infinity, -Infinity, -Infinity]
270
+ )
271
+ return transformExtent(totalExtent, 'EPSG:4326', 'EPSG:3857')
272
+ }
238
273
  }
239
274
 
240
275
  export function dragPanCondition(
@@ -22,10 +22,14 @@ import { StyleLike } from 'ol/style/Style'
22
22
  import {
23
23
  BehaviorSubject,
24
24
  combineLatest,
25
+ from,
26
+ lastValueFrom,
25
27
  Observable,
26
28
  of,
29
+ startWith,
27
30
  Subscription,
28
31
  throwError,
32
+ withLatestFrom,
29
33
  } from 'rxjs'
30
34
  import {
31
35
  catchError,
@@ -99,7 +103,7 @@ export class MapViewComponent implements OnInit, OnDestroy {
99
103
 
100
104
  mapContext$ = this.currentLayers$.pipe(
101
105
  switchMap((layers) =>
102
- this.mapUtils.getLayerExtent(layers[0]).pipe(
106
+ from(this.mapUtils.getLayerExtent(layers[0])).pipe(
103
107
  catchError((error) => {
104
108
  console.warn(error) // FIXME: report this to the user somehow
105
109
  return of(undefined)
@@ -115,7 +119,19 @@ export class MapViewComponent implements OnInit, OnDestroy {
115
119
  ),
116
120
  tap(() => this.resetSelection())
117
121
  )
118
- )
122
+ ),
123
+ withLatestFrom(this.mdViewFacade.metadata$),
124
+ map(([context, metadata]) => {
125
+ if (context.view.extent) return context
126
+ const extent = this.mapUtils.getRecordExtent(metadata)
127
+ return {
128
+ ...context,
129
+ view: {
130
+ ...context.view,
131
+ extent,
132
+ },
133
+ }
134
+ })
119
135
  )
120
136
 
121
137
  constructor(
@@ -123,7 +139,6 @@ export class MapViewComponent implements OnInit, OnDestroy {
123
139
  private mapManager: MapManagerService,
124
140
  private mapUtils: MapUtilsService,
125
141
  private dataService: DataService,
126
- private proxy: ProxyService,
127
142
  private featureInfo: FeatureInfoService,
128
143
  private changeRef: ChangeDetectorRef,
129
144
  private styleService: MapStyleService
@@ -18,5 +18,7 @@ export * from './lib/pagination/pagination.component'
18
18
  export * from './lib/related-record-card/related-record-card.component'
19
19
  export * from './lib/search-results-error/search-results-error.component'
20
20
  export * from './lib/user-preview/user-preview.component'
21
+ export * from './lib/max-lines/max-lines.component'
21
22
  export * from './lib/record-api-form/record-api-form.component'
22
23
  export * from './lib/markdown-parser/markdown-parser.component'
24
+ export * from './lib/image-overlay-preview/image-overlay-preview.component'
@@ -8,7 +8,10 @@
8
8
  >
9
9
  record.metadata.download
10
10
  </p>
11
- <div class="flex flex-wrap justify-start sm:justify-end sm:pb-4">
11
+ <div
12
+ class="flex flex-wrap justify-start sm:justify-end sm:pb-4"
13
+ data-cy="download-format-filters"
14
+ >
12
15
  <gn-ui-button
13
16
  class="m-1 format-filter"
14
17
  [extraClass]="
@@ -0,0 +1,30 @@
1
+ <gn-ui-content-ghost
2
+ [showContent]="imageUrl !== undefined"
3
+ ghostClass="h-48 mb-3"
4
+ >
5
+ <div
6
+ *ngIf="imageUrl"
7
+ [showContent]="imageUrl !== undefined"
8
+ data-cy="record-thumbnail"
9
+ class="flex-shrink-0 bg-gray-100 rounded-lg overflow-hidden w-full border border-gray-300 group-hover:shadow-xl group-hover:border-0 h-48 mb-3"
10
+ >
11
+ <gn-ui-thumbnail
12
+ class="relative h-full w-full"
13
+ [thumbnailUrl]="imageUrl"
14
+ fit="cover"
15
+ (placeholderShown)="isPlaceholderShown.emit($event)"
16
+ ></gn-ui-thumbnail>
17
+ <div class="relative">
18
+ <gn-ui-button
19
+ class="absolute bottom-0 right-0 z-10 mr-2 mb-2"
20
+ [type]="'outline'"
21
+ [extraClass]="'!py-2 !px-0'"
22
+ (buttonClick)="openLightbox(imageUrl)"
23
+ >
24
+ <mat-icon class="material-symbols-outlined font-extralight"
25
+ >zoom_out_map</mat-icon
26
+ >
27
+ </gn-ui-button>
28
+ </div>
29
+ </div>
30
+ </gn-ui-content-ghost>
@@ -0,0 +1,15 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core'
2
+ import * as basicLightbox from 'basiclightbox'
3
+
4
+ @Component({
5
+ selector: 'gn-ui-image-overlay-preview',
6
+ templateUrl: './image-overlay-preview.component.html',
7
+ styleUrls: ['./image-overlay-preview.component.css'],
8
+ })
9
+ export class ImageOverlayPreviewComponent {
10
+ @Input() imageUrl: string
11
+ @Output() isPlaceholderShown = new EventEmitter<boolean>()
12
+ openLightbox(src: string) {
13
+ basicLightbox.create(`<img src="${src}"/>`).show()
14
+ }
15
+ }