geonetwork-ui 2.3.0-dev.61774f7d → 2.3.0-dev.6b7e19cb

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 (152) hide show
  1. package/esm2022/libs/feature/dataviz/src/lib/service/data.service.mjs +35 -2
  2. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.mjs +64 -0
  3. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-resource-updated/form-field-resource-updated.component.mjs +15 -0
  4. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.mjs +20 -6
  5. package/esm2022/libs/feature/editor/src/lib/components/wizard-field/wizard-field.component.mjs +1 -1
  6. package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +16 -1
  7. package/esm2022/libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.mjs +73 -20
  8. package/esm2022/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.mjs +1 -1
  9. package/esm2022/libs/feature/map/src/lib/add-layer-from-wms/add-layer-from-wms.component.mjs +1 -1
  10. package/esm2022/libs/feature/map/src/lib/map-context/map-context.model.mjs +1 -1
  11. package/esm2022/libs/feature/map/src/lib/map-context/map-context.service.mjs +29 -8
  12. package/esm2022/libs/feature/record/src/lib/map-view/map-view.component.mjs +3 -2
  13. package/esm2022/libs/feature/record/src/lib/state/mdview.facade.mjs +1 -1
  14. package/esm2022/libs/feature/search/src/index.mjs +2 -1
  15. package/esm2022/libs/feature/search/src/lib/utils/service/fields.service.mjs +1 -1
  16. package/esm2022/libs/ui/elements/src/lib/api-card/api-card.component.mjs +3 -2
  17. package/esm2022/libs/ui/elements/src/lib/downloads-list/downloads-list.component.mjs +2 -2
  18. package/esm2022/libs/ui/elements/src/lib/link-card/link-card.component.mjs +16 -3
  19. package/esm2022/libs/ui/elements/src/lib/record-api-form/record-api-form.component.mjs +77 -15
  20. package/esm2022/libs/ui/elements/src/lib/ui-elements.module.mjs +1 -6
  21. package/esm2022/libs/ui/inputs/src/index.mjs +16 -15
  22. package/esm2022/libs/ui/inputs/src/lib/date-picker/date-picker.component.mjs +22 -0
  23. package/esm2022/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.mjs +7 -5
  24. package/esm2022/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.mjs +17 -8
  25. package/esm2022/libs/ui/inputs/src/lib/previous-next-buttons/previous-next-buttons.component.mjs +29 -0
  26. package/esm2022/libs/ui/inputs/src/lib/text-input/text-input.component.mjs +5 -3
  27. package/esm2022/libs/ui/inputs/src/lib/ui-inputs.module.mjs +11 -9
  28. package/esm2022/libs/ui/layout/src/index.mjs +2 -1
  29. package/esm2022/libs/ui/layout/src/lib/block-list/block-list.component.mjs +76 -0
  30. package/esm2022/libs/ui/layout/src/lib/carousel/carousel.component.mjs +42 -18
  31. package/esm2022/libs/ui/layout/src/lib/ui-layout.module.mjs +3 -8
  32. package/esm2022/libs/util/shared/src/lib/links/link-classifier.service.mjs +2 -2
  33. package/esm2022/libs/util/shared/src/lib/links/link-utils.mjs +22 -1
  34. package/esm2022/translations/de.json +20 -14
  35. package/esm2022/translations/en.json +20 -14
  36. package/esm2022/translations/es.json +20 -14
  37. package/esm2022/translations/fr.json +20 -14
  38. package/esm2022/translations/it.json +20 -14
  39. package/esm2022/translations/nl.json +20 -14
  40. package/esm2022/translations/pt.json +20 -14
  41. package/fesm2022/geonetwork-ui.mjs +1336 -864
  42. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  43. package/libs/feature/dataviz/src/lib/service/data.service.d.ts +3 -1
  44. package/libs/feature/dataviz/src/lib/service/data.service.d.ts.map +1 -1
  45. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.d.ts +39 -0
  46. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.d.ts.map +1 -0
  47. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-resource-updated/form-field-resource-updated.component.d.ts +8 -0
  48. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-resource-updated/form-field-resource-updated.component.d.ts.map +1 -0
  49. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts +3 -0
  50. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts.map +1 -1
  51. package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
  52. package/libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.d.ts +10 -5
  53. package/libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.d.ts.map +1 -1
  54. package/libs/feature/map/src/lib/map-context/map-context.model.d.ts +1 -0
  55. package/libs/feature/map/src/lib/map-context/map-context.model.d.ts.map +1 -1
  56. package/libs/feature/map/src/lib/map-context/map-context.service.d.ts.map +1 -1
  57. package/libs/feature/record/src/lib/map-view/map-view.component.d.ts.map +1 -1
  58. package/libs/feature/record/src/lib/state/mdview.facade.d.ts.map +1 -1
  59. package/libs/feature/search/src/index.d.ts +1 -0
  60. package/libs/feature/search/src/index.d.ts.map +1 -1
  61. package/libs/feature/search/src/lib/utils/service/fields.service.d.ts +3 -3
  62. package/libs/feature/search/src/lib/utils/service/fields.service.d.ts.map +1 -1
  63. package/libs/ui/elements/src/lib/api-card/api-card.component.d.ts.map +1 -1
  64. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts +1 -1
  65. package/libs/ui/elements/src/lib/link-card/link-card.component.d.ts +3 -1
  66. package/libs/ui/elements/src/lib/link-card/link-card.component.d.ts.map +1 -1
  67. package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts +12 -3
  68. package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts.map +1 -1
  69. package/libs/ui/elements/src/lib/ui-elements.module.d.ts +29 -30
  70. package/libs/ui/elements/src/lib/ui-elements.module.d.ts.map +1 -1
  71. package/libs/ui/inputs/src/index.d.ts +15 -14
  72. package/libs/ui/inputs/src/index.d.ts.map +1 -1
  73. package/libs/ui/inputs/src/lib/date-picker/date-picker.component.d.ts +9 -0
  74. package/libs/ui/inputs/src/lib/date-picker/date-picker.component.d.ts.map +1 -0
  75. package/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.d.ts +1 -1
  76. package/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.d.ts.map +1 -1
  77. package/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.d.ts +1 -1
  78. package/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.d.ts.map +1 -1
  79. package/libs/ui/inputs/src/lib/previous-next-buttons/previous-next-buttons.component.d.ts +12 -0
  80. package/libs/ui/inputs/src/lib/previous-next-buttons/previous-next-buttons.component.d.ts.map +1 -0
  81. package/libs/ui/inputs/src/lib/text-input/text-input.component.d.ts +2 -1
  82. package/libs/ui/inputs/src/lib/text-input/text-input.component.d.ts.map +1 -1
  83. package/libs/ui/inputs/src/lib/ui-inputs.module.d.ts +35 -35
  84. package/libs/ui/layout/src/index.d.ts +1 -0
  85. package/libs/ui/layout/src/index.d.ts.map +1 -1
  86. package/libs/ui/layout/src/lib/block-list/block-list.component.d.ts +25 -0
  87. package/libs/ui/layout/src/lib/block-list/block-list.component.d.ts.map +1 -0
  88. package/libs/ui/layout/src/lib/carousel/carousel.component.d.ts +13 -6
  89. package/libs/ui/layout/src/lib/carousel/carousel.component.d.ts.map +1 -1
  90. package/libs/ui/layout/src/lib/ui-layout.module.d.ts +4 -5
  91. package/libs/ui/layout/src/lib/ui-layout.module.d.ts.map +1 -1
  92. package/libs/util/shared/src/lib/links/link-utils.d.ts +18 -0
  93. package/libs/util/shared/src/lib/links/link-utils.d.ts.map +1 -1
  94. package/package.json +1 -1
  95. package/src/libs/common/fixtures/src/lib/link.fixtures.ts +8 -0
  96. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +2 -2
  97. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +51 -1
  98. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.css +0 -0
  99. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.html +8 -0
  100. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-license/form-field-license.component.ts +64 -0
  101. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-resource-updated/form-field-resource-updated.component.css +0 -0
  102. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-resource-updated/form-field-resource-updated.component.html +4 -0
  103. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-resource-updated/form-field-resource-updated.component.ts +15 -0
  104. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +33 -33
  105. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +16 -0
  106. package/src/libs/feature/editor/src/lib/fields.config.ts +15 -0
  107. package/src/libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.css +7 -0
  108. package/src/libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.html +32 -18
  109. package/src/libs/feature/map/src/lib/add-layer-from-ogc-api/add-layer-from-ogc-api.component.ts +72 -17
  110. package/src/libs/feature/map/src/lib/map-context/map-context.model.ts +1 -0
  111. package/src/libs/feature/map/src/lib/map-context/map-context.service.ts +26 -8
  112. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +2 -1
  113. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +0 -1
  114. package/src/libs/feature/search/src/index.ts +1 -0
  115. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +2 -2
  116. package/src/libs/ui/elements/src/lib/api-card/api-card.component.ts +2 -1
  117. package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.ts +1 -1
  118. package/src/libs/ui/elements/src/lib/link-card/link-card.component.html +38 -20
  119. package/src/libs/ui/elements/src/lib/link-card/link-card.component.ts +12 -0
  120. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +26 -10
  121. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +87 -8
  122. package/src/libs/ui/elements/src/lib/ui-elements.module.ts +0 -2
  123. package/src/libs/ui/inputs/src/index.ts +15 -14
  124. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.css +3 -0
  125. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.html +11 -0
  126. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +16 -0
  127. package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +8 -1
  128. package/src/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.ts +13 -0
  129. package/src/libs/ui/inputs/src/lib/previous-next-buttons/previous-next-buttons.component.css +6 -0
  130. package/src/libs/ui/inputs/src/lib/previous-next-buttons/previous-next-buttons.component.html +26 -0
  131. package/src/libs/ui/inputs/src/lib/previous-next-buttons/previous-next-buttons.component.ts +32 -0
  132. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.html +1 -0
  133. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.ts +1 -0
  134. package/src/libs/ui/inputs/src/lib/ui-inputs.module.ts +2 -2
  135. package/src/libs/ui/layout/src/index.ts +1 -0
  136. package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +23 -0
  137. package/src/libs/ui/layout/src/lib/block-list/block-list.component.html +20 -0
  138. package/src/libs/ui/layout/src/lib/block-list/block-list.component.ts +84 -0
  139. package/src/libs/ui/layout/src/lib/carousel/carousel.component.css +8 -4
  140. package/src/libs/ui/layout/src/lib/carousel/carousel.component.html +4 -4
  141. package/src/libs/ui/layout/src/lib/carousel/carousel.component.ts +45 -15
  142. package/src/libs/ui/layout/src/lib/ui-layout.module.ts +0 -2
  143. package/src/libs/util/shared/src/lib/links/link-classifier.service.ts +1 -1
  144. package/src/libs/util/shared/src/lib/links/link-utils.ts +21 -0
  145. package/translations/de.json +20 -14
  146. package/translations/en.json +20 -14
  147. package/translations/es.json +20 -14
  148. package/translations/fr.json +20 -14
  149. package/translations/it.json +20 -14
  150. package/translations/nl.json +20 -14
  151. package/translations/pt.json +20 -14
  152. package/translations/sk.json +20 -14
