geonetwork-ui 2.3.0-dev.ff2a9db7 → 2.3.0

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 (98) hide show
  1. package/esm2022/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.mjs +2 -2
  2. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.mjs +9 -7
  3. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +22 -7
  4. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +14 -3
  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 +14 -2
  7. package/esm2022/libs/feature/map/src/lib/map-context/map-context.model.mjs +1 -1
  8. package/esm2022/libs/feature/map/src/lib/map-context/map-context.service.mjs +30 -10
  9. package/esm2022/libs/feature/map/src/lib/utils/map-utils.service.mjs +7 -3
  10. package/esm2022/libs/feature/record/src/lib/map-view/map-view.component.mjs +14 -7
  11. package/esm2022/libs/feature/record/src/lib/state/mdview.facade.mjs +34 -5
  12. package/esm2022/libs/feature/record/src/lib/state/mdview.reducer.mjs +1 -3
  13. package/esm2022/libs/feature/search/src/lib/results-list/results-list.container.component.mjs +4 -4
  14. package/esm2022/libs/ui/elements/src/index.mjs +2 -2
  15. package/esm2022/libs/ui/elements/src/lib/error/error.component.mjs +30 -0
  16. package/esm2022/libs/ui/elements/src/lib/record-api-form/record-api-form.component.mjs +6 -7
  17. package/esm2022/libs/ui/elements/src/lib/ui-elements.module.mjs +6 -6
  18. package/esm2022/libs/util/app-config/src/lib/app-config.mjs +3 -1
  19. package/esm2022/libs/util/app-config/src/lib/fixtures.mjs +2 -1
  20. package/esm2022/libs/util/app-config/src/lib/model.mjs +1 -1
  21. package/esm2022/translations/de.json +1 -0
  22. package/esm2022/translations/en.json +1 -0
  23. package/esm2022/translations/es.json +1 -0
  24. package/esm2022/translations/fr.json +1 -0
  25. package/esm2022/translations/it.json +1 -0
  26. package/esm2022/translations/nl.json +1 -0
  27. package/esm2022/translations/pt.json +1 -0
  28. package/fesm2022/geonetwork-ui.mjs +391 -287
  29. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  30. package/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.d.ts +2 -2
  31. package/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.d.ts.map +1 -1
  32. package/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.d.ts +1 -1
  33. package/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.d.ts.map +1 -1
  34. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts +4 -1
  35. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
  36. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts +2 -1
  37. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
  38. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +3 -3
  39. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  40. package/libs/feature/dataviz/src/lib/service/data.service.d.ts +2 -1
  41. package/libs/feature/dataviz/src/lib/service/data.service.d.ts.map +1 -1
  42. package/libs/feature/map/src/lib/map-context/map-context.model.d.ts +6 -0
  43. package/libs/feature/map/src/lib/map-context/map-context.model.d.ts.map +1 -1
  44. package/libs/feature/map/src/lib/map-context/map-context.service.d.ts +1 -1
  45. package/libs/feature/map/src/lib/map-context/map-context.service.d.ts.map +1 -1
  46. package/libs/feature/map/src/lib/utils/map-utils.service.d.ts.map +1 -1
  47. package/libs/feature/record/src/lib/map-view/map-view.component.d.ts +2 -2
  48. package/libs/feature/record/src/lib/map-view/map-view.component.d.ts.map +1 -1
  49. package/libs/feature/record/src/lib/state/mdview.facade.d.ts +5 -2
  50. package/libs/feature/record/src/lib/state/mdview.facade.d.ts.map +1 -1
  51. package/libs/feature/record/src/lib/state/mdview.reducer.d.ts.map +1 -1
  52. package/libs/ui/elements/src/index.d.ts +1 -1
  53. package/libs/ui/elements/src/index.d.ts.map +1 -1
  54. package/libs/ui/elements/src/lib/error/error.component.d.ts +16 -0
  55. package/libs/ui/elements/src/lib/error/error.component.d.ts.map +1 -0
  56. package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts.map +1 -1
  57. package/libs/ui/elements/src/lib/ui-elements.module.d.ts +2 -2
  58. package/libs/ui/elements/src/lib/ui-elements.module.d.ts.map +1 -1
  59. package/libs/util/app-config/src/lib/app-config.d.ts.map +1 -1
  60. package/libs/util/app-config/src/lib/fixtures.d.ts.map +1 -1
  61. package/libs/util/app-config/src/lib/model.d.ts +1 -0
  62. package/libs/util/app-config/src/lib/model.d.ts.map +1 -1
  63. package/package.json +2 -2
  64. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +9 -1
  65. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +3 -3
  66. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +35 -31
  67. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +67 -15
  68. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +71 -13
  69. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +3 -3
  70. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +8 -0
  71. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +15 -1
  72. package/src/libs/feature/map/src/lib/map-context/map-context.model.ts +6 -0
  73. package/src/libs/feature/map/src/lib/map-context/map-context.service.ts +31 -9
  74. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +6 -2
  75. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +15 -8
  76. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +40 -3
  77. package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +0 -2
  78. package/src/libs/feature/search/src/lib/results-list/results-list.container.component.html +4 -4
  79. package/src/libs/ui/elements/src/index.ts +1 -1
  80. package/src/libs/ui/elements/src/lib/{search-results-error/search-results-error.component.html → error/error.component.html} +18 -3
  81. package/src/libs/ui/elements/src/lib/{search-results-error/search-results-error.component.ts → error/error.component.ts} +5 -4
  82. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +7 -8
  83. package/src/libs/ui/elements/src/lib/ui-elements.module.ts +3 -4
  84. package/src/libs/util/app-config/src/lib/app-config.ts +2 -0
  85. package/src/libs/util/app-config/src/lib/fixtures.ts +1 -0
  86. package/src/libs/util/app-config/src/lib/model.ts +1 -0
  87. package/translations/de.json +1 -0
  88. package/translations/en.json +1 -0
  89. package/translations/es.json +1 -0
  90. package/translations/fr.json +1 -0
  91. package/translations/it.json +1 -0
  92. package/translations/nl.json +1 -0
  93. package/translations/pt.json +1 -0
  94. package/translations/sk.json +1 -0
  95. package/esm2022/libs/ui/elements/src/lib/search-results-error/search-results-error.component.mjs +0 -29
  96. package/libs/ui/elements/src/lib/search-results-error/search-results-error.component.d.ts +0 -15
  97. package/libs/ui/elements/src/lib/search-results-error/search-results-error.component.d.ts.map +0 -1
  98. /package/src/libs/ui/elements/src/lib/{search-results-error/search-results-error.component.css → error/error.component.css} +0 -0
