geonetwork-ui 2.2.0-dev.6695ae4d → 2.2.0-dev.6f0f0ee9
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.
- package/esm2022/libs/api/metadata-converter/src/lib/gn4/atomic-operations.mjs +27 -1
- package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.mjs +8 -8
- package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.mjs +2 -2
- package/esm2022/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.mjs +1 -1
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/codelists/keyword.mapper.mjs +14 -0
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/converter.mjs +7 -9
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +30 -8
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +31 -14
- package/esm2022/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.mjs +10 -3
- package/esm2022/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.mjs +14 -5
- package/esm2022/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.mjs +33 -8
- package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
- package/esm2022/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.mjs +1 -1
- package/esm2022/libs/common/domain/src/lib/platform.service.interface.mjs +1 -1
- package/esm2022/libs/feature/map/src/lib/map-context/map-context.service.mjs +9 -1
- package/esm2022/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.mjs +3 -3
- package/esm2022/libs/feature/search/src/lib/utils/service/fields.mjs +41 -26
- package/esm2022/libs/feature/search/src/lib/utils/service/fields.service.mjs +11 -9
- package/esm2022/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.mjs +3 -3
- package/esm2022/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.mjs +3 -3
- package/esm2022/libs/ui/elements/src/lib/metadata-info/metadata-info.component.mjs +7 -7
- package/esm2022/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.mjs +3 -4
- package/esm2022/libs/ui/elements/src/lib/thumbnail/thumbnail.component.mjs +3 -3
- package/esm2022/libs/ui/elements/src/lib/ui-elements.module.mjs +4 -4
- package/esm2022/libs/ui/inputs/src/lib/button/button.component.mjs +3 -3
- package/esm2022/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.mjs +3 -3
- package/esm2022/libs/ui/inputs/src/lib/text-area/text-area.component.mjs +3 -3
- package/esm2022/libs/ui/inputs/src/lib/ui-inputs.module.mjs +6 -6
- package/esm2022/translations/de.json +2 -1
- package/esm2022/translations/en.json +2 -1
- package/esm2022/translations/es.json +2 -1
- package/esm2022/translations/fr.json +2 -1
- package/esm2022/translations/it.json +2 -1
- package/esm2022/translations/nl.json +2 -1
- package/esm2022/translations/pt.json +2 -1
- package/fesm2022/geonetwork-ui.mjs +267 -122
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/libs/api/metadata-converter/src/lib/gn4/atomic-operations.d.ts +2 -0
- package/libs/api/metadata-converter/src/lib/gn4/atomic-operations.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.d.ts +3 -5
- package/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/codelists/keyword.mapper.d.ts +3 -0
- package/libs/api/metadata-converter/src/lib/iso19139/codelists/keyword.mapper.d.ts.map +1 -0
- package/libs/api/metadata-converter/src/lib/iso19139/converter.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts +2 -3
- package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
- package/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.d.ts.map +1 -1
- package/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.d.ts +1 -1
- package/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.d.ts.map +1 -1
- package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts +9 -2
- package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts.map +1 -1
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +13 -2
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
- package/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.d.ts +1 -0
- package/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.d.ts.map +1 -1
- package/libs/common/domain/src/lib/platform.service.interface.d.ts +1 -1
- package/libs/common/domain/src/lib/platform.service.interface.d.ts.map +1 -1
- package/libs/feature/map/src/lib/map-context/map-context.service.d.ts +1 -0
- package/libs/feature/map/src/lib/map-context/map-context.service.d.ts.map +1 -1
- package/libs/feature/search/src/lib/utils/service/fields.d.ts +21 -8
- package/libs/feature/search/src/lib/utils/service/fields.d.ts.map +1 -1
- package/libs/feature/search/src/lib/utils/service/fields.service.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.d.ts +1 -1
- package/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/metadata-info/metadata-info.component.d.ts +3 -3
- package/libs/ui/elements/src/lib/metadata-info/metadata-info.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/ui-elements.module.d.ts +13 -13
- package/libs/ui/inputs/src/lib/button/button.component.d.ts +1 -1
- package/libs/ui/inputs/src/lib/button/button.component.d.ts.map +1 -1
- package/libs/ui/inputs/src/lib/text-area/text-area.component.d.ts +1 -1
- package/libs/ui/inputs/src/lib/text-area/text-area.component.d.ts.map +1 -1
- package/libs/ui/inputs/src/lib/ui-inputs.module.d.ts +40 -40
- package/libs/ui/layout/src/lib/interactive-table/interactive-table-column/interactive-table-column.component.d.ts +1 -1
- package/package.json +1 -1
- package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +55 -2
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +50 -6
- package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +203 -20
- package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +29 -0
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +11 -8
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.ts +1 -1
- package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +3 -5
- package/src/libs/api/metadata-converter/src/lib/iso19139/codelists/keyword.mapper.ts +16 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/converter.ts +5 -9
- package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +53 -19
- package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +78 -36
- package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +22 -2
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.ts +15 -4
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +47 -8
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +16 -3
- package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +1 -0
- package/src/libs/common/domain/src/lib/platform.service.interface.ts +1 -4
- package/src/libs/common/fixtures/src/lib/gn4/groups.fixtures.ts +1 -1
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +50 -10
- package/src/libs/feature/map/src/lib/map-context/map-context.service.ts +10 -0
- package/src/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.html +3 -4
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +21 -16
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +43 -34
- package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.html +1 -1
- package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.css +5 -0
- package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.html +39 -0
- package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.ts +58 -0
- package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +1 -0
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +6 -6
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +6 -3
- package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts +2 -3
- package/src/libs/ui/elements/src/lib/thumbnail/thumbnail.component.html +2 -2
- package/src/libs/ui/elements/src/lib/ui-elements.module.ts +1 -1
- package/src/libs/ui/inputs/src/lib/button/button.component.ts +1 -0
- package/src/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.css +3 -0
- package/src/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/text-area/text-area.component.ts +1 -0
- package/src/libs/ui/inputs/src/lib/ui-inputs.module.ts +2 -2
- package/tailwind.base.config.js +3 -0
- package/tailwind.base.css +2 -2
- package/translations/de.json +2 -1
- package/translations/en.json +2 -1
- package/translations/es.json +2 -1
- package/translations/fr.json +2 -1
- package/translations/it.json +2 -1
- package/translations/nl.json +2 -1
- package/translations/pt.json +2 -1
- package/translations/sk.json +2 -1
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
writeSecurityConstraints,
|
|
30
30
|
writeSpatialRepresentation,
|
|
31
31
|
writeStatus,
|
|
32
|
-
|
|
32
|
+
writeTopics,
|
|
33
33
|
writeTitle,
|
|
34
34
|
writeUniqueIdentifier,
|
|
35
35
|
writeUpdateFrequency,
|
|
@@ -56,7 +56,6 @@ import {
|
|
|
56
56
|
readSpatialRepresentation,
|
|
57
57
|
readStatus,
|
|
58
58
|
readTemporalExtents,
|
|
59
|
-
readThemes,
|
|
60
59
|
readTitle,
|
|
61
60
|
readUniqueIdentifier,
|
|
62
61
|
readUpdateFrequency,
|
|
@@ -76,16 +75,13 @@ export function toModel(xml: string): CatalogRecord {
|
|
|
76
75
|
const recordUpdated = readRecordUpdated(rootEl)
|
|
77
76
|
const recordCreated = recordUpdated
|
|
78
77
|
const keywords = readKeywords(rootEl)
|
|
79
|
-
const
|
|
78
|
+
const topics = readIsoTopics(rootEl)
|
|
80
79
|
const legalConstraints = readLegalConstraints(rootEl)
|
|
81
80
|
const otherConstraints = readOtherConstraints(rootEl)
|
|
82
81
|
const securityConstraints = readSecurityConstraints(rootEl)
|
|
83
82
|
const licenses = readLicenses(rootEl)
|
|
84
83
|
const overviews = readOverviews(rootEl)
|
|
85
84
|
|
|
86
|
-
// not used yet
|
|
87
|
-
const isoTopics = readIsoTopics(rootEl)
|
|
88
|
-
|
|
89
85
|
if (kind === 'dataset') {
|
|
90
86
|
const status = readStatus(rootEl)
|
|
91
87
|
const datasetCreated = readDatasetCreated(rootEl)
|
|
@@ -109,7 +105,7 @@ export function toModel(xml: string): CatalogRecord {
|
|
|
109
105
|
contacts,
|
|
110
106
|
contactsForResource: [], // FIXME: is that really useful??
|
|
111
107
|
keywords,
|
|
112
|
-
|
|
108
|
+
topics,
|
|
113
109
|
licenses,
|
|
114
110
|
legalConstraints,
|
|
115
111
|
securityConstraints,
|
|
@@ -136,7 +132,7 @@ export function toModel(xml: string): CatalogRecord {
|
|
|
136
132
|
ownerOrganization,
|
|
137
133
|
contacts,
|
|
138
134
|
keywords,
|
|
139
|
-
|
|
135
|
+
topics,
|
|
140
136
|
licenses,
|
|
141
137
|
legalConstraints,
|
|
142
138
|
securityConstraints,
|
|
@@ -168,7 +164,7 @@ export function toXml(record: CatalogRecord, originalXml?: string): string {
|
|
|
168
164
|
writeAbstract(record, rootEl)
|
|
169
165
|
fieldChanged('contacts') && writeContacts(record, rootEl)
|
|
170
166
|
fieldChanged('keywords') && writeKeywords(record, rootEl)
|
|
171
|
-
fieldChanged('
|
|
167
|
+
fieldChanged('topics') && writeTopics(record, rootEl)
|
|
172
168
|
fieldChanged('legalConstraints') && writeLegalConstraints(record, rootEl)
|
|
173
169
|
fieldChanged('securityConstraints') &&
|
|
174
170
|
writeSecurityConstraints(record, rootEl)
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
DatasetTemporalExtent,
|
|
6
6
|
GraphicOverview,
|
|
7
7
|
Individual,
|
|
8
|
+
Keyword,
|
|
9
|
+
KeywordThesaurus,
|
|
8
10
|
Organization,
|
|
9
11
|
RecordKind,
|
|
10
12
|
RecordStatus,
|
|
@@ -39,6 +41,7 @@ import {
|
|
|
39
41
|
} from '../function-utils'
|
|
40
42
|
import { getRoleFromRoleCode } from './codelists/role.mapper'
|
|
41
43
|
import { matchMimeType, matchProtocol } from '../common/distribution.mapper'
|
|
44
|
+
import { getKeywordTypeFromKeywordTypeCode } from './codelists/keyword.mapper'
|
|
42
45
|
|
|
43
46
|
function extractCharacterString(): ChainableFunction<XmlElement, string> {
|
|
44
47
|
return pipe(
|
|
@@ -552,29 +555,60 @@ export function readContacts(rootEl: XmlElement): Individual[] {
|
|
|
552
555
|
)(rootEl)
|
|
553
556
|
}
|
|
554
557
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
)
|
|
565
|
-
),
|
|
566
|
-
mapArray(findChildrenElement('gmd:keyword')),
|
|
567
|
-
flattenArray(),
|
|
568
|
-
mapArray(extractCharacterString())
|
|
558
|
+
// from gmd:thesaurusName
|
|
559
|
+
function readThesaurus(rootEl: XmlElement): KeywordThesaurus {
|
|
560
|
+
if (!rootEl) return null
|
|
561
|
+
|
|
562
|
+
const findIdentifier = findNestedElement(
|
|
563
|
+
'gmd:CI_Citation',
|
|
564
|
+
'gmd:identifier',
|
|
565
|
+
'gmd:MD_Identifier',
|
|
566
|
+
'gmd:code'
|
|
569
567
|
)
|
|
568
|
+
const id = pipe(findIdentifier, extractCharacterString())(rootEl)
|
|
569
|
+
const url = pipe(findIdentifier, extractUrl())(rootEl)
|
|
570
|
+
const name = pipe(
|
|
571
|
+
findNestedElement('gmd:CI_Citation', 'gmd:title'),
|
|
572
|
+
extractCharacterString()
|
|
573
|
+
)(rootEl)
|
|
574
|
+
return {
|
|
575
|
+
id,
|
|
576
|
+
...(name && { name }),
|
|
577
|
+
...(url && { url }),
|
|
578
|
+
}
|
|
570
579
|
}
|
|
571
580
|
|
|
572
|
-
|
|
573
|
-
|
|
581
|
+
// from gmd:MD_Keywords
|
|
582
|
+
function readKeywordGroup(rootEl: XmlElement): Keyword[] {
|
|
583
|
+
const type = pipe(
|
|
584
|
+
findChildrenElement('gmd:MD_KeywordTypeCode'),
|
|
585
|
+
mapArray(readAttribute('codeListValue')),
|
|
586
|
+
map((values) => getKeywordTypeFromKeywordTypeCode(values[0]))
|
|
587
|
+
)(rootEl)
|
|
588
|
+
const thesaurus = pipe(
|
|
589
|
+
findNestedElement('gmd:thesaurusName'),
|
|
590
|
+
readThesaurus
|
|
591
|
+
)(rootEl)
|
|
592
|
+
return pipe(
|
|
593
|
+
findChildrenElement('gmd:keyword'),
|
|
594
|
+
mapArray((el) => {
|
|
595
|
+
const label = extractCharacterString()(el)
|
|
596
|
+
return {
|
|
597
|
+
...(thesaurus ? { thesaurus } : {}),
|
|
598
|
+
label,
|
|
599
|
+
type,
|
|
600
|
+
} as Keyword
|
|
601
|
+
})
|
|
602
|
+
)(rootEl)
|
|
574
603
|
}
|
|
575
604
|
|
|
576
|
-
export function
|
|
577
|
-
return
|
|
605
|
+
export function readKeywords(rootEl: XmlElement): Keyword[] {
|
|
606
|
+
return pipe(
|
|
607
|
+
findIdentification(),
|
|
608
|
+
findNestedElements('gmd:descriptiveKeywords', 'gmd:MD_Keywords'),
|
|
609
|
+
mapArray(readKeywordGroup),
|
|
610
|
+
flattenArray()
|
|
611
|
+
)(rootEl)
|
|
578
612
|
}
|
|
579
613
|
|
|
580
614
|
export function readStatus(rootEl: XmlElement): RecordStatus {
|
|
@@ -627,7 +661,7 @@ export function readLicenses(rootEl: XmlElement): Constraint[] {
|
|
|
627
661
|
export function readIsoTopics(rootEl: XmlElement): string[] {
|
|
628
662
|
return pipe(
|
|
629
663
|
findIdentification(),
|
|
630
|
-
findChildrenElement('gmd:MD_TopicCategoryCode',
|
|
664
|
+
findChildrenElement('gmd:MD_TopicCategoryCode', true),
|
|
631
665
|
mapArray(readText())
|
|
632
666
|
)(rootEl)
|
|
633
667
|
}
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
DatasetRecord,
|
|
6
6
|
DatasetServiceDistribution,
|
|
7
7
|
Individual,
|
|
8
|
+
Keyword,
|
|
9
|
+
KeywordThesaurus,
|
|
8
10
|
RecordStatus,
|
|
9
11
|
Role,
|
|
10
12
|
ServiceEndpoint,
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
findNestedElement,
|
|
26
28
|
findNestedElements,
|
|
27
29
|
readAttribute,
|
|
30
|
+
readText,
|
|
28
31
|
removeAllChildren,
|
|
29
32
|
removeChildren,
|
|
30
33
|
removeChildrenByName,
|
|
@@ -326,47 +329,78 @@ function appendCitationDate(date, type: 'revision' | 'creation') {
|
|
|
326
329
|
)
|
|
327
330
|
}
|
|
328
331
|
|
|
329
|
-
function removeKeywords(
|
|
330
|
-
return removeChildren(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
332
|
+
function removeKeywords() {
|
|
333
|
+
return removeChildren(pipe(findNestedElements('gmd:descriptiveKeywords')))
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// returns a <gmd:thesaurusName> element
|
|
337
|
+
function createThesaurus(thesaurus: KeywordThesaurus) {
|
|
338
|
+
return pipe(
|
|
339
|
+
createElement('gmd:thesaurusName'),
|
|
340
|
+
createChild('gmd:CI_Citation'),
|
|
341
|
+
appendChildren(
|
|
342
|
+
pipe(
|
|
343
|
+
createElement('gmd:title'),
|
|
344
|
+
writeCharacterString(thesaurus.name || thesaurus.id)
|
|
345
|
+
),
|
|
346
|
+
pipe(
|
|
347
|
+
createElement('gmd:identifier'),
|
|
348
|
+
createChild('gmd:MD_Identifier'),
|
|
349
|
+
appendChildren(
|
|
350
|
+
pipe(
|
|
351
|
+
createElement('gmd:code'),
|
|
352
|
+
thesaurus.url
|
|
353
|
+
? writeAnchor(thesaurus.url, thesaurus.id)
|
|
354
|
+
: writeCharacterString(thesaurus.id)
|
|
355
|
+
)
|
|
343
356
|
)
|
|
344
357
|
)
|
|
345
358
|
)
|
|
346
359
|
)
|
|
347
360
|
}
|
|
348
361
|
|
|
349
|
-
function appendKeywords(keywords:
|
|
362
|
+
function appendKeywords(keywords: Keyword[]) {
|
|
363
|
+
const keywordsByThesaurus: Keyword[][] = keywords.reduce((acc, keyword) => {
|
|
364
|
+
const thesaurusId = keyword.thesaurus?.id
|
|
365
|
+
const type = keyword.type
|
|
366
|
+
let existingGroup = acc.find((group) =>
|
|
367
|
+
group[0].thesaurus
|
|
368
|
+
? group[0].thesaurus.id === thesaurusId
|
|
369
|
+
: group[0].type === type
|
|
370
|
+
)
|
|
371
|
+
if (!existingGroup) {
|
|
372
|
+
existingGroup = []
|
|
373
|
+
acc.push(existingGroup)
|
|
374
|
+
}
|
|
375
|
+
existingGroup.push(keyword)
|
|
376
|
+
return acc
|
|
377
|
+
}, [])
|
|
350
378
|
return appendChildren(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
379
|
+
...keywordsByThesaurus.map((keywords) =>
|
|
380
|
+
pipe(
|
|
381
|
+
createElement('gmd:descriptiveKeywords'),
|
|
382
|
+
createChild('gmd:MD_Keywords'),
|
|
383
|
+
appendChildren(
|
|
384
|
+
pipe(
|
|
385
|
+
createElement('gmd:type'),
|
|
386
|
+
createChild('gmd:MD_KeywordTypeCode'),
|
|
387
|
+
addAttribute(
|
|
388
|
+
'codeList',
|
|
389
|
+
'http://standards.iso.org/iso/19139/resources/gmxCodelists.xml#MD_KeywordTypeCode'
|
|
390
|
+
),
|
|
391
|
+
addAttribute('codeListValue', keywords[0].type)
|
|
392
|
+
)
|
|
393
|
+
),
|
|
394
|
+
keywords[0].thesaurus
|
|
395
|
+
? appendChildren(createThesaurus(keywords[0].thesaurus))
|
|
396
|
+
: noop,
|
|
397
|
+
appendChildren(
|
|
398
|
+
...keywords.map((keyword) =>
|
|
356
399
|
pipe(
|
|
357
|
-
createElement('gmd:
|
|
358
|
-
|
|
359
|
-
addAttribute(
|
|
360
|
-
'codeList',
|
|
361
|
-
'http://standards.iso.org/iso/19139/resources/gmxCodelists.xml#MD_KeywordTypeCode'
|
|
362
|
-
),
|
|
363
|
-
addAttribute('codeListValue', type)
|
|
400
|
+
createElement('gmd:keyword'),
|
|
401
|
+
writeCharacterString(keyword.label)
|
|
364
402
|
)
|
|
365
403
|
)
|
|
366
|
-
: noop,
|
|
367
|
-
appendChildren(
|
|
368
|
-
...keywords.map((keyword) =>
|
|
369
|
-
pipe(createElement('gmd:keyword'), writeCharacterString(keyword))
|
|
370
404
|
)
|
|
371
405
|
)
|
|
372
406
|
)
|
|
@@ -740,16 +774,24 @@ export function writeContacts(record: CatalogRecord, rootEl: XmlElement) {
|
|
|
740
774
|
export function writeKeywords(record: CatalogRecord, rootEl: XmlElement) {
|
|
741
775
|
pipe(
|
|
742
776
|
findOrCreateIdentification(),
|
|
743
|
-
removeKeywords(
|
|
744
|
-
appendKeywords(record.keywords
|
|
777
|
+
removeKeywords(),
|
|
778
|
+
appendKeywords(record.keywords)
|
|
745
779
|
)(rootEl)
|
|
746
780
|
}
|
|
747
781
|
|
|
748
|
-
export function
|
|
782
|
+
export function writeTopics(record: CatalogRecord, rootEl: XmlElement) {
|
|
749
783
|
pipe(
|
|
750
784
|
findOrCreateIdentification(),
|
|
751
|
-
|
|
752
|
-
|
|
785
|
+
removeChildrenByName('gmd:topicCategory'),
|
|
786
|
+
appendChildren(
|
|
787
|
+
...record.topics.map((topic) =>
|
|
788
|
+
pipe(
|
|
789
|
+
createElement('gmd:topicCategory'),
|
|
790
|
+
createChild('gmd:MD_TopicCategoryCode'),
|
|
791
|
+
setTextContent(topic)
|
|
792
|
+
)
|
|
793
|
+
)
|
|
794
|
+
)
|
|
753
795
|
)(rootEl)
|
|
754
796
|
}
|
|
755
797
|
|
package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts
CHANGED
|
@@ -25,7 +25,14 @@ import {
|
|
|
25
25
|
SourceWithUnknownProps,
|
|
26
26
|
} from '../../../../../../../libs/api/metadata-converter/src'
|
|
27
27
|
import { combineLatest, Observable, of, switchMap, takeLast } from 'rxjs'
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
filter,
|
|
30
|
+
map,
|
|
31
|
+
shareReplay,
|
|
32
|
+
startWith,
|
|
33
|
+
tap,
|
|
34
|
+
withLatestFrom,
|
|
35
|
+
} from 'rxjs/operators'
|
|
29
36
|
import { LangService } from '../../../../../../../libs/util/i18n/src'
|
|
30
37
|
import { PlatformServiceInterface } from '../../../../../../../libs/common/domain/src/lib/platform.service.interface'
|
|
31
38
|
import { coerce, satisfies, valid } from 'semver'
|
|
@@ -296,15 +303,28 @@ export class OrganizationsFromMetadataService
|
|
|
296
303
|
|
|
297
304
|
const ownerOrganization = allContactOrgs[0]
|
|
298
305
|
|
|
306
|
+
// read the owner group as well to have a fallback logo
|
|
307
|
+
const groupId = selectField(source, 'groupOwner')
|
|
308
|
+
const group$ = this.groups$.pipe(
|
|
309
|
+
map((groups) =>
|
|
310
|
+
groups.find((group) => {
|
|
311
|
+
return group.id === Number(groupId)
|
|
312
|
+
})
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
|
|
299
316
|
return this.organisations$.pipe(
|
|
300
317
|
takeLast(1),
|
|
301
|
-
|
|
318
|
+
withLatestFrom(group$),
|
|
319
|
+
map(([organisations, group]: [Organization[], GroupApiModel]) => {
|
|
302
320
|
const recordOrganisation = organisations.filter(
|
|
303
321
|
(org) => org.name === ownerOrganization.name
|
|
304
322
|
)[0]
|
|
323
|
+
const logoUrl = group?.logo && getAsUrl(`${IMAGE_URL}${group.logo}`)
|
|
305
324
|
return {
|
|
306
325
|
...record,
|
|
307
326
|
ownerOrganization: {
|
|
327
|
+
logoUrl,
|
|
308
328
|
...ownerOrganization,
|
|
309
329
|
...recordOrganisation,
|
|
310
330
|
},
|
|
@@ -45,12 +45,23 @@ export class Gn4PlatformMapper {
|
|
|
45
45
|
return { ...apiUser, id: id.toString() } as UserModel
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
thesaurusFromApi(thesaurus: any[]): ThesaurusModel {
|
|
48
|
+
thesaurusFromApi(thesaurus: any[], lang3?: string): ThesaurusModel {
|
|
49
49
|
return thesaurus.map((keyword) => {
|
|
50
|
-
|
|
50
|
+
let key = keyword.uri
|
|
51
|
+
// sometines GN can prefix an URI with an "all thesaurus" URI; only keep the last one
|
|
52
|
+
if (key.indexOf('@@@') > -1) {
|
|
53
|
+
key = key.split('@@@')[1]
|
|
54
|
+
}
|
|
55
|
+
const label =
|
|
56
|
+
lang3 && lang3 in keyword.values ? keyword.values[lang3] : keyword.value
|
|
57
|
+
const description =
|
|
58
|
+
lang3 && lang3 in keyword.definitions
|
|
59
|
+
? keyword.definitions[lang3]
|
|
60
|
+
: keyword.definition
|
|
51
61
|
return {
|
|
52
|
-
key
|
|
53
|
-
label
|
|
62
|
+
key,
|
|
63
|
+
label,
|
|
64
|
+
description,
|
|
54
65
|
}
|
|
55
66
|
})
|
|
56
67
|
}
|
|
@@ -14,8 +14,10 @@ import { Organization } from '../../../../../../../libs/common/domain/src/lib/mo
|
|
|
14
14
|
import { Gn4PlatformMapper } from './gn4-platform.mapper'
|
|
15
15
|
import { ltr } from 'semver'
|
|
16
16
|
import { ThesaurusModel } from '../../../../../../../libs/common/domain/src/lib/model/thesaurus/thesaurus.model'
|
|
17
|
+
import { LangService } from '../../../../../../../libs/util/i18n/src'
|
|
17
18
|
|
|
18
19
|
const minApiVersion = '4.2.2'
|
|
20
|
+
|
|
19
21
|
@Injectable()
|
|
20
22
|
export class Gn4PlatformService implements PlatformServiceInterface {
|
|
21
23
|
private readonly type = 'GeoNetwork'
|
|
@@ -50,13 +52,20 @@ export class Gn4PlatformService implements PlatformServiceInterface {
|
|
|
50
52
|
shareReplay(1)
|
|
51
53
|
)
|
|
52
54
|
|
|
55
|
+
/**
|
|
56
|
+
* A map of already loaded thesauri (groups of keywords); the key is a URI
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
private thesauri: Record<string, Observable<ThesaurusModel>> = {}
|
|
60
|
+
|
|
53
61
|
constructor(
|
|
54
62
|
private siteApiService: SiteApiService,
|
|
55
63
|
private meApi: MeApiService,
|
|
56
64
|
private usersApi: UsersApiService,
|
|
57
65
|
private mapper: Gn4PlatformMapper,
|
|
58
66
|
private toolsApiService: ToolsApiService,
|
|
59
|
-
private registriesApiService: RegistriesApiService
|
|
67
|
+
private registriesApiService: RegistriesApiService,
|
|
68
|
+
private langService: LangService
|
|
60
69
|
) {
|
|
61
70
|
this.me$ = this.meApi.getMe().pipe(
|
|
62
71
|
switchMap((apiUser) => this.mapper.userFromMeApi(apiUser)),
|
|
@@ -98,17 +107,47 @@ export class Gn4PlatformService implements PlatformServiceInterface {
|
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
translateKey(key: string): Observable<string> {
|
|
110
|
+
// if the key is a URI, use the registries API to look for the translation
|
|
111
|
+
if (key.match(/^https?:\/\//)) {
|
|
112
|
+
// the thesaurus URI is inferred by removing a part of the keyword URI
|
|
113
|
+
// this is not exact science but it's OK, we'll still end up loading a bunch of keywords at once anyway
|
|
114
|
+
const thesaurusUri = key.replace(/\/([^/]+)$/, '/')
|
|
115
|
+
return this.getThesaurusByUri(thesaurusUri).pipe(
|
|
116
|
+
map((thesaurus) => {
|
|
117
|
+
for (const item of thesaurus) {
|
|
118
|
+
if (item.key === key) return item.label
|
|
119
|
+
}
|
|
120
|
+
return key
|
|
121
|
+
})
|
|
122
|
+
)
|
|
123
|
+
}
|
|
101
124
|
return this.keyTranslations$.pipe(map((translations) => translations[key]))
|
|
102
125
|
}
|
|
103
126
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
.searchKeywords(
|
|
127
|
+
getThesaurusByUri(uri: string): Observable<ThesaurusModel> {
|
|
128
|
+
if (this.thesauri[uri]) {
|
|
129
|
+
return this.thesauri[uri]
|
|
130
|
+
}
|
|
131
|
+
this.thesauri[uri] = this.registriesApiService
|
|
132
|
+
.searchKeywords(
|
|
133
|
+
null,
|
|
134
|
+
this.langService.iso3,
|
|
135
|
+
1000,
|
|
136
|
+
0,
|
|
137
|
+
null,
|
|
138
|
+
null,
|
|
139
|
+
null,
|
|
140
|
+
`${uri}*`
|
|
141
|
+
)
|
|
110
142
|
.pipe(
|
|
111
|
-
map((thesaurus) =>
|
|
143
|
+
map((thesaurus) =>
|
|
144
|
+
this.mapper.thesaurusFromApi(
|
|
145
|
+
thesaurus as any[],
|
|
146
|
+
this.langService.iso3
|
|
147
|
+
)
|
|
148
|
+
),
|
|
149
|
+
shareReplay(1)
|
|
112
150
|
)
|
|
151
|
+
return this.thesauri[uri]
|
|
113
152
|
}
|
|
114
153
|
}
|
|
@@ -60,6 +60,20 @@ export type SpatialRepresentationType =
|
|
|
60
60
|
| 'table'
|
|
61
61
|
| 'point'
|
|
62
62
|
|
|
63
|
+
export type KeywordType = 'place' | 'temporal' | 'theme' | 'other'
|
|
64
|
+
|
|
65
|
+
export type KeywordThesaurus = {
|
|
66
|
+
id: string
|
|
67
|
+
name?: string
|
|
68
|
+
url?: URL
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface Keyword {
|
|
72
|
+
label: string
|
|
73
|
+
type: KeywordType
|
|
74
|
+
thesaurus?: KeywordThesaurus
|
|
75
|
+
}
|
|
76
|
+
|
|
63
77
|
export interface BaseRecord {
|
|
64
78
|
uniqueIdentifier: Uuid
|
|
65
79
|
ownerOrganization: Organization
|
|
@@ -69,8 +83,8 @@ export interface BaseRecord {
|
|
|
69
83
|
recordCreated?: Date
|
|
70
84
|
recordUpdated: Date
|
|
71
85
|
kind: RecordKind
|
|
72
|
-
|
|
73
|
-
keywords: Array<
|
|
86
|
+
topics: Array<string> // TODO: handle codelists
|
|
87
|
+
keywords: Array<Keyword>
|
|
74
88
|
licenses: Array<Constraint>
|
|
75
89
|
legalConstraints: Array<Constraint>
|
|
76
90
|
securityConstraints: Array<Constraint>
|
|
@@ -80,7 +94,6 @@ export interface BaseRecord {
|
|
|
80
94
|
landingPage?: URL
|
|
81
95
|
updateFrequency?: UpdateFrequency
|
|
82
96
|
|
|
83
|
-
// to add: iso19139.topicCategory
|
|
84
97
|
// to add: canonical url
|
|
85
98
|
// to add: source catalog (??)
|
|
86
99
|
// to add: is open data ?
|
|
@@ -15,8 +15,5 @@ export abstract class PlatformServiceInterface {
|
|
|
15
15
|
): Observable<UserModel[]>
|
|
16
16
|
abstract getOrganizations(): Observable<Organization[]>
|
|
17
17
|
abstract translateKey(key: string): Observable<string>
|
|
18
|
-
abstract
|
|
19
|
-
thesaurusName: string,
|
|
20
|
-
lang: string
|
|
21
|
-
): Observable<ThesaurusModel>
|
|
18
|
+
abstract getThesaurusByUri(uri: string): Observable<ThesaurusModel>
|
|
22
19
|
}
|
|
@@ -64,7 +64,7 @@ export const GROUPS_FIXTURE = deepFreeze([
|
|
|
64
64
|
defaultCategory: null,
|
|
65
65
|
allowedCategories: [],
|
|
66
66
|
enableAllowedCategories: false,
|
|
67
|
-
id:
|
|
67
|
+
id: 2,
|
|
68
68
|
email: 'ifremer.ifremer@ifremer.admin.fr',
|
|
69
69
|
referrer: null,
|
|
70
70
|
description: "Institut français de recherche pour l'exploitation de la mer",
|
|
@@ -69,8 +69,24 @@ Cette section contient des *caractères internationaux* (ainsi que des "caractè
|
|
|
69
69
|
url: new URL('http://my-org.net/two.png'),
|
|
70
70
|
},
|
|
71
71
|
],
|
|
72
|
-
keywords: [
|
|
73
|
-
|
|
72
|
+
keywords: [
|
|
73
|
+
{
|
|
74
|
+
thesaurus: { id: 'geonetwork.thesaurus.local' },
|
|
75
|
+
type: 'other',
|
|
76
|
+
label: 'international',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
thesaurus: { id: 'geonetwork.thesaurus.local' },
|
|
80
|
+
type: 'other',
|
|
81
|
+
label: 'test',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
thesaurus: { id: 'geonetwork.thesaurus.local' },
|
|
85
|
+
type: 'other',
|
|
86
|
+
label: '_another_keyword_',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
topics: ['testData', 'exampleData'],
|
|
74
90
|
spatialRepresentation: 'grid',
|
|
75
91
|
distributions: [
|
|
76
92
|
{
|
|
@@ -115,7 +131,7 @@ As such, **it is not very interesting at all.**`,
|
|
|
115
131
|
url: new URL('https://data.rennesmetropole.fr/pages/licence/'),
|
|
116
132
|
},
|
|
117
133
|
],
|
|
118
|
-
|
|
134
|
+
legalConstraints: [
|
|
119
135
|
{
|
|
120
136
|
text: "Dataset access isn't possible since it does not really exist",
|
|
121
137
|
type: 'other',
|
|
@@ -125,6 +141,8 @@ As such, **it is not very interesting at all.**`,
|
|
|
125
141
|
type: 'security',
|
|
126
142
|
},
|
|
127
143
|
],
|
|
144
|
+
securityConstraints: [],
|
|
145
|
+
otherConstraints: [],
|
|
128
146
|
spatialExtents: [],
|
|
129
147
|
temporalExtents: [],
|
|
130
148
|
updateFrequency: {
|
|
@@ -168,13 +186,33 @@ Malgré l'attention portée à la création de ces données, il est rappelé que
|
|
|
168
186
|
},
|
|
169
187
|
],
|
|
170
188
|
keywords: [
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
189
|
+
{
|
|
190
|
+
thesaurus: { id: 'geonetwork.thesaurus.local' },
|
|
191
|
+
type: 'other',
|
|
192
|
+
label: 'planification',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
thesaurus: { id: 'geonetwork.thesaurus.local' },
|
|
196
|
+
type: 'other',
|
|
197
|
+
label: 'PLU',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
thesaurus: { id: 'geonetwork.thesaurus.local' },
|
|
201
|
+
type: 'other',
|
|
202
|
+
label: "Plan local d'urbanisme",
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
thesaurus: { id: 'geonetwork.thesaurus.local' },
|
|
206
|
+
type: 'other',
|
|
207
|
+
label: 'données ouvertes',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
thesaurus: { id: 'geonetwork.thesaurus.local' },
|
|
211
|
+
type: 'other',
|
|
212
|
+
label: 'Avrigny*60036',
|
|
213
|
+
},
|
|
176
214
|
],
|
|
177
|
-
|
|
215
|
+
topics: ['Usage des sols', "document d'urbanisme"],
|
|
178
216
|
spatialRepresentation: 'vector',
|
|
179
217
|
distributions: [
|
|
180
218
|
{
|
|
@@ -189,7 +227,9 @@ Malgré l'attention portée à la création de ces données, il est rappelé que
|
|
|
189
227
|
],
|
|
190
228
|
lineage: `Document d’urbanisme numérisé conformément aux prescriptions nationales du CNIG par le Service d'Information Géographique de l'Agglomération de la Région de Compiègne.
|
|
191
229
|
Ce lot de données produit en 2019, a été numérisé à partir du PCI Vecteur de 2019 et contrôlé par le Service d'Information Géographique de l'Agglomération de la Région de Compiègne.`,
|
|
192
|
-
|
|
230
|
+
legalConstraints: [],
|
|
231
|
+
securityConstraints: [],
|
|
232
|
+
otherConstraints: [],
|
|
193
233
|
useLimitations: ["Aucune condition ne s'applique", 'Licence Ouverte 2.0'],
|
|
194
234
|
licenses: [
|
|
195
235
|
{
|
|
@@ -58,6 +58,8 @@ export class MapContextService {
|
|
|
58
58
|
): Map {
|
|
59
59
|
if (mapConfig) {
|
|
60
60
|
mapContext = this.mergeMapConfigWithContext(mapContext, mapConfig)
|
|
61
|
+
} else {
|
|
62
|
+
mapContext.layers = this.addDefaultBaselayerContext(mapContext.layers)
|
|
61
63
|
}
|
|
62
64
|
if (
|
|
63
65
|
!mapContext.view?.extent &&
|
|
@@ -179,6 +181,14 @@ export class MapContextService {
|
|
|
179
181
|
return view
|
|
180
182
|
}
|
|
181
183
|
|
|
184
|
+
addDefaultBaselayerContext(
|
|
185
|
+
layers: MapContextLayerModel[]
|
|
186
|
+
): MapContextLayerModel[] {
|
|
187
|
+
return layers.includes(DEFAULT_BASELAYER_CONTEXT)
|
|
188
|
+
? layers
|
|
189
|
+
: [DEFAULT_BASELAYER_CONTEXT, ...layers]
|
|
190
|
+
}
|
|
191
|
+
|
|
182
192
|
mergeMapConfigWithContext(
|
|
183
193
|
mapContext: MapContextModel,
|
|
184
194
|
mapConfig: MapConfig
|