@@ -4,8 +4,7 @@
4
4
  (valueChange)="urlChange.next($event)"
5
5
  [hint]="'map.ogc.urlInput.hint' | translate"
6
6
  class="w-96"
7
- >
8
- </gn-ui-text-input>
7
+ ></gn-ui-text-input>
9
8
  </div>
10
9
 
11
10
  <div *ngIf="errorMessage" class="text-red-500 mt-2">
@@ -16,21 +15,36 @@
16
15
  <p class="loading-message" translate>map.loading.service</p>
17
16
  </div>
18
17
 
19
- <div *ngIf="!loading && layers.length > 0">
20
- <h2 class="font-bold" translate>map.layers.available</h2>
21
- <ng-container *ngFor="let layer of layers">
22
- <div class="flex items-center justify-between my-2 layer-item-tree">
23
- <p class="max-w-xs overflow-hidden overflow-ellipsis whitespace-nowrap">
24
- {{ layer }}
25
- </p>
26
- <gn-ui-button
27
- class="layer-add-btn"
28
- type="primary"
29
- (buttonClick)="addLayer(layer)"
30
- extraClass="text-sm !px-2 !py-1"
31
- translate
32
- ><span translate> map.layer.add </span></gn-ui-button
18
+ <ng-container *ngFor="let layer of layers">
19
+ <div
20
+ *ngIf="shouldDisplayLayer(layer)"
21
+ class="flex items-center justify-between my-2 layer-item-tree"
22
+ >
23
+ <div class="flex flex-col items-start w-full">
24
+ <p
25
+ class="max-w-xs overflow-hidden overflow-ellipsis whitespace-nowrap"
26
+ [title]="layer.name"
33
27
  >
