geonetwork-ui 2.11.0-dev.0504d85cc → 2.11.0-dev.0559cfd9d
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/fesm2022/geonetwork-ui.mjs +1347 -1152
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +117 -64
- package/index.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +2 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/mel.records.dataset+featurecatalog.ts +395 -0
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +35 -8
- package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +19 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +17 -6
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +110 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +131 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +9 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +2 -1
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +29 -3
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +5 -34
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +14 -18
- package/src/libs/common/domain/src/lib/model/search/aggregation.model.ts +2 -0
- package/src/libs/common/domain/src/lib/model/search/filter.model.ts +4 -1
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +1 -1
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +42 -62
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +32 -19
- package/src/libs/feature/map/src/index.ts +0 -1
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +8 -3
- package/src/libs/feature/record/src/lib/state/mdview.effects.ts +3 -1
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +7 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +26 -4
- package/src/libs/feature/search/src/lib/geocoding/geocoding.service.ts +52 -8
- package/src/libs/feature/search/src/lib/location-search/location-search.component.html +11 -1
- package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +77 -3
- package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +1 -0
- package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +56 -4
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +33 -9
- package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +3 -3
- package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.ts +2 -2
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.css +0 -4
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +2 -2
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +3 -3
- package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +2 -1
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +7 -1
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +26 -3
- package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +1 -0
- package/src/libs/util/app-config/src/lib/app-config.ts +34 -0
- package/src/libs/util/app-config/src/lib/fixtures.ts +37 -1
- package/src/libs/util/app-config/src/lib/model.ts +24 -1
- package/translations/de.json +8 -2
- package/translations/en.json +8 -2
- package/translations/es.json +8 -2
- package/translations/fr.json +12 -6
- package/translations/it.json +8 -2
- package/translations/nl.json +8 -2
- package/translations/pt.json +8 -2
- package/translations/sk.json +8 -2
- package/src/libs/feature/map/src/lib/style/index.ts +0 -2
- package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
- package/src/libs/feature/map/src/lib/style/map-style.service.ts +0 -108
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CatalogRecord,
|
|
3
|
+
DatasetFeatureAttribute,
|
|
4
|
+
DatasetFeatureAttributeValue,
|
|
5
|
+
DatasetFeatureType,
|
|
3
6
|
DatasetRecord,
|
|
4
7
|
Individual,
|
|
5
8
|
LanguageCode,
|
|
@@ -616,3 +619,131 @@ export function writeAssociatedRecords(
|
|
|
616
619
|
)
|
|
617
620
|
)(rootEl)
|
|
618
621
|
}
|
|
622
|
+
|
|
623
|
+
function createNilElement(name: string, nilReason: string) {
|
|
624
|
+
return pipe(createElement(name), writeAttribute('gco:nilReason', nilReason))
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function createFeatureCatalogueShell() {
|
|
628
|
+
return pipe(
|
|
629
|
+
createNestedElement(
|
|
630
|
+
'mdb:contentInfo',
|
|
631
|
+
'mrc:MD_FeatureCatalogue',
|
|
632
|
+
'mrc:featureCatalogue',
|
|
633
|
+
'gfc:FC_FeatureCatalogue'
|
|
634
|
+
),
|
|
635
|
+
// create mandatory elements which are not part of the model yet
|
|
636
|
+
appendChildren(
|
|
637
|
+
createNilElement('cat:name', 'missing'),
|
|
638
|
+
createNilElement('cat:scope', 'missing'),
|
|
639
|
+
createNilElement('cat:versionNumber', 'missing'),
|
|
640
|
+
createNilElement('cat:versionDate', 'missing'),
|
|
641
|
+
createNilElement('gfc:producer', 'missing')
|
|
642
|
+
)
|
|
643
|
+
)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function createListedValue(value: DatasetFeatureAttributeValue) {
|
|
647
|
+
return pipe(
|
|
648
|
+
createNestedElement('gfc:listedValue', 'gfc:FC_ListedValue'),
|
|
649
|
+
appendChildren(
|
|
650
|
+
value.label
|
|
651
|
+
? pipe(createElement('gfc:label'), writeCharacterString(value.label))
|
|
652
|
+
: createNilElement('gfc:label', 'missing'),
|
|
653
|
+
value.value
|
|
654
|
+
? pipe(createElement('gfc:code'), writeCharacterString(value.value))
|
|
655
|
+
: null,
|
|
656
|
+
value.description
|
|
657
|
+
? pipe(
|
|
658
|
+
createElement('gfc:definition'),
|
|
659
|
+
writeCharacterString(value.description)
|
|
660
|
+
)
|
|
661
|
+
: null
|
|
662
|
+
)
|
|
663
|
+
)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function createFeatureAttribute(attribute: DatasetFeatureAttribute) {
|
|
667
|
+
return pipe(
|
|
668
|
+
createNestedElement(
|
|
669
|
+
'gfc:carrierOfCharacteristics',
|
|
670
|
+
'gfc:FC_FeatureAttribute'
|
|
671
|
+
),
|
|
672
|
+
appendChildren(
|
|
673
|
+
pipe(createElement('gfc:memberName'), setTextContent(attribute.name)),
|
|
674
|
+
attribute.description
|
|
675
|
+
? pipe(
|
|
676
|
+
createElement('gfc:definition'),
|
|
677
|
+
writeCharacterString(attribute.description)
|
|
678
|
+
)
|
|
679
|
+
: null,
|
|
680
|
+
createNilElement('gfc:cardinality', 'missing'),
|
|
681
|
+
attribute.label
|
|
682
|
+
? pipe(
|
|
683
|
+
createElement('gfc:designation'),
|
|
684
|
+
writeCharacterString(attribute.label)
|
|
685
|
+
)
|
|
686
|
+
: null,
|
|
687
|
+
attribute.code
|
|
688
|
+
? pipe(createElement('gfc:code'), writeCharacterString(attribute.code))
|
|
689
|
+
: null,
|
|
690
|
+
attribute.type
|
|
691
|
+
? pipe(
|
|
692
|
+
createNestedElement('gfc:valueType', 'gco:TypeName', 'gco:aName'),
|
|
693
|
+
writeCharacterString(attribute.type)
|
|
694
|
+
)
|
|
695
|
+
: null,
|
|
696
|
+
...(attribute.values ?? [])
|
|
697
|
+
.filter((value) => value.value || value.label)
|
|
698
|
+
.map(createListedValue)
|
|
699
|
+
)
|
|
700
|
+
)
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function createFeatureType(featureType: DatasetFeatureType) {
|
|
704
|
+
return pipe(
|
|
705
|
+
createNestedElement('gfc:featureType', 'gfc:FC_FeatureType'),
|
|
706
|
+
appendChildren(
|
|
707
|
+
pipe(createElement('gfc:typeName'), setTextContent(featureType.name)),
|
|
708
|
+
featureType.description
|
|
709
|
+
? pipe(
|
|
710
|
+
createElement('gfc:definition'),
|
|
711
|
+
writeCharacterString(featureType.description)
|
|
712
|
+
)
|
|
713
|
+
: null,
|
|
714
|
+
pipe(
|
|
715
|
+
createNestedElement('gfc:isAbstract', 'gco:Boolean'),
|
|
716
|
+
setTextContent('false')
|
|
717
|
+
),
|
|
718
|
+
featureType.label
|
|
719
|
+
? pipe(
|
|
720
|
+
createElement('gfc:designation'),
|
|
721
|
+
writeCharacterString(featureType.label)
|
|
722
|
+
)
|
|
723
|
+
: null,
|
|
724
|
+
...featureType.attributes.map(createFeatureAttribute),
|
|
725
|
+
createNilElement('gfc:featureCatalogue', 'missing')
|
|
726
|
+
)
|
|
727
|
+
)
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
export function writeFeatureTypeDescriptions(
|
|
731
|
+
record: DatasetRecord,
|
|
732
|
+
rootEl: XmlElement
|
|
733
|
+
) {
|
|
734
|
+
const featureTypes = record.featureTypeDescriptions ?? []
|
|
735
|
+
const catalogues = findNestedElements(
|
|
736
|
+
'mdb:contentInfo',
|
|
737
|
+
'mrc:MD_FeatureCatalogue',
|
|
738
|
+
'mrc:featureCatalogue',
|
|
739
|
+
'gfc:FC_FeatureCatalogue'
|
|
740
|
+
)(rootEl)
|
|
741
|
+
catalogues.forEach((catalogue) =>
|
|
742
|
+
removeChildrenByName('gfc:featureType')(catalogue)
|
|
743
|
+
)
|
|
744
|
+
if (!featureTypes.length) return
|
|
745
|
+
|
|
746
|
+
const catalogue =
|
|
747
|
+
catalogues[0] ?? appendChildTree(createFeatureCatalogueShell())(rootEl)
|
|
748
|
+
appendChildren(...featureTypes.map(createFeatureType))(catalogue)
|
|
749
|
+
}
|
|
@@ -118,6 +118,7 @@ export class Iso19139Converter extends BaseConverter<string> {
|
|
|
118
118
|
lineage: readLineage,
|
|
119
119
|
sourceRecords: readSourceRecords,
|
|
120
120
|
associatedRecords: readAssociatedRecords,
|
|
121
|
+
featureTypeDescriptions: () => undefined,
|
|
121
122
|
onlineResources: readOnlineResources,
|
|
122
123
|
temporalExtents: readTemporalExtents,
|
|
123
124
|
spatialExtents: readSpatialExtents,
|
|
@@ -161,6 +162,7 @@ export class Iso19139Converter extends BaseConverter<string> {
|
|
|
161
162
|
lineage: writeLineage,
|
|
162
163
|
sourceRecords: writeSourceRecords,
|
|
163
164
|
associatedRecords: writeAssociatedRecords,
|
|
165
|
+
featureTypeDescriptions: () => undefined,
|
|
164
166
|
onlineResources: writeOnlineResources,
|
|
165
167
|
temporalExtents: writeTemporalExtents,
|
|
166
168
|
spatialExtents: writeSpatialExtents,
|
|
@@ -306,6 +308,10 @@ export class Iso19139Converter extends BaseConverter<string> {
|
|
|
306
308
|
const sourceRecords = this.readers['sourceRecords'](rootEl, tr)
|
|
307
309
|
const associatedRecords = this.readers['associatedRecords'](rootEl, tr)
|
|
308
310
|
const updateFrequency = this.readers['updateFrequency'](rootEl, tr)
|
|
311
|
+
const featureTypeDescriptions = this.readers['featureTypeDescriptions'](
|
|
312
|
+
rootEl,
|
|
313
|
+
tr
|
|
314
|
+
) as DatasetRecord['featureTypeDescriptions']
|
|
309
315
|
|
|
310
316
|
return this.afterRecordRead({
|
|
311
317
|
...this.readBaseRecord(rootEl, tr),
|
|
@@ -317,6 +323,7 @@ export class Iso19139Converter extends BaseConverter<string> {
|
|
|
317
323
|
...(spatialRepresentation && { spatialRepresentation }),
|
|
318
324
|
temporalExtents,
|
|
319
325
|
updateFrequency,
|
|
326
|
+
...(featureTypeDescriptions?.length && { featureTypeDescriptions }),
|
|
320
327
|
translations: tr,
|
|
321
328
|
} as DatasetRecord)
|
|
322
329
|
} else if (kind === 'reuse') {
|
|
@@ -416,6 +423,8 @@ export class Iso19139Converter extends BaseConverter<string> {
|
|
|
416
423
|
|
|
417
424
|
if (record.kind === 'dataset') {
|
|
418
425
|
fieldChanged('status') && this.writers['status'](record, rootEl)
|
|
426
|
+
fieldChanged('featureTypeDescriptions') &&
|
|
427
|
+
this.writers['featureTypeDescriptions'](record, rootEl)
|
|
419
428
|
}
|
|
420
429
|
if (record.kind === 'dataset' || record.kind === 'reuse') {
|
|
421
430
|
fieldChanged('updateFrequency') &&
|
|
@@ -69,7 +69,8 @@ export function extractCharacterString(): ChainableFunction<
|
|
|
69
69
|
fallback(
|
|
70
70
|
findChildElement('gco:CharacterString', false),
|
|
71
71
|
findChildElement('gmx:Anchor', false),
|
|
72
|
-
findChildElement('gmx:MimeFileType', false)
|
|
72
|
+
findChildElement('gmx:MimeFileType', false),
|
|
73
|
+
findChildElement('gco:LocalName', false)
|
|
73
74
|
),
|
|
74
75
|
readText()
|
|
75
76
|
)
|
|
@@ -59,6 +59,8 @@ export class ElasticsearchService {
|
|
|
59
59
|
{ script: string; type: 'keyword' | 'date' }
|
|
60
60
|
> = {}
|
|
61
61
|
|
|
62
|
+
private fieldAliases: Record<string, string> = {}
|
|
63
|
+
|
|
62
64
|
// we're using getters in case the defined languages change over time
|
|
63
65
|
private get metadataLang(): LanguageCode {
|
|
64
66
|
const mdLangValue = this.injector.get(METADATA_LANGUAGE, null)
|
|
@@ -155,6 +157,10 @@ export class ElasticsearchService {
|
|
|
155
157
|
this.runtimeFields[fieldName] = { script: expression, type }
|
|
156
158
|
}
|
|
157
159
|
|
|
160
|
+
registerFieldAlias(fieldIdentifier: string, esFieldName: string) {
|
|
161
|
+
this.fieldAliases[fieldIdentifier] = esFieldName
|
|
162
|
+
}
|
|
163
|
+
|
|
158
164
|
getMetadataByIdsPayload(uuids: string[]): EsSearchParams {
|
|
159
165
|
return {
|
|
160
166
|
query: {
|
|
@@ -293,12 +299,25 @@ export class ElasticsearchService {
|
|
|
293
299
|
return Object.values(filters).find(isBoundingBox)
|
|
294
300
|
}
|
|
295
301
|
|
|
296
|
-
//
|
|
297
|
-
// the record matches if that interval intersects the filter range
|
|
302
|
+
// builds a query matching records whose dates intersect the filter range
|
|
298
303
|
private buildDateRangeQuery(searchField: string, dateRange: DateRange) {
|
|
304
|
+
// fields ending with 'DateRange' maps as ES range type
|
|
305
|
+
if (searchField.endsWith('DateRange')) {
|
|
306
|
+
return {
|
|
307
|
+
range: {
|
|
308
|
+
[searchField]: {
|
|
309
|
+
...(dateRange.start && { gte: formatDate(dateRange.start) }),
|
|
310
|
+
...(dateRange.end && { lte: formatDate(dateRange.end) }),
|
|
311
|
+
format: 'yyyy-MM-dd',
|
|
312
|
+
relation: 'intersects',
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
}
|
|
316
|
+
}
|
|
299
317
|
const minField = `${searchField}Min`
|
|
300
318
|
const maxField = `${searchField}Max`
|
|
301
319
|
if (minField in this.runtimeFields && maxField in this.runtimeFields) {
|
|
320
|
+
// min/max runtime fields form an interval per record; match if it intersects the filter range
|
|
302
321
|
const filter = [
|
|
303
322
|
dateRange.start && {
|
|
304
323
|
range: {
|
|
@@ -362,7 +381,8 @@ export class ElasticsearchService {
|
|
|
362
381
|
JSON.stringify(filters[fieldname]) !== '{}'
|
|
363
382
|
)
|
|
364
383
|
.map(
|
|
365
|
-
(fieldname) =>
|
|
384
|
+
(fieldname) =>
|
|
385
|
+
`${this.fieldAliases[fieldname] ?? fieldname}:(${makeQuery(filters[fieldname])})`
|
|
366
386
|
)
|
|
367
387
|
.join(' AND ')
|
|
368
388
|
if (filters['gn-ui-crossFieldFilter']) {
|
|
@@ -708,6 +728,12 @@ export class ElasticsearchService {
|
|
|
708
728
|
order: {
|
|
709
729
|
[`_${aggregation.sort[1]}`]: aggregation.sort[0],
|
|
710
730
|
},
|
|
731
|
+
...(aggregation.includeValues && {
|
|
732
|
+
include: aggregation.includeValues,
|
|
733
|
+
}),
|
|
734
|
+
...(aggregation.excludeValues && {
|
|
735
|
+
exclude: aggregation.excludeValues,
|
|
736
|
+
}),
|
|
711
737
|
},
|
|
712
738
|
}
|
|
713
739
|
case 'histogram':
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
AssociatedRecord,
|
|
18
18
|
CatalogRecord,
|
|
19
19
|
DatasetFeatureCatalog,
|
|
20
|
-
DatasetFeatureType,
|
|
21
20
|
LanguageCode,
|
|
22
21
|
LinkedRecord,
|
|
23
22
|
RecordRelation,
|
|
@@ -182,43 +181,15 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
182
181
|
)
|
|
183
182
|
}
|
|
184
183
|
|
|
185
|
-
|
|
186
|
-
featureTypes: Array<DatasetFeatureType>
|
|
187
|
-
): DatasetFeatureCatalog {
|
|
188
|
-
return {
|
|
189
|
-
featureTypes: featureTypes.map((featureType) => ({
|
|
190
|
-
name: featureType.typeName || '',
|
|
191
|
-
definition: featureType.definition || '',
|
|
192
|
-
attributes: Array.isArray(featureType.attributeTable)
|
|
193
|
-
? featureType.attributeTable.map((attr) => {
|
|
194
|
-
const values = attr.values
|
|
195
|
-
?.filter((v) => v.code || v.label)
|
|
196
|
-
.map((v) => ({
|
|
197
|
-
code: v.code,
|
|
198
|
-
label: v.label,
|
|
199
|
-
}))
|
|
200
|
-
return {
|
|
201
|
-
name: attr.name,
|
|
202
|
-
code: attr.code,
|
|
203
|
-
definition: attr.definition,
|
|
204
|
-
type: attr.type,
|
|
205
|
-
...(values?.length > 0 ? { values } : {}),
|
|
206
|
-
}
|
|
207
|
-
})
|
|
208
|
-
: [],
|
|
209
|
-
})),
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
getFeatureCatalog(
|
|
184
|
+
queryLinkedFeatureCatalog(
|
|
213
185
|
record: CatalogRecord,
|
|
214
186
|
visited: Set<string> = new Set() // prevent looping
|
|
215
187
|
): Observable<DatasetFeatureCatalog | null> {
|
|
216
188
|
if (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
record.extras['featureTypes'].length > 0
|
|
189
|
+
'featureTypeDescriptions' in record &&
|
|
190
|
+
record.featureTypeDescriptions?.length > 0
|
|
220
191
|
) {
|
|
221
|
-
return of(
|
|
192
|
+
return of({ featureTypes: record.featureTypeDescriptions })
|
|
222
193
|
}
|
|
223
194
|
|
|
224
195
|
const featureCatalogIdentifier = record.extras[
|
|
@@ -228,7 +199,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
228
199
|
visited.add(featureCatalogIdentifier)
|
|
229
200
|
return this.getRecord(featureCatalogIdentifier).pipe(
|
|
230
201
|
switchMap((record) =>
|
|
231
|
-
record ? this.
|
|
202
|
+
record ? this.queryLinkedFeatureCatalog(record, visited) : of(null)
|
|
232
203
|
)
|
|
233
204
|
)
|
|
234
205
|
}
|
|
@@ -316,6 +316,7 @@ export interface DatasetRecord extends BaseRecord {
|
|
|
316
316
|
spatialExtents: Array<DatasetSpatialExtent>
|
|
317
317
|
temporalExtents: Array<DatasetTemporalExtent>
|
|
318
318
|
spatialRepresentation?: SpatialRepresentationType
|
|
319
|
+
featureTypeDescriptions?: Array<DatasetFeatureType>
|
|
319
320
|
}
|
|
320
321
|
|
|
321
322
|
export interface ServiceEndpoint {
|
|
@@ -347,33 +348,28 @@ export interface ReuseRecord extends BaseRecord {
|
|
|
347
348
|
temporalExtents: Array<DatasetTemporalExtent>
|
|
348
349
|
}
|
|
349
350
|
|
|
351
|
+
export interface DatasetFeatureType {
|
|
352
|
+
name: string
|
|
353
|
+
label?: string
|
|
354
|
+
description?: string
|
|
355
|
+
attributes: DatasetFeatureAttribute[]
|
|
356
|
+
}
|
|
350
357
|
export type DatasetFeatureCatalog = {
|
|
351
|
-
featureTypes: Array<
|
|
352
|
-
name: string
|
|
353
|
-
definition: string
|
|
354
|
-
attributes: DatasetFeatureAttribute[]
|
|
355
|
-
}>
|
|
358
|
+
featureTypes: Array<DatasetFeatureType>
|
|
356
359
|
}
|
|
357
360
|
export interface DatasetFeatureAttributeValue {
|
|
358
|
-
|
|
359
|
-
definition?: string
|
|
361
|
+
value: string // empty string when the value has no code
|
|
360
362
|
label?: string
|
|
363
|
+
description?: string
|
|
361
364
|
}
|
|
362
365
|
export interface DatasetFeatureAttribute {
|
|
363
366
|
name: string
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
+
label?: string
|
|
368
|
+
description?: string
|
|
369
|
+
type?: string
|
|
370
|
+
code?: string
|
|
367
371
|
values?: Array<DatasetFeatureAttributeValue>
|
|
368
372
|
}
|
|
369
|
-
export interface DatasetFeatureType {
|
|
370
|
-
aliases: string
|
|
371
|
-
code: string
|
|
372
|
-
isAbstract: string
|
|
373
|
-
typeName: string
|
|
374
|
-
definition: string
|
|
375
|
-
attributeTable: Array<DatasetFeatureAttribute>
|
|
376
|
-
}
|
|
377
373
|
|
|
378
374
|
export type ReuseType = 'application' | 'map' | 'other'
|
|
379
375
|
|
|
@@ -21,7 +21,10 @@ export type QueryString = {
|
|
|
21
21
|
query_string: string
|
|
22
22
|
}
|
|
23
23
|
export type QueryRange = {
|
|
24
|
-
range: Record<
|
|
24
|
+
range: Record<
|
|
25
|
+
string,
|
|
26
|
+
{ format: string; gte?: string; lte?: string; relation?: string }
|
|
27
|
+
>
|
|
25
28
|
}
|
|
26
29
|
export type QueryGeoShape = {
|
|
27
30
|
geo_shape: {
|
|
@@ -17,7 +17,7 @@ export abstract class RecordsRepositoryInterface {
|
|
|
17
17
|
abstract search(params: SearchParams): Observable<SearchResults>
|
|
18
18
|
abstract getMatchesCount(filters: FieldFilters): Observable<number>
|
|
19
19
|
abstract getRecord(uniqueIdentifier: string): Observable<CatalogRecord | null>
|
|
20
|
-
abstract
|
|
20
|
+
abstract queryLinkedFeatureCatalog(
|
|
21
21
|
record: CatalogRecord
|
|
22
22
|
): Observable<DatasetFeatureCatalog | null>
|
|
23
23
|
abstract aggregate(params: AggregationsParams): Observable<Aggregations>
|
|
@@ -164,69 +164,49 @@ As such, **it is not very interesting at all.**`,
|
|
|
164
164
|
extras: {
|
|
165
165
|
isPublishedToAll: true,
|
|
166
166
|
edit: true,
|
|
167
|
-
featureTypes: [
|
|
168
|
-
{
|
|
169
|
-
attributeTable: [
|
|
170
|
-
{
|
|
171
|
-
code: 'OBJECTID',
|
|
172
|
-
name: 'OBJECTID',
|
|
173
|
-
link: '',
|
|
174
|
-
definition: 'Object identifier',
|
|
175
|
-
type: 'OID',
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
code: 'NOM',
|
|
179
|
-
name: 'Nom',
|
|
180
|
-
link: '',
|
|
181
|
-
definition: 'Nom de la rue',
|
|
182
|
-
type: 'String (48)',
|
|
183
|
-
values: [
|
|
184
|
-
{
|
|
185
|
-
code: 'Pomme',
|
|
186
|
-
label: 'Les Pommiers',
|
|
187
|
-
description: 'Sous les pommiers',
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
code: 'Cotton',
|
|
191
|
-
label: 'Rue Cotton',
|
|
192
|
-
description: 'Rue Cotton',
|
|
193
|
-
},
|
|
194
|
-
{ code: "Passage de l'échiquier" },
|
|
195
|
-
{ description: 'Rue du Charlieu' },
|
|
196
|
-
],
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
code: 'RUE',
|
|
200
|
-
name: 'Rue',
|
|
201
|
-
link: '',
|
|
202
|
-
definition: '',
|
|
203
|
-
type: 'String (50)',
|
|
204
|
-
},
|
|
205
|
-
],
|
|
206
|
-
code: '',
|
|
207
|
-
aliases: '',
|
|
208
|
-
typeName: "Catalogue d'attributs N°1",
|
|
209
|
-
definition: 'Définition du catalogue d attributs N°1',
|
|
210
|
-
isAbstract: 'false',
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
attributeTable: [
|
|
214
|
-
{
|
|
215
|
-
code: 'UniqueObject',
|
|
216
|
-
name: 'unique object ',
|
|
217
|
-
link: '',
|
|
218
|
-
definition: 'this is the only object of this catalog',
|
|
219
|
-
type: 'String (50)',
|
|
220
|
-
},
|
|
221
|
-
],
|
|
222
|
-
code: '',
|
|
223
|
-
aliases: '',
|
|
224
|
-
typeName: "Catalogue d'attributs N°2",
|
|
225
|
-
definition: 'Définition du catalogue d attributs N°2',
|
|
226
|
-
isAbstract: 'false',
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
167
|
},
|
|
168
|
+
featureTypeDescriptions: [
|
|
169
|
+
{
|
|
170
|
+
name: "Catalogue d'attributs N°1",
|
|
171
|
+
description: 'Définition du catalogue d attributs N°1',
|
|
172
|
+
attributes: [
|
|
173
|
+
{
|
|
174
|
+
name: 'OBJECTID',
|
|
175
|
+
description: 'Object identifier',
|
|
176
|
+
code: 'OBJECTID',
|
|
177
|
+
type: 'OID',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'Nom',
|
|
181
|
+
description: 'Nom de la rue',
|
|
182
|
+
code: 'NOM',
|
|
183
|
+
type: 'String (48)',
|
|
184
|
+
values: [
|
|
185
|
+
{ value: 'Pomme', label: 'Les Pommiers' },
|
|
186
|
+
{ value: 'Cotton', label: 'Rue Cotton' },
|
|
187
|
+
{ value: "Passage de l'échiquier" },
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: 'Rue',
|
|
192
|
+
code: 'RUE',
|
|
193
|
+
type: 'String (50)',
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "Catalogue d'attributs N°2",
|
|
199
|
+
description: 'Définition du catalogue d attributs N°2',
|
|
200
|
+
attributes: [
|
|
201
|
+
{
|
|
202
|
+
name: 'unique object ',
|
|
203
|
+
description: 'this is the only object of this catalog',
|
|
204
|
+
code: 'UniqueObject',
|
|
205
|
+
type: 'String (50)',
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
],
|
|
230
210
|
},
|
|
231
211
|
{
|
|
232
212
|
uniqueIdentifier: '7d002c4c-92ef-4b9f-a568-d732f740b99e',
|
|
@@ -209,7 +209,8 @@ export class DataService {
|
|
|
209
209
|
ogcApiLink: DatasetServiceDistribution
|
|
210
210
|
): Promise<DatasetOnlineResource[]> {
|
|
211
211
|
const collectionInfo = await this.getDownloadUrlsFromOgcApi(
|
|
212
|
-
ogcApiLink.url.href
|
|
212
|
+
ogcApiLink.url.href,
|
|
213
|
+
ogcApiLink.name
|
|
213
214
|
)
|
|
214
215
|
|
|
215
216
|
return Object.keys(collectionInfo.bulkDownloadLinks).map((mimeType) => {
|
|
@@ -229,31 +230,41 @@ export class DataService {
|
|
|
229
230
|
})
|
|
230
231
|
}
|
|
231
232
|
|
|
232
|
-
async
|
|
233
|
+
private async getTargetCollectionId(
|
|
234
|
+
endpoint: OgcApiEndpoint,
|
|
235
|
+
collectionId?: string
|
|
236
|
+
): Promise<string | undefined> {
|
|
237
|
+
const collections = await endpoint.featureCollections
|
|
238
|
+
return collections.length === 1 && !collectionId
|
|
239
|
+
? collections[0]
|
|
240
|
+
: collectionId
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async getDownloadUrlsFromOgcApi(
|
|
244
|
+
url: string,
|
|
245
|
+
collectionId?: string
|
|
246
|
+
): Promise<OgcApiCollectionInfo> {
|
|
233
247
|
const endpoint = new OgcApiEndpoint(url)
|
|
234
|
-
|
|
235
|
-
.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
.
|
|
239
|
-
|
|
240
|
-
})
|
|
248
|
+
try {
|
|
249
|
+
const id = await this.getTargetCollectionId(endpoint, collectionId)
|
|
250
|
+
return await endpoint.getCollectionInfo(id)
|
|
251
|
+
} catch {
|
|
252
|
+
throw new Error('ogc.unreachable.unknown')
|
|
253
|
+
}
|
|
241
254
|
}
|
|
242
255
|
|
|
243
256
|
async getItemsFromOgcApi(
|
|
244
257
|
url: string,
|
|
258
|
+
collectionId?: string,
|
|
245
259
|
limit?: number
|
|
246
260
|
): Promise<OgcApiRecord[]> {
|
|
247
261
|
const endpoint = new OgcApiEndpoint(url)
|
|
248
|
-
|
|
249
|
-
.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
.catch(() => {
|
|
255
|
-
throw new Error(`ogc.unreachable.unknown`)
|
|
256
|
-
})
|
|
262
|
+
try {
|
|
263
|
+
const id = await this.getTargetCollectionId(endpoint, collectionId)
|
|
264
|
+
return await endpoint.getCollectionItems(id, limit)
|
|
265
|
+
} catch {
|
|
266
|
+
throw new Error('ogc.unreachable.unknown')
|
|
267
|
+
}
|
|
257
268
|
}
|
|
258
269
|
|
|
259
270
|
async getItemsFromStacApi(
|
|
@@ -381,7 +392,9 @@ export class DataService {
|
|
|
381
392
|
link.type === 'service' &&
|
|
382
393
|
link.accessServiceProtocol === 'ogcFeatures'
|
|
383
394
|
) {
|
|
384
|
-
return from(
|
|
395
|
+
return from(
|
|
396
|
+
this.getDownloadUrlsFromOgcApi(link.url.href, link.name)
|
|
397
|
+
).pipe(
|
|
385
398
|
switchMap((collectionInfo) => {
|
|
386
399
|
const isMimeTypeJson = (mimeType: string): boolean => {
|
|
387
400
|
return mimeType.toLowerCase().indexOf('json') > -1
|
|
@@ -6,7 +6,6 @@ export * from './lib/feature-map.module'
|
|
|
6
6
|
export * from './lib/map-state-container/map-state-container.component'
|
|
7
7
|
export * from './lib/constant'
|
|
8
8
|
export * from './lib/utils'
|
|
9
|
-
export * from './lib/style'
|
|
10
9
|
export * from './lib/layers-panel/layers-panel.component'
|
|
11
10
|
export * from './lib/add-layer-from-catalog/add-layer-from-catalog.component'
|
|
12
11
|
export * from './lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component'
|
package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts
CHANGED
|
@@ -33,7 +33,6 @@ import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain
|
|
|
33
33
|
import { NOT_KNOWN_CONSTRAINT } from '../../../../../../libs/feature/editor/src'
|
|
34
34
|
import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
|
|
35
35
|
import { ButtonComponent, TextInputComponent } from '../../../../../../libs/ui/inputs/src'
|
|
36
|
-
import { getOptionalEditorConfig } from '../../../../../../libs/util/app-config/src'
|
|
37
36
|
import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
|
|
38
37
|
import { iconoirAppWindow, iconoirPlusCircle } from '@ng-icons/iconoir'
|
|
39
38
|
import { matCloseOutline } from '@ng-icons/material-icons/outline'
|
|
@@ -48,6 +47,10 @@ marker('notify.reuse.form.error.title')
|
|
|
48
47
|
marker('notify.reuse.form.error.body')
|
|
49
48
|
|
|
50
49
|
export const REUSE_FORM_URL = new InjectionToken<string>('reuseFormUrl')
|
|
50
|
+
// language assigned to the created reuse record; falls back to the current UI language
|
|
51
|
+
export const NEW_RECORD_DEFAULT_LANGUAGE = new InjectionToken<string>(
|
|
52
|
+
'newRecordDefaultLanguage'
|
|
53
|
+
)
|
|
51
54
|
|
|
52
55
|
@Component({
|
|
53
56
|
selector: 'gn-ui-notify-reuse-form',
|
|
@@ -86,6 +89,9 @@ export class NotifyReuseFormComponent implements OnDestroy {
|
|
|
86
89
|
private readonly notificationsService = inject(NotificationsService)
|
|
87
90
|
private readonly platformServiceInterface = inject(PlatformServiceInterface)
|
|
88
91
|
reuseFormUrl = inject(REUSE_FORM_URL, { optional: true })
|
|
92
|
+
private newRecordDefaultLanguage = inject(NEW_RECORD_DEFAULT_LANGUAGE, {
|
|
93
|
+
optional: true,
|
|
94
|
+
})
|
|
89
95
|
private locationStrategy = inject(LocationStrategy)
|
|
90
96
|
|
|
91
97
|
me = toSignal(this.platformServiceInterface.getMe())
|
|
@@ -158,8 +164,7 @@ export class NotifyReuseFormComponent implements OnDestroy {
|
|
|
158
164
|
submit() {
|
|
159
165
|
if (!this.isFormValid()) return
|
|
160
166
|
const defaultLang =
|
|
161
|
-
|
|
162
|
-
this.translate.currentLang
|
|
167
|
+
this.newRecordDefaultLanguage ?? this.translate.currentLang
|
|
163
168
|
const onlineResource: OnlineLinkResource = {
|
|
164
169
|
type: 'link',
|
|
165
170
|
url: new URL(this.url()),
|
|
@@ -40,7 +40,9 @@ export class MdViewEffects {
|
|
|
40
40
|
this.actions$.pipe(
|
|
41
41
|
ofType(MdViewActions.loadFullMetadataSuccess),
|
|
42
42
|
filter(({ full }) => full !== undefined),
|
|
43
|
-
switchMap(({ full }) =>
|
|
43
|
+
switchMap(({ full }) =>
|
|
44
|
+
this.recordsRepository.queryLinkedFeatureCatalog(full)
|
|
45
|
+
),
|
|
44
46
|
map((featureCatalog) =>
|
|
45
47
|
MdViewActions.loadFeatureCatalogSuccess({
|
|
46
48
|
datasetCatalog: featureCatalog,
|
|
@@ -162,7 +162,7 @@ export class MdViewFacade {
|
|
|
162
162
|
link.accessServiceProtocol === 'ogcFeatures'
|
|
163
163
|
) {
|
|
164
164
|
return from(
|
|
165
|
-
this.dataService.getItemsFromOgcApi(link.url.href, 1)
|
|
165
|
+
this.dataService.getItemsFromOgcApi(link.url.href, link.name, 1)
|
|
166
166
|
).pipe(
|
|
167
167
|
map((collectionRecords: OgcApiRecord[]) => {
|
|
168
168
|
return collectionRecords &&
|