geonetwork-ui 2.4.0-dev.aa5f997e → 2.4.0-dev.caf9e4e4
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/iso19139/iso19139.converter.mjs +5 -5
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +30 -2
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.mjs +31 -0
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +23 -1
- package/esm2022/libs/api/metadata-converter/src/lib/xml-utils.mjs +6 -1
- package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
- package/esm2022/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.mjs +138 -0
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.mjs +21 -0
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.mjs +19 -9
- package/esm2022/libs/feature/editor/src/lib/components/record-form/record-form.component.mjs +2 -1
- package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +12 -2
- package/esm2022/libs/feature/map/src/lib/utils/map-utils.service.mjs +3 -4
- package/esm2022/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.mjs +4 -3
- package/esm2022/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.mjs +2 -2
- package/esm2022/libs/ui/elements/src/lib/metadata-info/metadata-info.component.mjs +3 -3
- package/esm2022/libs/ui/inputs/src/lib/image-input/image-input.component.mjs +11 -5
- package/esm2022/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.mjs +3 -3
- package/esm2022/translations/de.json +8 -16
- package/esm2022/translations/en.json +1 -9
- package/esm2022/translations/es.json +1 -9
- package/esm2022/translations/fr.json +1 -9
- package/esm2022/translations/it.json +1 -9
- package/esm2022/translations/nl.json +1 -9
- package/esm2022/translations/pt.json +1 -9
- package/fesm2022/geonetwork-ui.mjs +309 -116
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts +8 -1
- package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.d.ts +5 -0
- package/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.d.ts.map +1 -0
- package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts +3 -1
- package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/xml-utils.d.ts +1 -0
- package/libs/api/metadata-converter/src/lib/xml-utils.d.ts.map +1 -1
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +2 -1
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.d.ts +27 -0
- package/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.d.ts.map +1 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.d.ts +11 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.d.ts.map +1 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts +5 -1
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/components/record-form/record-form.component.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/fields.config.d.ts +1 -0
- package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
- package/libs/feature/map/src/lib/utils/map-utils.service.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts +1 -1
- package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts +1 -1
- package/libs/ui/inputs/src/lib/image-input/image-input.component.d.ts +4 -2
- package/libs/ui/inputs/src/lib/image-input/image-input.component.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +5 -1
- package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +37 -12
- package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +5 -1
- package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +4 -2
- package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +72 -2
- package/src/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.ts +39 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +70 -1
- package/src/libs/api/metadata-converter/src/lib/xml-utils.ts +8 -0
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +2 -1
- package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html +3 -1
- package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts +117 -21
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.css +0 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.html +5 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +22 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +6 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +13 -1
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +1 -0
- package/src/libs/feature/editor/src/lib/fields.config.ts +12 -1
- package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +2 -3
- package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.html +4 -1
- package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.css +2 -1
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +12 -8
- package/src/libs/ui/inputs/src/lib/image-input/image-input.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/image-input/image-input.component.ts +7 -2
- package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +1 -1
- package/translations/de.json +8 -16
- package/translations/en.json +1 -9
- package/translations/es.json +1 -9
- package/translations/fr.json +1 -9
- package/translations/it.json +1 -9
- package/translations/nl.json +1 -9
- package/translations/pt.json +1 -9
- package/translations/sk.json +1 -9
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
UpdateFrequency,
|
|
14
14
|
UpdateFrequencyCustom,
|
|
15
15
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
16
|
+
import { ThesaurusModel } from '../../../../../../libs/common/domain/src/lib/model/thesaurus'
|
|
17
|
+
import { Geometry } from 'geojson'
|
|
16
18
|
import { matchMimeType, matchProtocol } from '../common/distribution.mapper'
|
|
17
19
|
import {
|
|
18
20
|
ChainableFunction,
|
|
@@ -26,21 +28,22 @@ import {
|
|
|
26
28
|
pipe,
|
|
27
29
|
} from '../function-utils'
|
|
28
30
|
import {
|
|
29
|
-
XmlElement,
|
|
30
31
|
findChildElement,
|
|
31
32
|
findChildrenElement,
|
|
32
33
|
findNestedElement,
|
|
33
34
|
findNestedElements,
|
|
34
35
|
findParent,
|
|
36
|
+
firstChildElement,
|
|
35
37
|
readAttribute,
|
|
36
38
|
readText,
|
|
39
|
+
XmlElement,
|
|
37
40
|
} from '../xml-utils'
|
|
41
|
+
import { readGeometry } from './utils/geometry'
|
|
38
42
|
import { fullNameToParts } from './utils/individual-name'
|
|
39
43
|
import { getKeywordTypeFromKeywordTypeCode } from './utils/keyword.mapper'
|
|
40
44
|
import { getRoleFromRoleCode } from './utils/role.mapper'
|
|
41
45
|
import { getStatusFromStatusCode } from './utils/status.mapper'
|
|
42
46
|
import { getUpdateFrequencyFromFrequencyCode } from './utils/update-frequency.mapper'
|
|
43
|
-
import { ThesaurusModel } from '../../../../../../libs/common/domain/src/lib/model/thesaurus'
|
|
44
47
|
|
|
45
48
|
export function extractCharacterString(): ChainableFunction<
|
|
46
49
|
XmlElement,
|
|
@@ -66,6 +69,14 @@ export function extractDateTime(): ChainableFunction<XmlElement, Date> {
|
|
|
66
69
|
)
|
|
67
70
|
}
|
|
68
71
|
|
|
72
|
+
export function extractDecimal(): ChainableFunction<XmlElement, number> {
|
|
73
|
+
return pipe(
|
|
74
|
+
findChildElement('gco:Decimal', false),
|
|
75
|
+
readText(),
|
|
76
|
+
map((numberStr) => (numberStr ? Number(numberStr) : null))
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
69
80
|
export function extractUrl(): ChainableFunction<XmlElement, URL> {
|
|
70
81
|
const getUrl = pipe(findChildElement('gmd:URL', false), readText())
|
|
71
82
|
const getCharacterString = pipe(
|
|
@@ -895,3 +906,62 @@ export function readTemporalExtents(rootEl: XmlElement) {
|
|
|
895
906
|
})
|
|
896
907
|
)(rootEl)
|
|
897
908
|
}
|
|
909
|
+
|
|
910
|
+
export function readSpatialExtents(rootEl: XmlElement) {
|
|
911
|
+
const extractGeometry = (rootEl: XmlElement): Geometry => {
|
|
912
|
+
if (!rootEl) return null
|
|
913
|
+
return pipe(
|
|
914
|
+
findChildElement('gmd:polygon', false),
|
|
915
|
+
firstChildElement,
|
|
916
|
+
map((el) => readGeometry(el))
|
|
917
|
+
)(rootEl)
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
const extractBBox = (
|
|
921
|
+
rootEl: XmlElement
|
|
922
|
+
): [number, number, number, number] => {
|
|
923
|
+
if (!rootEl) return null
|
|
924
|
+
return pipe(
|
|
925
|
+
combine(
|
|
926
|
+
pipe(findChildElement('gmd:westBoundLongitude'), extractDecimal()),
|
|
927
|
+
pipe(findChildElement('gmd:southBoundLatitude'), extractDecimal()),
|
|
928
|
+
pipe(findChildElement('gmd:eastBoundLongitude'), extractDecimal()),
|
|
929
|
+
pipe(findChildElement('gmd:northBoundLatitude'), extractDecimal())
|
|
930
|
+
)
|
|
931
|
+
)(rootEl)
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
const extractDescription = (rootEl: XmlElement): string => {
|
|
935
|
+
if (!rootEl) return null
|
|
936
|
+
return pipe(
|
|
937
|
+
findNestedElement(
|
|
938
|
+
'gmd:geographicIdentifier',
|
|
939
|
+
'gmd:MD_Identifier',
|
|
940
|
+
'gmd:code'
|
|
941
|
+
),
|
|
942
|
+
extractCharacterString()
|
|
943
|
+
)(rootEl)
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
return pipe(
|
|
947
|
+
findIdentification(),
|
|
948
|
+
findNestedElements('gmd:extent', 'gmd:EX_Extent', 'gmd:geographicElement'),
|
|
949
|
+
mapArray(
|
|
950
|
+
combine(
|
|
951
|
+
pipe(findChildElement('gmd:EX_BoundingPolygon'), extractGeometry),
|
|
952
|
+
pipe(findChildElement('gmd:EX_GeographicBoundingBox'), extractBBox),
|
|
953
|
+
pipe(
|
|
954
|
+
findChildElement('gmd:EX_GeographicDescription'),
|
|
955
|
+
extractDescription
|
|
956
|
+
)
|
|
957
|
+
)
|
|
958
|
+
),
|
|
959
|
+
mapArray(([geometry, bbox, description]) => {
|
|
960
|
+
return {
|
|
961
|
+
...(geometry && { geometry }),
|
|
962
|
+
...(bbox && { bbox }),
|
|
963
|
+
...(description && { description }),
|
|
964
|
+
}
|
|
965
|
+
})
|
|
966
|
+
)(rootEl)
|
|
967
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { XmlElement } from '@rgrove/parse-xml'
|
|
2
|
+
import { Geometry } from 'geojson'
|
|
3
|
+
import GML32 from 'ol/format/GML32'
|
|
4
|
+
import GeoJSON from 'ol/format/GeoJSON'
|
|
5
|
+
import { parse } from 'ol/xml'
|
|
6
|
+
import {
|
|
7
|
+
createDocument,
|
|
8
|
+
getRootElement,
|
|
9
|
+
parseXmlString,
|
|
10
|
+
xmlToString,
|
|
11
|
+
} from '../../xml-utils'
|
|
12
|
+
|
|
13
|
+
export function readGeometry(el: XmlElement): Geometry {
|
|
14
|
+
const xmlDoc = createDocument(el)
|
|
15
|
+
xmlDoc.root.attributes['xmlns'] = 'http://www.opengis.net/gml/3.2'
|
|
16
|
+
const gmlString = xmlToString(xmlDoc)
|
|
17
|
+
const doc = parse(gmlString)
|
|
18
|
+
// we need an intermediate node to be able to parse the GML
|
|
19
|
+
const node = document.createElement('pre')
|
|
20
|
+
node.appendChild(doc.documentElement)
|
|
21
|
+
const gml32Format = new GML32()
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
const geometry = gml32Format.readGeometryFromNode(node)
|
|
25
|
+
const geojsonFormat = new GeoJSON()
|
|
26
|
+
return geojsonFormat.writeGeometryObject(geometry)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function writeGeometry(geometryObject: Geometry): XmlElement {
|
|
30
|
+
const geojsonFormat = new GeoJSON()
|
|
31
|
+
const geometry = geojsonFormat.readGeometry(geometryObject)
|
|
32
|
+
const gml32Format = new GML32()
|
|
33
|
+
const node = gml32Format.writeGeometryNode(geometry)
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
const element = node.firstElementChild as HTMLElement
|
|
37
|
+
const gmlString = new XMLSerializer().serializeToString(element)
|
|
38
|
+
return getRootElement(parseXmlString(gmlString))
|
|
39
|
+
}
|
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
UpdateFrequencyCode,
|
|
15
15
|
UpdateFrequencyCustom,
|
|
16
16
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
17
|
+
import { ThesaurusModel } from '../../../../../../libs/common/domain/src/lib/model/thesaurus'
|
|
17
18
|
import format from 'date-fns/format'
|
|
19
|
+
import { Geometry } from 'geojson'
|
|
18
20
|
import {
|
|
19
21
|
ChainableFunction,
|
|
20
22
|
fallback,
|
|
@@ -44,8 +46,8 @@ import {
|
|
|
44
46
|
setTextContent,
|
|
45
47
|
} from '../xml-utils'
|
|
46
48
|
import { readKind } from './read-parts'
|
|
49
|
+
import { writeGeometry } from './utils/geometry'
|
|
47
50
|
import { namePartsToFull } from './utils/individual-name'
|
|
48
|
-
import { ThesaurusModel } from '../../../../../../libs/common/domain/src/lib/model/thesaurus'
|
|
49
51
|
|
|
50
52
|
export function writeCharacterString(
|
|
51
53
|
text: string
|
|
@@ -101,6 +103,14 @@ export function writeDate(
|
|
|
101
103
|
)
|
|
102
104
|
}
|
|
103
105
|
|
|
106
|
+
export function writeDecimal(
|
|
107
|
+
decimal: number
|
|
108
|
+
): ChainableFunction<XmlElement, XmlElement> {
|
|
109
|
+
return tap(
|
|
110
|
+
pipe(findChildOrCreate('gco:Decimal'), setTextContent(decimal.toString()))
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
104
114
|
export function getProgressCode(status: RecordStatus): string {
|
|
105
115
|
switch (status) {
|
|
106
116
|
case 'completed':
|
|
@@ -1184,3 +1194,62 @@ export function writeTemporalExtents(
|
|
|
1184
1194
|
)
|
|
1185
1195
|
)(rootEl)
|
|
1186
1196
|
}
|
|
1197
|
+
|
|
1198
|
+
export function writeSpatialExtents(record: DatasetRecord, rootEl: XmlElement) {
|
|
1199
|
+
const appendBoundingPolygon = (geometry?: Geometry) => {
|
|
1200
|
+
if (!geometry) return null
|
|
1201
|
+
return pipe(
|
|
1202
|
+
createElement('gmd:EX_BoundingPolygon'),
|
|
1203
|
+
appendChildren(
|
|
1204
|
+
pipe(
|
|
1205
|
+
createElement('gmd:polygon'),
|
|
1206
|
+
appendChildren(() => writeGeometry(geometry))
|
|
1207
|
+
)
|
|
1208
|
+
)
|
|
1209
|
+
)
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
const appendGeographicBoundingBox = (
|
|
1213
|
+
bbox?: [number, number, number, number]
|
|
1214
|
+
) => {
|
|
1215
|
+
if (!bbox) return null
|
|
1216
|
+
return pipe(
|
|
1217
|
+
createElement('gmd:EX_GeographicBoundingBox'),
|
|
1218
|
+
appendChildren(
|
|
1219
|
+
pipe(createElement('gmd:westBoundLongitude'), writeDecimal(bbox[0])),
|
|
1220
|
+
pipe(createElement('gmd:eastBoundLongitude'), writeDecimal(bbox[2])),
|
|
1221
|
+
pipe(createElement('gmd:southBoundLatitude'), writeDecimal(bbox[1])),
|
|
1222
|
+
pipe(createElement('gmd:northBoundLatitude'), writeDecimal(bbox[3]))
|
|
1223
|
+
)
|
|
1224
|
+
)
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
const appendGeographicDescription = (description?: string) => {
|
|
1228
|
+
if (!description) return null
|
|
1229
|
+
return pipe(
|
|
1230
|
+
createElement('gmd:EX_GeographicDescription'),
|
|
1231
|
+
createChild('gmd:geographicIdentifier'),
|
|
1232
|
+
createChild('gmd:MD_Identifier'),
|
|
1233
|
+
createChild('gmd:code'),
|
|
1234
|
+
writeCharacterString(description)
|
|
1235
|
+
)
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
pipe(
|
|
1239
|
+
findOrCreateIdentification(),
|
|
1240
|
+
findNestedChildOrCreate('gmd:extent', 'gmd:EX_Extent'),
|
|
1241
|
+
removeChildrenByName('gmd:geographicElement'),
|
|
1242
|
+
appendChildren(
|
|
1243
|
+
...record.spatialExtents.map((extent) =>
|
|
1244
|
+
pipe(
|
|
1245
|
+
createElement('gmd:geographicElement'),
|
|
1246
|
+
appendChildren(
|
|
1247
|
+
appendBoundingPolygon(extent.geometry),
|
|
1248
|
+
appendGeographicBoundingBox(extent.bbox),
|
|
1249
|
+
appendGeographicDescription(extent.description)
|
|
1250
|
+
)
|
|
1251
|
+
)
|
|
1252
|
+
)
|
|
1253
|
+
)
|
|
1254
|
+
)(rootEl)
|
|
1255
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
XmlText,
|
|
7
7
|
} from '@rgrove/parse-xml'
|
|
8
8
|
import { ChainableFunction, fallback } from './function-utils'
|
|
9
|
+
|
|
9
10
|
export { XmlDocument, XmlElement } from '@rgrove/parse-xml'
|
|
10
11
|
|
|
11
12
|
export class XmlParseError extends Error {
|
|
@@ -115,6 +116,10 @@ export function allChildrenElement(element: XmlElement): Array<XmlElement> {
|
|
|
115
116
|
] as Array<XmlElement>
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
export function firstChildElement(element: XmlElement): XmlElement {
|
|
120
|
+
return allChildrenElement(element)[0] ?? null
|
|
121
|
+
}
|
|
122
|
+
|
|
118
123
|
/**
|
|
119
124
|
* Will return all matching elements nested according to the given
|
|
120
125
|
* names (similar to a path), starting form the input element;
|
|
@@ -228,6 +233,7 @@ export function xmlToString(
|
|
|
228
233
|
${padding}<${el.name}${attrs}/>
|
|
229
234
|
${parentPadding}`
|
|
230
235
|
}
|
|
236
|
+
|
|
231
237
|
return `
|
|
232
238
|
${padding}<${el.name}${attrs}>${children}</${el.name}>
|
|
233
239
|
${parentPadding}`
|
|
@@ -307,11 +313,13 @@ function getTreeRoot(element: XmlElement): XmlElement {
|
|
|
307
313
|
|
|
308
314
|
// stays on the parent element
|
|
309
315
|
// if the given elements are part of a subtree, will add the root of subtree
|
|
316
|
+
// will filter out falsy elements
|
|
310
317
|
export function appendChildren(
|
|
311
318
|
...childrenFns: Array<ChainableFunction<void, XmlElement>>
|
|
312
319
|
): ChainableFunction<XmlElement, XmlElement> {
|
|
313
320
|
return (element) => {
|
|
314
321
|
if (!element) return null
|
|
322
|
+
childrenFns = childrenFns.filter((fn) => fn)
|
|
315
323
|
element.children.push(...childrenFns.map((fn) => fn()).map(getTreeRoot))
|
|
316
324
|
element.children.forEach((el) => (el.parent = element))
|
|
317
325
|
return element
|
package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<gn-ui-image-input
|
|
2
2
|
[maxSizeMB]="5"
|
|
3
3
|
[previewUrl]="resourceUrl"
|
|
4
|
-
[altText]="
|
|
4
|
+
[altText]="resourceAltText"
|
|
5
|
+
[formControl]="formControl"
|
|
5
6
|
(fileChange)="handleFileChange($event)"
|
|
6
7
|
(urlChange)="handleUrlChange($event)"
|
|
8
|
+
(altTextChange)="handleAltTextChange($event)"
|
|
7
9
|
(delete)="handleDelete()"
|
|
8
10
|
></gn-ui-image-input>
|
package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts
CHANGED
|
@@ -2,12 +2,24 @@ import {
|
|
|
2
2
|
ChangeDetectionStrategy,
|
|
3
3
|
ChangeDetectorRef,
|
|
4
4
|
Component,
|
|
5
|
+
EventEmitter,
|
|
5
6
|
Input,
|
|
7
|
+
OnChanges,
|
|
6
8
|
OnInit,
|
|
9
|
+
Output,
|
|
10
|
+
SimpleChanges,
|
|
7
11
|
} from '@angular/core'
|
|
8
12
|
import { CommonModule } from '@angular/common'
|
|
9
13
|
import { RecordsApiService } from '../../../../../../../libs/data-access/gn4/src'
|
|
10
14
|
import { UiInputsModule } from '../../../../../../../libs/ui/inputs/src'
|
|
15
|
+
import { FormControl } from '@angular/forms'
|
|
16
|
+
import { GraphicOverview } from '../../../../../../../libs/common/domain/src/lib/model/record'
|
|
17
|
+
|
|
18
|
+
const extractFileNameFromUrl = (url: string): string => {
|
|
19
|
+
const pattern = new RegExp(`attachments/([^/?#]+)(?:[/?#]|$)`, 'i')
|
|
20
|
+
const match = url.match(pattern)
|
|
21
|
+
return match ? match[1] : ''
|
|
22
|
+
}
|
|
11
23
|
|
|
12
24
|
@Component({
|
|
13
25
|
selector: 'gn-ui-overview-upload',
|
|
@@ -17,11 +29,15 @@ import { UiInputsModule } from '../../../../../../../libs/ui/inputs/src'
|
|
|
17
29
|
styleUrls: ['./overview-upload.component.css'],
|
|
18
30
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
19
31
|
})
|
|
20
|
-
export class OverviewUploadComponent implements OnInit {
|
|
32
|
+
export class OverviewUploadComponent implements OnInit, OnChanges {
|
|
21
33
|
@Input() metadataUuid: string
|
|
34
|
+
@Input() formControl!: FormControl
|
|
35
|
+
@Output() overviewChange = new EventEmitter<GraphicOverview | null>()
|
|
36
|
+
@Output() altTextChange: EventEmitter<string> = new EventEmitter()
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
resourceAltText = '' // = ressourceFileName by default
|
|
39
|
+
resourceFileName = ''
|
|
40
|
+
resourceUrl: URL
|
|
25
41
|
|
|
26
42
|
constructor(
|
|
27
43
|
private recordsApiService: RecordsApiService,
|
|
@@ -29,42 +45,122 @@ export class OverviewUploadComponent implements OnInit {
|
|
|
29
45
|
) {}
|
|
30
46
|
|
|
31
47
|
ngOnInit(): void {
|
|
32
|
-
this.recordsApiService
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
this.recordsApiService.getAllResources(this.metadataUuid).subscribe({
|
|
49
|
+
next: (resources) => {
|
|
50
|
+
if (resources && resources.length > 0) {
|
|
51
|
+
this.resourceUrl = new URL(resources[0]?.url)
|
|
52
|
+
this.resourceAltText =
|
|
53
|
+
this.resourceAltText === ''
|
|
54
|
+
? resources[0].filename
|
|
55
|
+
: this.resourceAltText
|
|
56
|
+
this.resourceFileName = extractFileNameFromUrl(resources[0]?.url)
|
|
57
|
+
} else {
|
|
58
|
+
this.resourceUrl = null
|
|
59
|
+
this.resourceAltText = ''
|
|
60
|
+
this.resourceFileName = ''
|
|
61
|
+
}
|
|
62
|
+
|
|
37
63
|
this.cd.markForCheck()
|
|
38
|
-
}
|
|
64
|
+
},
|
|
65
|
+
error: this.errorHandle,
|
|
66
|
+
})
|
|
39
67
|
}
|
|
40
68
|
|
|
41
69
|
handleFileChange(file: File) {
|
|
42
70
|
this.recordsApiService
|
|
43
71
|
.putResource(this.metadataUuid, file, 'public')
|
|
44
|
-
.subscribe(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
72
|
+
.subscribe({
|
|
73
|
+
next: (resource) => {
|
|
74
|
+
this.resourceUrl = new URL(resource.url)
|
|
75
|
+
this.resourceAltText = resource.filename
|
|
76
|
+
|
|
77
|
+
this.overviewChange.emit({
|
|
78
|
+
url: new URL(resource.url),
|
|
79
|
+
description: resource.filename,
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
this.cd.markForCheck()
|
|
83
|
+
},
|
|
84
|
+
error: this.errorHandle,
|
|
48
85
|
})
|
|
49
86
|
}
|
|
50
87
|
|
|
51
88
|
handleUrlChange(url: string) {
|
|
52
89
|
this.recordsApiService
|
|
53
90
|
.putResourceFromURL(this.metadataUuid, url, 'public')
|
|
54
|
-
.subscribe(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
91
|
+
.subscribe({
|
|
92
|
+
next: (resource) => {
|
|
93
|
+
this.resourceUrl = new URL(resource.url)
|
|
94
|
+
this.resourceAltText = resource.filename
|
|
95
|
+
|
|
96
|
+
this.overviewChange.emit({
|
|
97
|
+
url: new URL(resource.url),
|
|
98
|
+
description: resource.filename,
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
this.cd.markForCheck()
|
|
102
|
+
},
|
|
103
|
+
error: this.errorHandle,
|
|
58
104
|
})
|
|
59
105
|
}
|
|
60
106
|
|
|
107
|
+
handleAltTextChange(newAltText: string) {
|
|
108
|
+
this.resourceAltText = newAltText
|
|
109
|
+
|
|
110
|
+
this.overviewChange.emit({
|
|
111
|
+
url: this.resourceUrl,
|
|
112
|
+
description: this.resourceAltText,
|
|
113
|
+
})
|
|
114
|
+
this.cd.markForCheck()
|
|
115
|
+
}
|
|
116
|
+
|
|
61
117
|
handleDelete() {
|
|
62
118
|
this.recordsApiService
|
|
63
119
|
.delResource(this.metadataUuid, this.resourceFileName)
|
|
64
|
-
.subscribe(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
120
|
+
.subscribe({
|
|
121
|
+
next: () => {
|
|
122
|
+
this.resourceAltText = null
|
|
123
|
+
this.resourceUrl = null
|
|
124
|
+
|
|
125
|
+
this.overviewChange.emit(null)
|
|
126
|
+
|
|
127
|
+
this.cd.markForCheck()
|
|
128
|
+
},
|
|
129
|
+
error: this.errorHandle,
|
|
68
130
|
})
|
|
69
131
|
}
|
|
132
|
+
|
|
133
|
+
private errorHandle = (error: never) => {
|
|
134
|
+
console.error(error)
|
|
135
|
+
|
|
136
|
+
this.resourceUrl = null
|
|
137
|
+
this.resourceAltText = ''
|
|
138
|
+
this.resourceFileName = ''
|
|
139
|
+
|
|
140
|
+
this.overviewChange.emit(null)
|
|
141
|
+
|
|
142
|
+
this.cd.markForCheck()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
146
|
+
const overviewChanges = changes['formControl']
|
|
147
|
+
if (
|
|
148
|
+
overviewChanges &&
|
|
149
|
+
overviewChanges.currentValue !== overviewChanges.previousValue
|
|
150
|
+
) {
|
|
151
|
+
let overview: GraphicOverview
|
|
152
|
+
if (
|
|
153
|
+
overviewChanges.currentValue.value &&
|
|
154
|
+
overviewChanges.currentValue.value.length > 0
|
|
155
|
+
) {
|
|
156
|
+
overview = overviewChanges.currentValue.value[0] as GraphicOverview
|
|
157
|
+
} else {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
if (overview.description && overview.description !== '') {
|
|
161
|
+
this.resourceAltText = overview.description
|
|
162
|
+
this.cd.markForCheck()
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
70
166
|
}
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common'
|
|
2
|
+
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
|
3
|
+
import { OverviewUploadComponent } from '../../../overview-upload/overview-upload.component'
|
|
4
|
+
import { FormControl } from '@angular/forms'
|
|
5
|
+
import { GraphicOverview } from '../../../../../../../../../libs/common/domain/src/lib/model/record'
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'gn-ui-form-field-overviews',
|
|
9
|
+
templateUrl: './form-field-overviews.component.html',
|
|
10
|
+
styleUrls: ['./form-field-overviews.component.css'],
|
|
11
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
12
|
+
standalone: true,
|
|
13
|
+
imports: [CommonModule, OverviewUploadComponent],
|
|
14
|
+
})
|
|
15
|
+
export class FormFieldOverviewsComponent {
|
|
16
|
+
@Input() metadataUuid: string
|
|
17
|
+
@Input() control!: FormControl
|
|
18
|
+
|
|
19
|
+
handleOverviewChange(overView: GraphicOverview | null) {
|
|
20
|
+
this.control.setValue(overView ? [overView] : [])
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html
CHANGED
|
@@ -76,6 +76,12 @@
|
|
|
76
76
|
<ng-container *ngIf="isSpatialExtentField">
|
|
77
77
|
<gn-ui-form-field-spatial-extent></gn-ui-form-field-spatial-extent>
|
|
78
78
|
</ng-container>
|
|
79
|
+
<ng-container *ngIf="isGraphicOverview">
|
|
80
|
+
<gn-ui-form-field-overviews
|
|
81
|
+
[control]="formControl"
|
|
82
|
+
[metadataUuid]="metadataUuid$ | async"
|
|
83
|
+
></gn-ui-form-field-overviews>
|
|
84
|
+
</ng-container>
|
|
79
85
|
<ng-container *ngIf="isKeywords">
|
|
80
86
|
<gn-ui-form-field-keywords
|
|
81
87
|
[control]="formControl"
|
package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts
CHANGED
|
@@ -28,6 +28,9 @@ import { FormFieldSpatialExtentComponent } from './form-field-spatial-extent/for
|
|
|
28
28
|
import { FormFieldUpdateFrequencyComponent } from './form-field-update-frequency/form-field-update-frequency.component'
|
|
29
29
|
import { CatalogRecordKeys } from '../../../../../../../../libs/common/domain/src/lib/model/record'
|
|
30
30
|
import { FormFieldKeywordsComponent } from './form-field-keywords/form-field-keywords.component'
|
|
31
|
+
import { FormFieldOverviewsComponent } from './form-field-overviews/form-field-overviews.component'
|
|
32
|
+
import { map, take } from 'rxjs/operators'
|
|
33
|
+
import { EditorFacade } from '../../../+state/editor.facade'
|
|
31
34
|
import { FormFieldConfig } from '../../../models'
|
|
32
35
|
|
|
33
36
|
@Component({
|
|
@@ -55,6 +58,7 @@ import { FormFieldConfig } from '../../../models'
|
|
|
55
58
|
FormFieldArrayComponent,
|
|
56
59
|
FormFieldKeywordsComponent,
|
|
57
60
|
TranslateModule,
|
|
61
|
+
FormFieldOverviewsComponent,
|
|
58
62
|
],
|
|
59
63
|
})
|
|
60
64
|
export class FormFieldComponent {
|
|
@@ -70,9 +74,14 @@ export class FormFieldComponent {
|
|
|
70
74
|
|
|
71
75
|
@ViewChild('titleInput') titleInput: ElementRef
|
|
72
76
|
|
|
77
|
+
metadataUuid$ = this.facade.record$.pipe(
|
|
78
|
+
take(1),
|
|
79
|
+
map((record) => record.uniqueIdentifier)
|
|
80
|
+
)
|
|
81
|
+
|
|
73
82
|
formControl = new FormControl()
|
|
74
83
|
|
|
75
|
-
constructor() {
|
|
84
|
+
constructor(private facade: EditorFacade) {
|
|
76
85
|
this.valueChange = this.formControl.valueChanges
|
|
77
86
|
}
|
|
78
87
|
|
|
@@ -101,6 +110,9 @@ export class FormFieldComponent {
|
|
|
101
110
|
get isSpatialExtentField() {
|
|
102
111
|
return this.model === 'spatialExtents'
|
|
103
112
|
}
|
|
113
|
+
get isGraphicOverview() {
|
|
114
|
+
return this.model === 'overviews'
|
|
115
|
+
}
|
|
104
116
|
get isSimpleField() {
|
|
105
117
|
return this.model === 'uniqueIdentifier' || this.model === 'recordUpdated'
|
|
106
118
|
}
|
|
@@ -83,6 +83,13 @@ export const RECORD_ABSTRACT_FIELD: EditorField = {
|
|
|
83
83
|
},
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
export const RECORD_GRAPHICAL_OVERVIEW_FIELD: EditorField = {
|
|
87
|
+
model: 'overviews',
|
|
88
|
+
formFieldConfig: {
|
|
89
|
+
labelKey: marker('editor.record.form.field.overviews'),
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
86
93
|
/************************************************************
|
|
87
94
|
*************** SECTIONS *****************
|
|
88
95
|
************************************************************
|
|
@@ -90,7 +97,11 @@ export const RECORD_ABSTRACT_FIELD: EditorField = {
|
|
|
90
97
|
|
|
91
98
|
export const TITLE_SECTION: EditorSection = {
|
|
92
99
|
hidden: false,
|
|
93
|
-
fields: [
|
|
100
|
+
fields: [
|
|
101
|
+
RECORD_TITLE_FIELD,
|
|
102
|
+
RECORD_ABSTRACT_FIELD,
|
|
103
|
+
RECORD_GRAPHICAL_OVERVIEW_FIELD,
|
|
104
|
+
],
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
export const ABOUT_SECTION: EditorSection = {
|
|
@@ -220,11 +220,10 @@ export class MapUtilsService {
|
|
|
220
220
|
if (!('spatialExtents' in record) || record.spatialExtents.length === 0) {
|
|
221
221
|
return null
|
|
222
222
|
}
|
|
223
|
-
//
|
|
223
|
+
// extend all the spatial extents bbox into an including bbox
|
|
224
224
|
const totalExtent = record.spatialExtents.reduce(
|
|
225
225
|
(prev, curr) => {
|
|
226
|
-
|
|
227
|
-
return extend(prev, geom.getExtent())
|
|
226
|
+
return extend(prev, curr.bbox)
|
|
228
227
|
},
|
|
229
228
|
[Infinity, Infinity, -Infinity, -Infinity]
|
|
230
229
|
)
|