28
+ {{ layer.name }}
29
+ </p>
30
+ <div class="flex justify-between items-center w-full">
31
+ <gn-ui-dropdown-selector
32
+ [title]="'Add Layer As' | translate"
33
+ [choices]="getLayerChoices(layer)"
34
+ (selectValue)="onLayerTypeSelect(layer.name, $event)"
35
+ [selected]="selectedLayerTypes[layer.name]"
36
+ extraBtnClass="w-6 h-5 !text-sm !px-2 !py-1"
37
+ ></gn-ui-dropdown-selector>
38
+ <gn-ui-button
39
+ class="layer-add-btn"
40
+ type="primary"
41
+ (buttonClick)="addLayer(layer.name, selectedLayerTypes[layer.name])"
42
+ extraClass="text-sm !px-2 !py-1"
43
+ translate
44
+ >
45
+ <span translate>map.layer.add</span>
46
+ </gn-ui-button>
47
+ </div>
34
48
  </div>
35
- </ng-container>
36
- </div>
49
+ </div>
50
+ </ng-container>
@@ -14,7 +14,7 @@ import {
14
14
  MapContextLayerTypeEnum,
15
15
  } from '../map-context/map-context.model'
16
16
  import { TranslateModule } from '@ngx-translate/core'
17
- import { UiInputsModule } from '../../../../../../libs/ui/inputs/src'
17
+ import { DropdownChoice, UiInputsModule } from '../../../../../../libs/ui/inputs/src'
18
18
  import { CommonModule } from '@angular/common'
19
19
  import { MapLayer } from '../+state/map.models'
20
20
 