@@ -4,6 +4,9 @@ import {
4
4
  DatasetRecord,
5
5
  ServiceRecord,
6
6
  } from '../../../../../../libs/common/domain/src/lib/model/record'
7
+ import { XmlElement } from '@rgrove/parse-xml'
8
+ import { BaseConverter } from '../base.converter'
9
+ import { isEqual } from '../convert-utils'
7
10
  import {
8
11
  createDocument,
9
12
  createElement,
@@ -11,32 +14,6 @@ import {
11
14
  parseXmlString,
12
15
  xmlToString,
13
16
  } from '../xml-utils'
14
- import {
15
- writeAbstract,
16
- writeContacts,
17
- writeContactsForResource,
18
- writeDistributions,
19
- writeGraphicOverviews,
20
- writeKeywords,
21
- writeKind,
22
- writeLegalConstraints,
23
- writeLicenses,
24
- writeLineage,
25
- writeOnlineResources,
26
- writeOtherConstraints,
27
- writeOwnerOrganization,
28
- writeRecordUpdated,
29
- writeResourceCreated,
30
- writeResourcePublished,
31
- writeResourceUpdated,
32
- writeSecurityConstraints,
33
- writeSpatialRepresentation,
34
- writeStatus,
35
- writeTitle,
36
- writeTopics,
37
- writeUniqueIdentifier,
38
- writeUpdateFrequency,
39
- } from './write-parts'
40
17
  import {
41
18
  readAbstract,
42
19
  readContacts,
@@ -59,13 +36,38 @@ import {
59
36
  readSecurityConstraints,
60
37
  readSpatialRepresentation,
61
38
  readStatus,
39
+ readTemporalExtents,
62
40
  readTitle,
63
41
  readUniqueIdentifier,
64
42
  readUpdateFrequency,
65
43
  } from './read-parts'
66
- import { isEqual } from '../convert-utils'
67
- import { BaseConverter } from '../base.converter'
68
- import { XmlElement } from '@rgrove/parse-xml'
44
+ import {
45
+ writeAbstract,
46
+ writeContacts,
47
+ writeContactsForResource,
48
+ writeDistributions,
49
+ writeGraphicOverviews,
50
+ writeKeywords,
51
+ writeKind,
52
+ writeLegalConstraints,
53
+ writeLicenses,
54
+ writeLineage,
55
+ writeOnlineResources,
56
+ writeOtherConstraints,
57
+ writeOwnerOrganization,
58
+ writeRecordUpdated,
59
+ writeResourceCreated,
60
+ writeResourcePublished,
61
+ writeResourceUpdated,
62
+ writeSecurityConstraints,
63
+ writeSpatialRepresentation,
64
+ writeStatus,
65
+ writeTemporalExtents,
66
+ writeTitle,
67
+ writeTopics,
68
+ writeUniqueIdentifier,
69
+ writeUpdateFrequency,
70
+ } from './write-parts'
69
71
 
70
72
  export class Iso19139Converter extends BaseConverter<string> {
71
73
  protected readers: Record<
@@ -98,9 +100,9 @@ export class Iso19139Converter extends BaseConverter<string> {
98
100
  lineage: readLineage,
99
101
  distributions: readDistributions,
100
102
  onlineResources: readOnlineResources,
103
+ temporalExtents: readTemporalExtents,
101
104
  // TODO
102
105
  spatialExtents: () => [],
103
- temporalExtents: () => [],
104
106
  extras: () => undefined,
105
107
  landingPage: () => undefined,
106
108
  languages: () => [],
@@ -136,9 +138,9 @@ export class Iso19139Converter extends BaseConverter<string> {
136
138
  lineage: writeLineage,
137
139
  distributions: writeDistributions,
138
140
  onlineResources: writeOnlineResources,
141
+ temporalExtents: writeTemporalExtents,
139
142
  // TODO
140
143
  spatialExtents: () => undefined,
141
- temporalExtents: () => undefined,
142
144
  extras: () => undefined,
143
145
  landingPage: () => undefined,
144
146
  languages: () => undefined,
@@ -311,6 +313,8 @@ export class Iso19139Converter extends BaseConverter<string> {
311
313
  fieldChanged('spatialRepresentation') &&
312
314
  this.writers['spatialRepresentation'](record, rootEl)
313
315
  fieldChanged('overviews') && this.writers['overviews'](record, rootEl)
316
+ fieldChanged('temporalExtents') &&
317
+ this.writers['temporalExtents'](record, rootEl)
314
318
  fieldChanged('distributions') &&
315
319
  this.writers['distributions'](record, rootEl)
316
320
  fieldChanged('lineage') && this.writers['lineage'](record, rootEl)
@@ -14,18 +14,7 @@ import {
14
14
  UpdateFrequency,
15
15
  UpdateFrequencyCustom,
16
16
  } from '../../../../../../libs/common/domain/src/lib/model/record'
17
- import { getStatusFromStatusCode } from './utils/status.mapper'
18
- import { getUpdateFrequencyFromFrequencyCode } from './utils/update-frequency.mapper'
19
- import {
20
- findChildElement,
21
- findChildrenElement,
22
- findNestedElement,
23
- findNestedElements,
24
- findParent,
25
- readAttribute,
26
- readText,
27
- XmlElement,
28
- } from '../xml-utils'
17
+ import { matchMimeType, matchProtocol } from '../common/distribution.mapper'
29
18
  import {
30
19
  ChainableFunction,
31
20
  combine,
@@ -37,10 +26,21 @@ import {
37
26
  mapArray,
38
27
  pipe,
39
28
  } from '../function-utils'
40
- import { getRoleFromRoleCode } from './utils/role.mapper'
41
- import { matchMimeType, matchProtocol } from '../common/distribution.mapper'
42
- import { getKeywordTypeFromKeywordTypeCode } from './utils/keyword.mapper'
29
+ import {
30
+ XmlElement,
31
+ findChildElement,
32
+ findChildrenElement,
33
+ findNestedElement,
34
+ findNestedElements,
35
+ findParent,
36
+ readAttribute,
37
+ readText,
38
+ } from '../xml-utils'
43
39
  import { fullNameToParts } from './utils/individual-name'
40
+ import { getKeywordTypeFromKeywordTypeCode } from './utils/keyword.mapper'
41
+ import { getRoleFromRoleCode } from './utils/role.mapper'
42
+ import { getStatusFromStatusCode } from './utils/status.mapper'
43
+ import { getUpdateFrequencyFromFrequencyCode } from './utils/update-frequency.mapper'
44
44
 
45
45
  export function extractCharacterString(): ChainableFunction<
46
46
  XmlElement,
@@ -843,3 +843,55 @@ export function readOnlineResources(
843
843
  flattenArray()
844
844
  )(rootEl)
845
845
  }
846
+
847
+ export function readTemporalExtents(rootEl: XmlElement) {
848
+ return pipe(
849
+ findIdentification(),
850
+ findNestedElements('gmd:extent', 'gmd:EX_Extent', 'gmd:temporalElement'),
851
+ mapArray(
852
+ combine(
853
+ findNestedElement(
854
+ 'gmd:EX_TemporalExtent',
855
+ 'gmd:extent',
856
+ 'gml:TimePeriod'
857
+ ),
858
+ findNestedElement(
859
+ 'gmd:EX_TemporalExtent',
860
+ 'gmd:extent',
861
+ 'gml:TimeInstant'
862
+ )
863
+ )
864
+ ),
865
+ mapArray(([periodEl, instantEl]) => {
866
+ if (periodEl) {
867
+ return pipe(
868
+ combine(
869
+ pipe(
870
+ findChildElement('gml:beginPosition', false),
871
+ readText(),
872
+ map((dateStr) => (dateStr ? new Date(dateStr) : null))
873
+ ),
874
+ pipe(
875
+ findChildElement('gml:endPosition', false),
876
+ readText(),
877
+ map((dateStr) => (dateStr ? new Date(dateStr) : null))
878
+ )
879
+ ),
880
+ map(([start, end]) => ({
881
+ start,
882
+ end,
883
+ }))
884
+ )(periodEl)
885
+ } else {
886
+ return pipe(
887
+ findChildElement('gml:timePosition', false),
888
+ readText(),
889
+ map((dateStr) => (dateStr ? new Date(dateStr) : null)),
890
+ map((date) => ({
891
+ start: date,
892
+ }))
893
+ )(instantEl)
894
+ }
895
+ })
896
+ )(rootEl)
897
+ }
@@ -15,7 +15,20 @@ import {
15
15
  UpdateFrequencyCode,
16
16
  UpdateFrequencyCustom,
17
17
  } from '../../../../../../libs/common/domain/src/lib/model/record'
18
+ import format from 'date-fns/format'
19
+ import {
20
+ ChainableFunction,
21
+ fallback,
22
+ filterArray,
23
+ getAtIndex,
24
+ map,
25
+ mapArray,
26
+ noop,
27
+ pipe,
28
+ tap,
29
+ } from '../function-utils'
18
30
  import {
31
+ XmlElement,
19
32
  addAttribute,
20
33
  appendChildren,
21
34
  createChild,
@@ -30,20 +43,7 @@ import {
30
43
  removeChildren,
31
44
  removeChildrenByName,
32
45
  setTextContent,
33
- XmlElement,
34
46
  } from '../xml-utils'
35
- import {
36
- ChainableFunction,
37
- fallback,
38
- filterArray,
39
- getAtIndex,
40
- map,
41
- mapArray,
42
- noop,
43
- pipe,
44
- tap,
45
- } from '../function-utils'
46
- import format from 'date-fns/format'
47
47
  import { readKind } from './read-parts'
48
48
  import { namePartsToFull } from './utils/individual-name'
49
49
 
@@ -1126,3 +1126,61 @@ export function writeOnlineResources(
1126
1126
  appendChildren(...record.onlineResources.map(createOnlineResource))
1127
1127
  )(rootEl)
1128
1128
  }
1129
+
1130
+ export function writeTemporalExtents(
1131
+ record: DatasetRecord,
1132
+ rootEl: XmlElement
1133
+ ) {
1134
+ pipe(
1135
+ findOrCreateIdentification(),
1136
+ findNestedChildOrCreate('gmd:extent', 'gmd:EX_Extent'),
1137
+ removeChildrenByName('gmd:temporalElement'),
1138
+ appendChildren(
1139
+ ...record.temporalExtents.map((extent) =>
1140
+ pipe(
1141
+ createElement('gmd:temporalElement'),
1142
+ createChild('gmd:EX_TemporalExtent'),
1143
+ appendChildren(
1144
+ 'start' in extent && 'end' in extent
1145
+ ? pipe(
1146
+ createElement('gmd:extent'),
1147
+ createChild('gml:TimePeriod'),
1148
+ appendChildren(
1149
+ pipe(
1150
+ createElement('gml:beginPosition'),
1151
+ pipe(
1152
+ extent.start
1153
+ ? setTextContent(format(extent.start, 'yyyy-MM-dd'))
1154
+ : addAttribute('indeterminatePosition', 'unknown')
1155
+ )
1156
+ ),
1157
+ pipe(
1158
+ createElement('gml:endPosition'),
1159
+ pipe(
1160
+ extent.end
1161
+ ? setTextContent(format(extent.end, 'yyyy-MM-dd'))
1162
+ : addAttribute('indeterminatePosition', 'unknown')
1163
+ )
1164
+ )
1165
+ )
1166
+ )
1167
+ : pipe(
1168
+ createElement('gmd:extent'),
1169
+ createChild('gml:TimeInstant'),
1170
+ appendChildren(
1171
+ pipe(
1172
+ createElement('gml:timePosition'),
1173
+ pipe(
1174
+ extent.start
1175
+ ? setTextContent(format(extent.start, 'yyyy-MM-dd'))
1176
+ : addAttribute('indeterminatePosition', 'unknown')
1177
+ )
1178
+ )
1179
+ )
1180
+ )
1181
+ )
1182
+ )
1183
+ )
1184
+ )
1185
+ )(rootEl)
1186
+ }
@@ -169,12 +169,12 @@ export interface DatasetSpatialExtent {
169
169
  }
170
170
 
171
171
  /**
172
- * At least a start or an end date should be provided
172
+ * Period if both start and end are provided
173
+ * Instant if only start is provided
173
174
  */
174
175
  export interface DatasetTemporalExtent {
175
- start?: Date
176
+ start: Date
176
177
  end?: Date
177
- description?: string
178
178
  }
179
179
 
180
180
  export interface DatasetRecord extends BaseRecord {
@@ -117,6 +117,14 @@ Cette section contient des *caractères internationaux* (ainsi que des "caractè
117
117
  description: 'This WFS service offers direct download capability',
118
118
  identifierInService: 'my:featuretype',
119
119
  },
120
+ {
121
+ type: 'service',
122
+ url: new URL('https://my-org.net/ogc'),
123
+ accessServiceProtocol: 'ogcFeatures',
124
+ name: 'my:featuretype',
125
+ description: 'This OGC service offers direct download capability',
126
+ identifierInService: 'my:featuretype',
127
+ },
120
128
  ],
121
129
  lineage: `This record was edited manually to test the conversion processes
122
130
 
@@ -3,6 +3,7 @@ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
3
3
  import {
4
4
  OgcApiCollectionInfo,
5
5
  OgcApiEndpoint,
6
+ OgcApiRecord,
6
7
  WfsEndpoint,
7
8
  WfsVersion,
8
9
  } from '@camptocamp/ogc-client'
@@ -173,10 +174,23 @@ export class DataService {
173
174
  }
174
175
 
175
176
  async getDownloadUrlsFromOgcApi(url: string): Promise<OgcApiCollectionInfo> {
177
+ const endpoint = new OgcApiEndpoint(this.proxy.getProxiedUrl(url))
178
+ return await endpoint.allCollections
179
+ .then((collections) => {
180
+ return endpoint.getCollectionInfo(collections[0].name)
181
+ })
182
+ .catch((error) => {
183
+ throw new Error(`ogc.unreachable.unknown`)
184
+ })
185
+ }
186
+
187
+ async getItemsFromOgcApi(url: string): Promise<OgcApiRecord> {
176
188
  const endpoint = new OgcApiEndpoint(this.proxy.getProxiedUrl(url))
177
189
  return await endpoint.featureCollections
178
190
  .then((collections) => {
179
- return endpoint.getCollectionInfo(collections[0])
191
+ return collections.length
192
+ ? endpoint.getCollectionItem(collections[0], '1')
193
+ : null
180
194
  })
181
195
  .catch((error) => {
182
196
  throw new Error(`ogc.unreachable.unknown`)
@@ -20,18 +20,21 @@ export interface MapContextLayerWmsModel {
20
20
  type: 'wms'
21
21
  url: string
22
22
  name: string
23
+ attributions?: string
23
24
  }
24
25
 
25
26
  export interface MapContextLayerWmtsModel {
26
27
  type: 'wmts'
27
28
  url: string
28
29
  name: string
30
+ attributions?: string
29
31
  }
30
32
 
31
33
  interface MapContextLayerWfsModel {
32
34
  type: 'wfs'
33
35
  url: string
34
36
  name: string
37
+ attributions?: string
35
38
  }
36
39
 
37
40
  export interface MapContextLayerOgcapiModel {
@@ -39,11 +42,13 @@ export interface MapContextLayerOgcapiModel {
39
42
  url: string
40
43
  name: string
41
44
  layerType: 'feature' | 'vectorTiles' | 'mapTiles' | 'record'
45
+ attributions?: string
42
46
  }
43
47
 
44
48
  interface LayerXyzModel {
45
49
  type: 'xyz'
46
50
  name?: string
51
+ attributions?: string
47
52
  }
48
53
  interface LayerXyzModelWithUrl extends LayerXyzModel {
49
54
  url: string
@@ -59,6 +64,7 @@ export type MapContextLayerXyzModel =
59
64
 
60
65
  interface LayerGeojson {
61
66
  type: 'geojson'
67
+ attributions?: string
62
68
  }
63
69
  interface LayerGeojsonWithUrl extends LayerGeojson {
64
70
  url: string
@@ -29,6 +29,8 @@ import OGCVectorTile from 'ol/source/OGCVectorTile.js'
29
29
  import { MVT } from 'ol/format'
30
30
  import VectorTileLayer from 'ol/layer/VectorTile'
31
31
  import OGCMapTile from 'ol/source/OGCMapTile.js'
32
+ import ImageLayer from 'ol/layer/Image'
33
+ import ImageWMS from 'ol/source/ImageWMS'
32
34
 
33
35
  export const DEFAULT_BASELAYER_CONTEXT: MapContextLayerXyzModel = {
34
36
  type: MapContextLayerTypeEnum.XYZ,
@@ -37,6 +39,7 @@ export const DEFAULT_BASELAYER_CONTEXT: MapContextLayerXyzModel = {
37
39
  `https://b.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png`,
38
40
  `https://c.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png`,
39
41
  ],
42
+ attributions: `<span>© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, © <a href="https://carto.com/">Carto</a></span>`,
40
43
  }
41
44
 
42
45
  export const DEFAULT_VIEW: MapContextViewModel = {
@@ -73,11 +76,13 @@ export class MapContextService {
73
76
  }
74
77
  map.setView(this.createView(mapContext.view, map))
75
78
  map.getLayers().clear()
76
- mapContext.layers.forEach((layer) => map.addLayer(this.createLayer(layer)))
79
+ mapContext.layers.forEach((layer) =>
80
+ map.addLayer(this.createLayer(layer, mapConfig))
81
+ )
77
82
  return map
78
83
  }
79
84
 
80
- createLayer(layerModel: MapContextLayerModel): Layer {
85
+ createLayer(layerModel: MapContextLayerModel, mapConfig?: MapConfig): Layer {
81
86
  const { type } = layerModel
82
87
  const style = this.styleService.styles.default
83
88
  switch (type) {
@@ -87,12 +92,14 @@ export class MapContextService {
87
92
  source: new OGCVectorTile({
88
93
  url: layerModel.url,
89
94
  format: new MVT(),
95
+ attributions: layerModel.attributions,
90
96
  }),
91
97
  })
92
98
  } else if (layerModel.layerType === 'mapTiles') {
93
99
  return new TileLayer({
94
100
  source: new OGCMapTile({
95
101
  url: layerModel.url,
102
+ attributions: layerModel.attributions,
96
103
  }),
97
104
  })
98
105
  } else {
@@ -100,6 +107,7 @@ export class MapContextService {
100
107
  source: new VectorSource({
101
108
  format: new GeoJSON(),
102
109
  url: layerModel.url,
110
+ attributions: layerModel.attributions,
103
111
  }),
104
112
  style,
105
113
  })
@@ -109,16 +117,28 @@ export class MapContextService {
109
117
  source: new XYZ({
110
118
  url: 'url' in layerModel ? layerModel.url : undefined,
111
119
  urls: 'urls' in layerModel ? layerModel.urls : undefined,
120
+ attributions: layerModel.attributions,
112
121
  }),
113
122
  })
114
123
  case MapContextLayerTypeEnum.WMS:
115
- return new TileLayer({
116
- source: new TileWMS({
117
- url: layerModel.url,
118
- params: { LAYERS: layerModel.name },
119
- gutter: 20,
120
- }),
121
- })
124
+ if (mapConfig?.DO_NOT_TILE_WMS) {
125
+ return new ImageLayer({
126
+ source: new ImageWMS({
127
+ url: layerModel.url,
128
+ params: { LAYERS: layerModel.name },
129
+ attributions: layerModel.attributions,
130
+ }),
131
+ })
132
+ } else {
133
+ return new TileLayer({
134
+ source: new TileWMS({
135
+ url: layerModel.url,
136
+ params: { LAYERS: layerModel.name, TILED: true },
137
+ attributions: layerModel.attributions,
138
+ }),
139
+ })
140
+ }
141
+
122
142
  case MapContextLayerTypeEnum.WMTS: {
123
143
  // TODO: isolate this in utils service
124
144
  const olLayer = new TileLayer({})
@@ -141,6 +161,7 @@ export class MapContextService {
141
161
  tileGrid,
142
162
  projection: matrixSet.crs,
143
163
  dimensions,
164
+ attributions: layerModel.attributions,
144
165
  })
145
166
  )
146
167
  })
@@ -166,6 +187,7 @@ export class MapContextService {
166
187
  })