@@ -30,18 +30,16 @@ export class AddLayerFromOgcApiComponent implements OnInit {
30
30
  @Output() layerAdded = new EventEmitter<MapLayer>()
31
31
 
32
32
  urlChange = new Subject<string>()
33
- layerUrl = ''
34
33
  loading = false
35
- layers: string[] = []
36
- ogcEndpoint: OgcApiEndpoint = null
34
+ layers: any[] = []
37
35
  errorMessage: string | null = null
36
+ selectedLayerTypes: { [key: string]: DropdownChoice['value'] } = {}
38
37
 
39
38
  constructor(private changeDetectorRef: ChangeDetectorRef) {}
40
39
 
41
40
  ngOnInit() {
42
41
  this.urlChange.pipe(debounceTime(700)).subscribe(() => {
43
42
  this.loadLayers()
44
- this.changeDetectorRef.detectChanges() // manually trigger change detection
45
43
  })
46
44
  }
47
45
 
@@ -49,14 +47,13 @@ export class AddLayerFromOgcApiComponent implements OnInit {
49
47
  this.errorMessage = null
50
48
  try {
51
49
  this.loading = true
52
- if (this.ogcUrl.trim() === '') {
50
+ if (!this.ogcUrl.trim()) {
53
51
  this.layers = []
54
52
  return
55
53
  }
56
- this.ogcEndpoint = await new OgcApiEndpoint(this.ogcUrl)
57
-
58
- // Currently only supports feature collections
59
- this.layers = await this.ogcEndpoint.featureCollections
54
+ const ogcEndpoint = await new OgcApiEndpoint(this.ogcUrl)
55
+ this.layers = await ogcEndpoint.allCollections
56
+ this.setDefaultLayerTypes()
60
57
  } catch (error) {
61
58
  const err = error as Error
62
59
  this.layers = []
@@ -67,14 +64,72 @@ export class AddLayerFromOgcApiComponent implements OnInit {
67
64
  }
68
65
  }
69
66
 
70
- async addLayer(layer: string) {
71
- this.layerUrl = await this.ogcEndpoint.getCollectionItemsUrl(layer)
67
+ setDefaultLayerTypes() {
68
+ this.layers.forEach((layer) => {
69
+ const choices = this.getLayerChoices(layer)
70
+ if (choices.length > 0) {
71
+ this.selectedLayerTypes[layer.name] = choices[0].value
72
+ }
73
+ })
74
+ }
72
75
 
73
- const layerToAdd: MapContextLayerModel = {
74
- name: layer,
75
- url: this.layerUrl,
76
- type: MapContextLayerTypeEnum.OGCAPI,
76
+ getLayerChoices(layer: any) {
77
+ const choices = []
78
+ if (layer.hasRecords) {
79
+ choices.push({ label: 'Records', value: 'record' })
80
+ }
81
+ if (layer.hasFeatures) {
82
+ choices.push({ label: 'Features', value: 'features' })
83
+ }
84
+ if (layer.hasVectorTiles) {
85
+ choices.push({ label: 'Vector Tiles', value: 'vectorTiles' })
86
+ }
87
+ if (layer.hasMapTiles) {
88
+ choices.push({ label: 'Map Tiles', value: 'mapTiles' })
89
+ }
90
+ return choices
91
+ }
92
+
93
+ shouldDisplayLayer(layer: any) {
94
+ return (
95
+ layer.hasRecords ||
96
+ layer.hasFeatures ||
97
+ layer.hasVectorTiles ||
98
+ layer.hasMapTiles
99
+ )
100
+ }
101
+
102
+ onLayerTypeSelect(layerName: string, selectedType: any) {
103
+ this.selectedLayerTypes[layerName] = selectedType
104
+ ? selectedType
105
+ : this.getLayerChoices(layerName)[0]?.value
106
+ }
107
+
108
+ async addLayer(layer: string, layerType: any) {
109
+ try {
110
+ const ogcEndpoint = await new OgcApiEndpoint(this.ogcUrl)
111
+ let layerUrl: string
112
+
113
+ if (layerType === 'vectorTiles') {
114
+ layerUrl = await ogcEndpoint.getVectorTilesetUrl(layer)
115
+ } else if (layerType === 'mapTiles') {
116
+ layerUrl = await ogcEndpoint.getMapTilesetUrl(layer)
117
+ } else {
118
+ layerUrl = await ogcEndpoint.getCollectionItemsUrl(layer, {
119
+ outputFormat: 'json',
120
+ })
121
+ }
122
+
123
+ const layerToAdd: MapContextLayerModel = {
124
+ name: layer,
125
+ url: layerUrl,
126
+ type: MapContextLayerTypeEnum.OGCAPI,
127
+ layerType: layerType,
128
+ }
129
+ this.layerAdded.emit({ ...layerToAdd, title: layer })
130
+ } catch (error) {
131
+ const err = error as Error
132
+ console.error('Error adding layer:', err.message)
77
133
  }
78
- this.layerAdded.emit({ ...layerToAdd, title: layer })
79
134
  }
80
135
  }
@@ -38,6 +38,7 @@ export interface MapContextLayerOgcapiModel {
38
38
  type: 'ogcapi'
39
39
  url: string
40
40
  name: string
41
+ layerType: 'feature' | 'vectorTiles' | 'mapTiles' | 'record'
41
42
  }
42
43
 
43
44
  interface LayerXyzModel {
@@ -25,6 +25,10 @@ import WMTS from 'ol/source/WMTS'
25
25
  import { Geometry } from 'ol/geom'
26
26
  import Feature from 'ol/Feature'
27
27
  import { WfsEndpoint, WmtsEndpoint } from '@camptocamp/ogc-client'
28
+ import OGCVectorTile from 'ol/source/OGCVectorTile.js'
29
+ import { MVT } from 'ol/format'
30
+ import VectorTileLayer from 'ol/layer/VectorTile'
31
+ import OGCMapTile from 'ol/source/OGCMapTile.js'
28
32
 
29
33
  export const DEFAULT_BASELAYER_CONTEXT: MapContextLayerXyzModel = {
30
34
  type: MapContextLayerTypeEnum.XYZ,
@@ -78,14 +82,28 @@ export class MapContextService {
78
82
  const style = this.styleService.styles.default
79
83
  switch (type) {
80
84
  case MapContextLayerTypeEnum.OGCAPI:
81
- return new VectorLayer({
82
- source: new VectorSource({
83
- format: new GeoJSON(),
84
- url: layerModel.url,
85
- }),
86
- style,
87
- })
88
-
85
+ if (layerModel.layerType === 'vectorTiles') {
86
+ return new VectorTileLayer({
87
+ source: new OGCVectorTile({
88
+ url: layerModel.url,
89
+ format: new MVT(),
90
+ }),
91
+ })
92
+ } else if (layerModel.layerType === 'mapTiles') {
93
+ return new TileLayer({
94
+ source: new OGCMapTile({
95
+ url: layerModel.url,
96
+ }),
97
+ })
98
+ } else {
99
+ return new VectorLayer({
100
+ source: new VectorSource({
101
+ format: new GeoJSON(),
102
+ url: layerModel.url,
103
+ }),
104
+ style,
105
+ })
106
+ }
89
107
  case MapContextLayerTypeEnum.XYZ:
90
108
  return new TileLayer({
91
109
  source: new XYZ({
@@ -196,7 +196,8 @@ export class MapViewComponent implements OnInit, OnDestroy {
196
196
  } else if (
197
197
  (link.type === 'service' &&
198
198
  (link.accessServiceProtocol === 'wfs' ||
199
- link.accessServiceProtocol === 'esriRest')) ||
199
+ link.accessServiceProtocol === 'esriRest' ||
200
+ link.accessServiceProtocol === 'ogcFeatures')) ||
200
201
  link.type === 'download'
201
202
  ) {
202
203
  return this.dataService.readAsGeoJson(link).pipe(
@@ -8,7 +8,6 @@ import { DatavizConfigurationModel } from '../../../../../../libs/common/domain/
8
8
  import {
9
9
  CatalogRecord,
10
10
  UserFeedback,
11
- UserFeedbackViewModel,
12
11
  } from '../../../../../../libs/common/domain/src/lib/model/record'
13
12
  import { AvatarServiceInterface } from '../../../../../../libs/api/repository/src'
14
13
 
@@ -8,6 +8,7 @@ export * from './lib/state/effects'
8
8
  export * from './lib/state/reducer'
9
9
  export * from './lib/utils/service/search.service'
10
10
  export * from './lib/utils/service/fields.service'
11
+ export * from './lib/utils/service/fields'
11
12
  export * from './lib/results-list/results-list.container.component'
12
13
  export * from './lib/filter-dropdown/filter-dropdown.component'
13
14
  export * from './lib/constants'
@@ -36,7 +36,7 @@ marker('search.filters.contact')
36
36
  providedIn: 'root',
37
37
  })
38
38
  export class FieldsService {
39
- private fields = {
39
+ protected fields = {
40
40
  publisher: new OrganizationSearchField(this.injector),
41
41
  format: new SimpleSearchField('format', this.injector, 'asc'),
42
42
  resourceType: new TranslatedSearchField(
@@ -76,7 +76,7 @@ export class FieldsService {
76
76
  return Object.keys(this.fields)
77
77
  }
78
78
 
79
- constructor(private injector: Injector) {}
79
+ constructor(protected injector: Injector) {}
80
80
 
81
81
  getAvailableValues(fieldName: string) {
82
82
  if (this.supportedFields.indexOf(fieldName) === -1)
@@ -26,7 +26,8 @@ export class ApiCardComponent implements OnInit, OnChanges {
26
26
 
27
27
  ngOnInit() {
28
28
  this.displayApiFormButton =
29
- this.link.accessServiceProtocol === 'ogcFeatures' ? true : false
29
+ this.link.accessServiceProtocol === 'ogcFeatures' ||
30
+ this.link.accessServiceProtocol === 'wfs'
30
31
  }
31
32
 
32
33
  ngOnChanges(changes: SimpleChanges) {
@@ -95,6 +95,6 @@ export class DownloadsListComponent {
95
95
  }
96
96
 
97
97
  isFromWfs(link: DatasetDistribution) {
98
- return link.type === 'service' && link.accessServiceProtocol === 'wfs'
98
+ return link.type === 'download' && link.accessServiceProtocol === 'wfs'
99
99
  }
100
100
  }
@@ -1,25 +1,43 @@
1
1
  <a
2
2
  [href]="link.url"
3
3
  target="_blank"
4
- class="flex flex-col justify-between group h-40 grow py-5 px-5 bg-white rounded border-gray-300 filter card-shadow overflow-hidden lg:w-80"
4
+ class="flex flex-col justify-between group grow py-5 px-5 bg-white rounded border-gray-300 filter card-shadow overflow-hidden"
5
+ [ngClass]="{ 'h-40': !compact }"
6
+ [title]="title"
5
7
  >
6
- <div class="max-h-24 overflow-hidden text-ellipsis">
7
- <p
8
- class="font-title font-medium text-21 text-black break-words mb-1 line-clamp-2"
9
- >
10
- {{ link.name }}
11
- </p>
12
- <p class="font-medium text-sm break-words">
13
- {{ link.description }}
14
- </p>
15
- <p
16
- *ngIf="!link.name && !link.description"
17
- class="font-medium text-sm break-words truncate"
18
- >
19
- {{ link.url }}
20
- </p>
21
- </div>
22
- <div>
23
- <mat-icon class="material-symbols-outlined card-icon">open_in_new</mat-icon>
24
- </div>
8
+ <ng-container *ngIf="!compact; else compactTpl">
9
+ <div class="max-h-24 overflow-hidden text-ellipsis">
10
+ <p
11
+ class="font-title font-medium text-21 text-black break-words mb-1 line-clamp-2"
12
+ >
13
+ {{ link.name }}
14
+ </p>
15
+ <p class="font-medium text-sm break-words">
16
+ {{ link.description }}
17
+ </p>
18
+ <p
19
+ *ngIf="!link.name && !link.description"
20
+ class="font-medium text-sm break-words truncate"
21
+ >
22
+ {{ link.url }}
23
+ </p>
24
+ </div>
25
+ <div>
26
+ <mat-icon class="material-symbols-outlined card-icon"
27
+ >open_in_new</mat-icon
28
+ >
29
+ </div>
30
+ </ng-container>
31
+ <ng-template #compactTpl>
32
+ <div class="flex items-center justify-between gap-4">
33
+ <p
34
+ class="overflow-hidden font-title font-medium text-21 text-black text-ellipsis whitespace-nowrap"
35
+ >
36
+ {{ link.name || link.description }}
37
+ </p>
38
+ <mat-icon class="material-symbols-outlined card-icon flex-shrink-0"
39
+ >open_in_new</mat-icon
40
+ >
41
+ </div>
42
+ </ng-template>
25
43
  </a>
@@ -1,12 +1,24 @@
1
1
  import { Component, ChangeDetectionStrategy, Input } from '@angular/core'
2
2
  import { DatasetDistribution } from '../../../../../../libs/common/domain/src/lib/model/record'
3
+ import { MatIconModule } from '@angular/material/icon'
4
+ import { CommonModule } from '@angular/common'
3
5
 
4
6
  @Component({
5
7
  selector: 'gn-ui-link-card',
6
8
  templateUrl: './link-card.component.html',
7
9
  styleUrls: ['./link-card.component.css'],
8
10
  changeDetection: ChangeDetectionStrategy.OnPush,
11
+ standalone: true,
12
+ imports: [CommonModule, MatIconModule],
9
13
  })
10
14
  export class LinkCardComponent {
11
15
  @Input() link: DatasetDistribution
16
+ @Input() compact = false
17
+
18
+ get title() {
19
+ if (this.link.name && this.link.description) {
20
+ return `${this.link.name} | ${this.link.description}`
21
+ }
22
+ return this.link.name || this.link.description || ''
23
+ }
12
24
  }
@@ -37,15 +37,31 @@
37
37
  </div>
38
38
  </div>
39
39
  </div>
40
- <div class="flex flex-col gap-3">
41
- <p class="text-sm" translate>record.metadata.api.form.offset</p>
42
- <gn-ui-text-input
43
- class="w-20"
44
- [value]="offset$ | async"
45
- (valueChange)="setOffset($event)"
46
- hint=""
47
- >
48
- </gn-ui-text-input>
40
+ <div class="flex flex-col gap-3 relative">
41
+ <p class="text-sm" [class.text-gray-600]="!supportOffset" translate>
42
+ record.metadata.api.form.offset
43
+ </p>
44
+ <div class="flex items-center">
45
+ <gn-ui-text-input
46
+ class="w-20"
47
+ [value]="offset$ | async"
48
+ [disabled]="!supportOffset"
49
+ (valueChange)="supportOffset ? setOffset($event) : null"
50
+ hint=""
51
+ >
52
+ </gn-ui-text-input>
53
+ <div
54
+ *ngIf="!supportOffset"
55
+ class="flex items-center gap-2 text-orange-500 z-10 ml-3"
56
+ >
57
+ <span
58
+ class="material-symbols-outlined"
59
+ matTooltip="Not supported on this service"
60
+ >
61
+ warning
62
+ </span>
63
+ </div>
64
+ </div>
49
65
  </div>
50
66
  <div class="flex flex-col gap-3">
51
67
  <p class="text-sm" translate>record.metadata.api.form.type</p>
@@ -55,7 +71,7 @@
55
71
  extraBtnClass="secondary min-w-full !w-40 !text-black"
56
72
  [showTitle]="false"
57
73
  class="text-black"
58
- [choices]="formatsList"
74
+ [choices]="outputFormats"
59
75
  (selectValue)="setFormat($event)"
60
76
  [selected]="format$ | async"
61
77
  ></gn-ui-dropdown-selector>
@@ -1,6 +1,11 @@
1
1
  import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
2
- import { DatasetServiceDistribution } from '../../../../../../libs/common/domain/src/lib/model/record'
3
- import { BehaviorSubject, combineLatest, map } from 'rxjs'
2
+ import { OgcApiEndpoint, WfsEndpoint } from '@camptocamp/ogc-client'
3
+ import {
4
+ DatasetServiceDistribution,
5
+ ServiceProtocol,
6
+ } from '../../../../../../libs/common/domain/src/lib/model/record'
7
+ import { mimeTypeToFormat } from '../../../../../../libs/util/shared/src'
8
+ import { BehaviorSubject, combineLatest, map, switchMap } from 'rxjs'
4
9
 
5
10
  const DEFAULT_PARAMS = {
6
11
  OFFSET: '',
@@ -15,19 +20,27 @@ const DEFAULT_PARAMS = {
15
20
  })
16
21
  export class RecordApiFormComponent {
17
22
  @Input() set apiLink(value: DatasetServiceDistribution) {
18
- this.apiBaseUrl = value ? value.url.href : undefined
23
+ this.outputFormats = [{ value: 'json', label: 'JSON' }]
24
+ this.accessServiceProtocol = value ? value.accessServiceProtocol : undefined
25
+ this.apiFeatureType = value ? value.name : undefined
26
+ if (value) {
27
+ this.apiBaseUrl = value.url.href
28
+ this.parseOutputFormats()
29
+ }
19
30
  this.resetUrl()
20
31
  }
32
+
21
33
  offset$ = new BehaviorSubject('')
22
34
  limit$ = new BehaviorSubject('')
23
35
  format$ = new BehaviorSubject('')
24
36
  apiBaseUrl: string
25
- formatsList = [
26
- { label: 'JSON', value: 'json' },
27
- { label: 'CSV', value: 'csv' },
28
- ]
37
+ apiFeatureType: string
38
+ supportOffset = true
39
+ accessServiceProtocol: ServiceProtocol | undefined
40
+ outputFormats = [{ value: 'json', label: 'JSON' }]
41
+
29
42
  apiQueryUrl$ = combineLatest([this.offset$, this.limit$, this.format$]).pipe(
30
- map(([offset, limit, format]) => {
43
+ switchMap(async ([offset, limit, format]) => {
31
44
  let outputUrl
32
45
  if (this.apiBaseUrl) {
33
46
  const url = new URL(this.apiBaseUrl)
@@ -41,6 +54,20 @@ export class RecordApiFormComponent {
41
54
  }
42
55
  outputUrl = url.toString()
43
56
  }
57
+
58
+ if (this.accessServiceProtocol === 'wfs') {
59
+ const wfsEndpoint = new WfsEndpoint(this.apiBaseUrl)
60
+ if (await wfsEndpoint.isReady()) {
61
+ const options = {
62
+ outputFormat: format,
63
+ startIndex: Number(offset),
64
+ }
65
+ if (limit !== '-1') {
66
+ options['maxFeatures'] = Number(limit)
67
+ }
68
+ outputUrl = wfsEndpoint.getFeatureUrl(this.apiFeatureType, options)
69
+ }
70
+ }
44
71
  return outputUrl
45
72
  })
46
73
  )
@@ -70,4 +97,56 @@ export class RecordApiFormComponent {
70
97
  this.limit$.next(DEFAULT_PARAMS.LIMIT)
71
98
  this.format$.next(DEFAULT_PARAMS.FORMAT)
72
99
  }
100
+
101
+ parseOutputFormats() {
102
+ const apiUrl =
103
+ this.apiBaseUrl.slice(-1) === '?'
104
+ ? this.apiBaseUrl.slice(0, -1)
105
+ : this.apiBaseUrl
106
+
107
+ this.getOutputFormats(apiUrl, this.accessServiceProtocol).then(
108
+ (outputFormats) => {
109
+ let formatsList = []
110
+ if ('itemFormats' in outputFormats) {
111
+ formatsList = this.mapFormats(outputFormats.itemFormats)
112
+ } else if ('outputFormats' in outputFormats) {
113
+ formatsList = this.mapFormats(outputFormats.outputFormats)
114
+ }
115
+ this.outputFormats = this.outputFormats.concat(
116
+ formatsList.filter(Boolean)
117
+ )
118
+ this.outputFormats = this.outputFormats
119
+ .filter(
120
+ (format, index, self) =>
121
+ index === self.findIndex((t) => t.value === format.value)
122
+ )
123
+ .sort((a, b) => a.label.localeCompare(b.label))
124
+ }
125
+ )
126
+ }
127
+
128
+ mapFormats(formats: any[]) {
129
+ return formats.map((format) => {
130
+ const normalizedFormat = mimeTypeToFormat(format)
131
+ if (normalizedFormat) {
132
+ return {
133
+ label: normalizedFormat.toUpperCase(),
134
+ value: normalizedFormat,
135
+ }
136
+ }
137
+ return null
138
+ })
139
+ }
140
+
141
+ async getOutputFormats(url: string, accessServiceProtocol: string) {
142
+ if (accessServiceProtocol === 'wfs') {
143
+ const endpoint = await new WfsEndpoint(url).isReady()
144
+ this.supportOffset = endpoint.supportsStartIndex()
145
+ return endpoint.getServiceInfo()
146
+ } else {
147
+ const endpoint = await new OgcApiEndpoint(url)
148
+ const firstCollection = (await endpoint.featureCollections)[0]
149
+ return endpoint.getCollectionInfo(firstCollection)
150
+ }
151
+ }
73
152
  }
@@ -57,7 +57,6 @@ import { TimeSincePipe } from './user-feedback-item/time-since.pipe'
57
57
  DownloadItemComponent,
58
58
  DownloadsListComponent,
59
59
  ApiCardComponent,
60
- LinkCardComponent,
61
60
  RelatedRecordCardComponent,
62
61
  MetadataContactComponent,
63
62
  MetadataCatalogComponent,
@@ -80,7 +79,6 @@ import { TimeSincePipe } from './user-feedback-item/time-since.pipe'
80
79
  DownloadItemComponent,
81
80
  DownloadsListComponent,
82
81
  ApiCardComponent,
83
- LinkCardComponent,
84
82
  RelatedRecordCardComponent,
85
83
  MetadataContactComponent,
86
84
  MetadataCatalogComponent,
@@ -1,21 +1,22 @@
1
- export * from './lib/dropdown-selector/dropdown-selector.component'
2
- export * from './lib/dropdown-selector/dropdown-selector.model'
3
- export * from './lib/dropdown-multiselect/dropdown-multiselect.component'
4
- export * from './lib/dropdown-multiselect/dropdown-multiselect.model'
5
- export * from './lib/text-input/text-input.component'
6
- export * from './lib/chips-input/chips-input.component'
7
- export * from './lib/text-area/text-area.component'
8
1
  export * from './lib/autocomplete/autocomplete.component'
9
- export * from './lib/star-toggle/star-toggle.component'
10
2
  export * from './lib/button/button.component'
11
- export * from './lib/viewport-intersector/viewport-intersector.component'
12
3
  export * from './lib/check-toggle/check-toggle.component'
13
- export * from './lib/ui-inputs.module'
4
+ export * from './lib/checkbox/checkbox.component'
5
+ export * from './lib/chips-input/chips-input.component'
14
6
  export * from './lib/copy-text-button/copy-text-button.component'
7
+ export * from './lib/date-picker/date-picker.component'
8
+ export * from './lib/date-range-picker/date-range-picker.component'
15
9
  export * from './lib/drag-and-drop-file-input/drag-and-drop-file-input.component'
10
+ export * from './lib/dropdown-multiselect/dropdown-multiselect.component'
11
+ export * from './lib/dropdown-multiselect/dropdown-multiselect.model'
12
+ export * from './lib/dropdown-selector/dropdown-selector.component'
13
+ export * from './lib/dropdown-selector/dropdown-selector.model'
14
+ export * from './lib/editable-label/editable-label.directive'
16
15
  export * from './lib/navigation-button/navigation-button.component'
17
- export * from './lib/viewport-intersector/viewport-intersector.component'
18
- export * from './lib/checkbox/checkbox.component'
19
16
  export * from './lib/search-input/search-input.component'
20
- export * from './lib/date-range-picker/date-range-picker.component'
21
- export * from './lib/editable-label/editable-label.directive'
17
+ export * from './lib/star-toggle/star-toggle.component'
18
+ export * from './lib/text-area/text-area.component'
19
+ export * from './lib/text-input/text-input.component'
20
+ export * from './lib/ui-inputs.module'
21
+ export * from './lib/viewport-intersector/viewport-intersector.component'
22
+ export * from './lib/previous-next-buttons/previous-next-buttons.component'
@@ -0,0 +1,3 @@
1
+ mat-datepicker-toggle {
2
+ @apply text-primary;
3
+ }
@@ -0,0 +1,11 @@
1
+ <div
2
+ class="flex items-center justify-between pl-3 rounded-lg border border-gray-300 bg-white"
3
+ >
4
+ <input
5
+ [matDatepicker]="picker"
6
+ [value]="date"
7
+ (dateChange)="dateChange.emit($event.value)"
8
+ />
9
+ <mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
10
+ </div>
11
+ <mat-datepicker #picker></mat-datepicker>