167
188
  },
168
189
  strategy: bboxStrategy,
190
+ attributions: layerModel.attributions,
169
191
  })
170
192
  )
171
193
  })
@@ -33,6 +33,7 @@ import { WmsEndpoint, WmtsEndpoint } from '@camptocamp/ogc-client'
33
33
  import { LONLAT_CRS_CODES } from '../constant/projections'
34
34
  import { fromEPSGCode, register } from 'ol/proj/proj4'
35
35
  import proj4 from 'proj4/dist/proj4'
36
+ import { defaults as defaultControls } from 'ol/control/defaults'
36
37
 
37
38
  const FEATURE_PROJECTION = 'EPSG:3857'
38
39
  const DATA_PROJECTION = 'EPSG:4326'
@@ -47,7 +48,10 @@ export class MapUtilsService {
47
48
 
48
49
  createEmptyMap(): Map {
49
50
  return new Map({
50
- controls: [],
51
+ controls: defaultControls({
52
+ attribution: true,
53
+ attributionOptions: { collapsible: false },
54
+ }),
51
55
  pixelRatio: 1,
52
56
  })
53
57
  }
@@ -213,7 +217,7 @@ export class MapUtilsService {
213
217
  }
214
218
 
215
219
  getRecordExtent(record: Partial<CatalogRecord>): Extent {
216
- if (!('spatialExtents' in record)) {
220
+ if (!('spatialExtents' in record) || record.spatialExtents.length === 0) {
217
221
  return null
218
222
  }
219
223
  // transform an array of geojson geometries into a bbox
@@ -15,7 +15,7 @@ import {
15
15
  MapUtilsService,
16
16
  } from '../../../../../../libs/feature/map/src'
17
17
  import { getOptionalMapConfig, MapConfig } from '../../../../../../libs/util/app-config/src'
18
- import { getLinkLabel, ProxyService } from '../../../../../../libs/util/shared/src'
18
+ import { getLinkLabel } from '../../../../../../libs/util/shared/src'
19
19
  import Feature from 'ol/Feature'
20
20
  import { Geometry } from 'ol/geom'
21
21
  import { StyleLike } from 'ol/style/Style'
@@ -23,10 +23,8 @@ import {
23
23
  BehaviorSubject,
24
24
  combineLatest,
25
25
  from,
26
- lastValueFrom,
27
26
  Observable,
28
27
  of,
29
- startWith,
30
28
  Subscription,
31
29
  throwError,
32
30
  withLatestFrom,
@@ -36,6 +34,7 @@ import {
36
34
  distinctUntilChanged,
37
35
  finalize,
38
36
  map,
37
+ startWith,
39
38
  switchMap,
40
39
  tap,
41
40
  } from 'rxjs/operators'
@@ -57,9 +56,11 @@ export class MapViewComponent implements OnInit, OnDestroy {
57
56
 
58
57
  compatibleMapLinks$ = combineLatest([
59
58
  this.mdViewFacade.mapApiLinks$,
60
- this.mdViewFacade.geoDataLinks$,
59
+ this.mdViewFacade.geoDataLinksWithGeometry$,
61
60
  ]).pipe(
62
- map(([mapApiLinks, geoDataLinks]) => [...mapApiLinks, ...geoDataLinks])
61
+ map(([mapApiLinks, geoDataLinksWithGeometry]) => {
62
+ return [...mapApiLinks, ...geoDataLinksWithGeometry]
63
+ })
63
64
  )
64
65
 
65
66
  dropdownChoices$ = this.compatibleMapLinks$.pipe(
@@ -104,8 +105,8 @@ export class MapViewComponent implements OnInit, OnDestroy {
104
105
  mapContext$ = this.currentLayers$.pipe(
105
106
  switchMap((layers) =>
106
107
  from(this.mapUtils.getLayerExtent(layers[0])).pipe(
107
- catchError((error) => {
108
- console.warn(error) // FIXME: report this to the user somehow
108
+ catchError(() => {
109
+ this.error = 'The layer has no extent'
109
110
  return of(undefined)
110
111
  }),
111
112
  map(
@@ -117,9 +118,15 @@ export class MapViewComponent implements OnInit, OnDestroy {
117
118
  },
118
119
  } as MapContextModel)
119
120
  ),
120
- tap(() => this.resetSelection())
121
+ tap((res) => {
122
+ this.resetSelection()
123
+ })
121
124
  )
122
125
  ),
126
+ startWith({
127
+ layers: [],
128
+ view: {},
129
+ } as MapContextModel),
123
130
  withLatestFrom(this.mdViewFacade.metadata$),
124
131
  map(([context, metadata]) => {
125
132
  if (context.view.extent) return context