geonetwork-ui 2.11.0-dev.a83f0f51a → 2.11.0-dev.bc1a6d4e2
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 +1244 -677
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +219 -78
- package/index.d.ts.map +1 -1
- package/package.json +12 -11
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +43 -13
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +48 -7
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +40 -16
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +12 -0
- package/src/libs/common/domain/src/lib/model/search/filter.model.ts +13 -1
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -2
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
- package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +22 -22
- package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
- package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
- package/src/libs/feature/record/src/lib/state/mdview.actions.ts +4 -8
- package/src/libs/feature/record/src/lib/state/mdview.effects.ts +7 -16
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -3
- package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +4 -9
- package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +2 -7
- package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +11 -4
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +58 -19
- package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +17 -8
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +3 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +27 -2
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
- package/src/libs/ui/inputs/src/index.ts +2 -0
- package/src/libs/ui/inputs/src/lib/date-adapter.providers.ts +30 -0
- package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +3 -24
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.css +37 -3
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html +135 -16
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts +166 -29
- package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +3 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +111 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +215 -0
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +6 -5
- package/src/libs/util/app-config/src/lib/app-config.ts +3 -0
- package/src/libs/util/app-config/src/lib/model.ts +1 -0
- package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +19 -22
- package/src/libs/util/data-fetcher/src/lib/engine/duckdb.ts +185 -0
- package/src/libs/util/data-fetcher/src/lib/engine/results.ts +63 -0
- package/src/libs/util/data-fetcher/src/lib/{sql-utils.ts → engine/sql-utils.ts} +28 -13
- package/src/libs/util/data-fetcher/src/lib/model.ts +2 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +53 -38
- package/src/libs/util/data-fetcher/src/lib/readers/base.ts +11 -0
- package/src/libs/util/data-fetcher/src/lib/readers/csv.ts +9 -47
- package/src/libs/util/data-fetcher/src/lib/readers/excel.ts +27 -27
- package/src/libs/util/data-fetcher/src/lib/readers/geojson.ts +5 -24
- package/src/libs/util/data-fetcher/src/lib/readers/gml.ts +5 -49
- package/src/libs/util/data-fetcher/src/lib/readers/json.ts +5 -23
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +184 -128
- package/src/libs/util/data-fetcher/src/lib/utils.ts +0 -143
- package/src/libs/util/shared/src/index.ts +1 -0
- package/src/libs/util/shared/src/lib/autofocus.directive.ts +26 -0
- package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
- package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
- package/src/libs/util/shared/src/lib/utils/geojson.ts +8 -0
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/tailwind.base.css +5 -0
- package/translations/de.json +15 -2
- package/translations/en.json +14 -1
- package/translations/es.json +13 -0
- package/translations/fr.json +15 -2
- package/translations/it.json +13 -0
- package/translations/nl.json +13 -0
- package/translations/pt.json +13 -0
- package/translations/sk.json +13 -0
- package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
|
@@ -34,7 +34,12 @@ import {
|
|
|
34
34
|
LanguageCode,
|
|
35
35
|
} from '../../../../../../../libs/common/domain/src/lib/model/record'
|
|
36
36
|
import { TranslateService } from '@ngx-translate/core'
|
|
37
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
bboxToPolygon,
|
|
39
|
+
BoundingBox,
|
|
40
|
+
getGeometryBoundingBox,
|
|
41
|
+
isBoundingBox,
|
|
42
|
+
} from '../../../../../../../libs/util/shared/src'
|
|
38
43
|
import { getLength as getGeodesicLength } from 'ol/sphere.js'
|
|
39
44
|
import { LineString } from 'ol/geom.js'
|
|
40
45
|
|
|
@@ -142,6 +147,7 @@ export class ElasticsearchService {
|
|
|
142
147
|
values: uuids,
|
|
143
148
|
},
|
|
144
149
|
},
|
|
150
|
+
size: uuids.length,
|
|
145
151
|
}
|
|
146
152
|
}
|
|
147
153
|
|
|
@@ -263,8 +269,18 @@ export class ElasticsearchService {
|
|
|
263
269
|
return this.metadataLang === 'current'
|
|
264
270
|
}
|
|
265
271
|
|
|
266
|
-
private
|
|
272
|
+
private findSpatialFilterExtent(
|
|
267
273
|
filters: FieldFilters | FiltersAggregationParams | string
|
|
274
|
+
): BoundingBox | undefined {
|
|
275
|
+
if (typeof filters === 'string') {
|
|
276
|
+
return undefined
|
|
277
|
+
}
|
|
278
|
+
return Object.values(filters).find(isBoundingBox)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private filtersToQuery(
|
|
282
|
+
filters: FieldFilters | FiltersAggregationParams | string,
|
|
283
|
+
spatialFilterExtent = this.findSpatialFilterExtent(filters)
|
|
268
284
|
): FilterQuery {
|
|
269
285
|
const addQuote = (key: string) => (/^\/.+\/$/.test(key) ? key : `"${key}"`)
|
|
270
286
|
const makeQuery = (filter: FieldFilter): string => {
|
|
@@ -285,7 +301,9 @@ export class ElasticsearchService {
|
|
|
285
301
|
? filters
|
|
286
302
|
: Object.keys(filters)
|
|
287
303
|
.filter((fieldname) => fieldname !== 'gn-ui-crossFieldFilter')
|
|
304
|
+
.filter((fieldname) => !isBoundingBox(filters[fieldname]))
|
|
288
305
|
.filter((fieldname) => !isDateRange(filters[fieldname]))
|
|
306
|
+
.filter((fieldname) => !Array.isArray(filters[fieldname]))
|
|
289
307
|
.filter(
|
|
290
308
|
(fieldname) =>
|
|
291
309
|
filters[fieldname] &&
|
|
@@ -329,6 +347,21 @@ export class ElasticsearchService {
|
|
|
329
347
|
},
|
|
330
348
|
},
|
|
331
349
|
},
|
|
350
|
+
spatialFilterExtent && {
|
|
351
|
+
geo_shape: {
|
|
352
|
+
geom: {
|
|
353
|
+
shape: {
|
|
354
|
+
type: 'envelope',
|
|
355
|
+
// spatialFilterExtent is [minX, minY, maxX, maxY]; envelope coordinates are [top-left, bottom-right]
|
|
356
|
+
coordinates: [
|
|
357
|
+
[spatialFilterExtent[0], spatialFilterExtent[3]],
|
|
358
|
+
[spatialFilterExtent[2], spatialFilterExtent[1]],
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
relation: 'intersects',
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
},
|
|
332
365
|
].filter(Boolean)
|
|
333
366
|
return queryParts.length > 0 ? (queryParts as FilterQuery) : undefined
|
|
334
367
|
}
|
|
@@ -367,7 +400,12 @@ export class ElasticsearchService {
|
|
|
367
400
|
},
|
|
368
401
|
})
|
|
369
402
|
}
|
|
370
|
-
|
|
403
|
+
// a spatial extent filter takes precedence over the preference geometry for boosting
|
|
404
|
+
const spatialFilterExtent = this.findSpatialFilterExtent(fieldSearchFilters)
|
|
405
|
+
const queryFilters = this.filtersToQuery(
|
|
406
|
+
fieldSearchFilters,
|
|
407
|
+
spatialFilterExtent
|
|
408
|
+
)
|
|
371
409
|
if (queryFilters) {
|
|
372
410
|
filter.push(...queryFilters)
|
|
373
411
|
}
|
|
@@ -378,7 +416,10 @@ export class ElasticsearchService {
|
|
|
378
416
|
},
|
|
379
417
|
})
|
|
380
418
|
}
|
|
381
|
-
|
|
419
|
+
const boostGeometry = spatialFilterExtent
|
|
420
|
+
? bboxToPolygon(spatialFilterExtent)
|
|
421
|
+
: geometry
|
|
422
|
+
if (boostGeometry) {
|
|
382
423
|
// boosts applied using the filter geometry:
|
|
383
424
|
// * records completely within the geometry receive a boost of 5
|
|
384
425
|
// * records intersecting the geometry receive a boost of 2
|
|
@@ -388,7 +429,7 @@ export class ElasticsearchService {
|
|
|
388
429
|
{
|
|
389
430
|
geo_shape: {
|
|
390
431
|
geom: {
|
|
391
|
-
shape:
|
|
432
|
+
shape: boostGeometry,
|
|
392
433
|
relation: 'within',
|
|
393
434
|
},
|
|
394
435
|
boost: 5.0,
|
|
@@ -397,7 +438,7 @@ export class ElasticsearchService {
|
|
|
397
438
|
{
|
|
398
439
|
geo_shape: {
|
|
399
440
|
geom: {
|
|
400
|
-
shape:
|
|
441
|
+
shape: boostGeometry,
|
|
401
442
|
relation: 'intersects',
|
|
402
443
|
},
|
|
403
444
|
boost: 2.0,
|
|
@@ -408,7 +449,7 @@ export class ElasticsearchService {
|
|
|
408
449
|
// this will boost the results variably depending on their distance from the given geometry
|
|
409
450
|
// note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
|
|
410
451
|
// combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
|
|
411
|
-
const bbox = getGeometryBoundingBox(
|
|
452
|
+
const bbox = getGeometryBoundingBox(boostGeometry)
|
|
412
453
|
const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2]
|
|
413
454
|
const northToCenter = new LineString([
|
|
414
455
|
[center[0], bbox[3]],
|
|
@@ -14,10 +14,13 @@ import {
|
|
|
14
14
|
} from '../../../../../../libs/api/metadata-converter/src'
|
|
15
15
|
import { PublicationVersionError } from '../../../../../../libs/common/domain/src/lib/model/error'
|
|
16
16
|
import {
|
|
17
|
+
AssociatedRecord,
|
|
17
18
|
CatalogRecord,
|
|
18
19
|
DatasetFeatureCatalog,
|
|
19
20
|
DatasetFeatureType,
|
|
20
21
|
LanguageCode,
|
|
22
|
+
LinkedRecord,
|
|
23
|
+
RecordRelation,
|
|
21
24
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
22
25
|
import {
|
|
23
26
|
Aggregations,
|
|
@@ -145,7 +148,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
145
148
|
return this.gn4SearchApi
|
|
146
149
|
.search(
|
|
147
150
|
'bucket',
|
|
148
|
-
['fcats', 'hassources'],
|
|
151
|
+
['fcats', 'hassources', 'siblings', 'associated'],
|
|
149
152
|
JSON.stringify(
|
|
150
153
|
this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])
|
|
151
154
|
)
|
|
@@ -249,22 +252,43 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
249
252
|
)
|
|
250
253
|
}
|
|
251
254
|
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
255
|
+
getLinkedRecords(record: CatalogRecord): Observable<LinkedRecord[]> {
|
|
256
|
+
const siblings = (record.extras?.['siblings'] ?? []) as AssociatedRecord[]
|
|
257
|
+
const relations: Array<[RecordRelation, string[]]> = [
|
|
258
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? []) as string[]],
|
|
259
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? []) as string[]],
|
|
260
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
261
|
+
[
|
|
262
|
+
'associated',
|
|
263
|
+
(record.extras?.['associatedIdentifiers'] ?? []) as string[],
|
|
264
|
+
],
|
|
265
|
+
]
|
|
266
|
+
const requested = relations.filter(
|
|
267
|
+
([, identifiers]) => identifiers.length > 0
|
|
268
|
+
)
|
|
269
|
+
if (requested.length === 0) {
|
|
270
|
+
return of([])
|
|
266
271
|
}
|
|
267
|
-
return
|
|
272
|
+
return forkJoin(
|
|
273
|
+
requested.map(([relation, identifiers]) =>
|
|
274
|
+
this.getMultipleRecords(identifiers).pipe(
|
|
275
|
+
map((records) =>
|
|
276
|
+
(records ?? []).map((record) => ({
|
|
277
|
+
record,
|
|
278
|
+
relation,
|
|
279
|
+
associationType:
|
|
280
|
+
relation === 'sibling'
|
|
281
|
+
? siblings.find(
|
|
282
|
+
({ uniqueIdentifier }) =>
|
|
283
|
+
uniqueIdentifier === record.uniqueIdentifier
|
|
284
|
+
)?.associationType
|
|
285
|
+
: undefined,
|
|
286
|
+
}))
|
|
287
|
+
),
|
|
288
|
+
catchError(() => of([]))
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
).pipe(map((groups) => groups.flat()))
|
|
268
292
|
}
|
|
269
293
|
|
|
270
294
|
aggregate(params: AggregationsParams): Observable<Aggregations> {
|
|
@@ -294,6 +294,18 @@ export interface AssociatedRecord {
|
|
|
294
294
|
associationType: AssociationType
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Nature of the link between a record and another one; the association type is
|
|
299
|
+
* only known for 'sibling' relations, as it is held by the declaring record.
|
|
300
|
+
*/
|
|
301
|
+
export type RecordRelation = 'source' | 'sourceOf' | 'sibling' | 'associated'
|
|
302
|
+
|
|
303
|
+
export interface LinkedRecord {
|
|
304
|
+
record: CatalogRecord
|
|
305
|
+
relation: RecordRelation
|
|
306
|
+
associationType?: AssociationType
|
|
307
|
+
}
|
|
308
|
+
|
|
297
309
|
export interface DatasetRecord extends BaseRecord {
|
|
298
310
|
kind: 'dataset'
|
|
299
311
|
status: RecordStatus
|
|
@@ -6,11 +6,15 @@ export type FieldFilterByRange = {
|
|
|
6
6
|
start?: Date
|
|
7
7
|
end?: Date
|
|
8
8
|
}
|
|
9
|
+
// matches util/shared's BoundingBox; duplicated here to avoid a circular
|
|
10
|
+
// dependency (util/shared already depends on common/domain)
|
|
11
|
+
export type FieldFilterByBoundingBox = [number, number, number, number]
|
|
9
12
|
|
|
10
13
|
export type FieldFilter =
|
|
11
14
|
| FieldFilterByExpression
|
|
12
15
|
| FieldFilterByValues
|
|
13
16
|
| FieldFilterByRange
|
|
17
|
+
| FieldFilterByBoundingBox
|
|
14
18
|
export type FieldFilters = Record<FieldName, FieldFilter>
|
|
15
19
|
|
|
16
20
|
export type QueryString = {
|
|
@@ -19,4 +23,12 @@ export type QueryString = {
|
|
|
19
23
|
export type QueryRange = {
|
|
20
24
|
range: Record<string, { format: string; gte: string; lte: string }>
|
|
21
25
|
}
|
|
22
|
-
export type
|
|
26
|
+
export type QueryGeoShape = {
|
|
27
|
+
geo_shape: {
|
|
28
|
+
geom: {
|
|
29
|
+
shape: { type: string; coordinates: number[][] }
|
|
30
|
+
relation: string
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export type FilterQuery = Array<QueryString | QueryRange | QueryGeoShape>
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
CatalogRecord,
|
|
11
11
|
DatasetFeatureCatalog,
|
|
12
12
|
LanguageCode,
|
|
13
|
+
LinkedRecord,
|
|
13
14
|
} from '../model/record'
|
|
14
15
|
|
|
15
16
|
export abstract class RecordsRepositoryInterface {
|
|
@@ -23,8 +24,7 @@ export abstract class RecordsRepositoryInterface {
|
|
|
23
24
|
abstract getSimilarRecords(
|
|
24
25
|
similarTo: CatalogRecord
|
|
25
26
|
): Observable<CatalogRecord[]>
|
|
26
|
-
abstract
|
|
27
|
-
abstract getSourceOf(record: CatalogRecord): Observable<CatalogRecord[]>
|
|
27
|
+
abstract getLinkedRecords(record: CatalogRecord): Observable<LinkedRecord[]>
|
|
28
28
|
abstract fuzzySearch(query: string): Observable<SearchResults>
|
|
29
29
|
abstract canDuplicate(record: CatalogRecord): boolean
|
|
30
30
|
abstract canDelete(record: CatalogRecord): Observable<boolean>
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
ChangeDetectionStrategy,
|
|
3
3
|
ChangeDetectorRef,
|
|
4
4
|
Component,
|
|
5
|
+
inject,
|
|
5
6
|
Input,
|
|
6
7
|
Output,
|
|
7
|
-
inject,
|
|
8
8
|
} from '@angular/core'
|
|
9
9
|
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
10
10
|
import {
|
|
@@ -183,7 +183,7 @@ export class ChartViewComponent {
|
|
|
183
183
|
yChoices$ = this.properties$.pipe(
|
|
184
184
|
map((properties) =>
|
|
185
185
|
properties
|
|
186
|
-
.filter((prop) => prop.type === 'number'
|
|
186
|
+
.filter((prop) => prop.type === 'number')
|
|
187
187
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))
|
|
188
188
|
),
|
|
189
189
|
tap((choices) => {
|
|
@@ -222,6 +222,7 @@ export class ChartViewComponent {
|
|
|
222
222
|
switchMap(([dataset, xProp, yProp, aggregation]) => {
|
|
223
223
|
const fieldAgg: FieldAggregation =
|
|
224
224
|
aggregation === 'count' ? ['count'] : [aggregation, yProp]
|
|
225
|
+
this.loading = true
|
|
225
226
|
return dataset
|
|
226
227
|
.groupBy(['distinct', xProp])
|
|
227
228
|
.aggregate(fieldAgg)
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
ChangeDetectionStrategy,
|
|
3
3
|
ChangeDetectorRef,
|
|
4
4
|
Component,
|
|
5
|
+
inject,
|
|
5
6
|
Input,
|
|
6
7
|
OnDestroy,
|
|
7
8
|
OnInit,
|
|
8
9
|
ViewChild,
|
|
9
|
-
inject,
|
|
10
10
|
} from '@angular/core'
|
|
11
11
|
import {
|
|
12
12
|
DataTableComponent,
|
|
@@ -75,6 +75,7 @@ export class GeoTableViewComponent implements OnInit, OnDestroy {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
private async initMapContext(): Promise<MapContext> {
|
|
78
|
+
this.dataset.load()
|
|
78
79
|
this.dataset.selectAll()
|
|
79
80
|
return {
|
|
80
81
|
layers: [
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { inject, Injectable } from '@angular/core'
|
|
2
2
|
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
3
3
|
import {
|
|
4
|
+
GetCollectionItemsOptions,
|
|
4
5
|
OgcApiCollectionInfo,
|
|
5
6
|
OgcApiEndpoint,
|
|
6
7
|
OgcApiRecord,
|
|
7
|
-
WfsEndpoint,
|
|
8
|
-
WfsVersion,
|
|
9
|
-
TmsEndpoint,
|
|
10
8
|
StacEndpoint,
|
|
11
|
-
GetCollectionItemsOptions,
|
|
12
9
|
StacItemsDocument,
|
|
10
|
+
TmsEndpoint,
|
|
11
|
+
WfsEndpoint,
|
|
12
|
+
WfsVersion,
|
|
13
13
|
} from '@camptocamp/ogc-client'
|
|
14
14
|
import {
|
|
15
15
|
BaseReader,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from '../../../../../../libs/util/shared/src'
|
|
27
27
|
import type { FeatureCollection } from 'geojson'
|
|
28
28
|
import { from, Observable, throwError } from 'rxjs'
|
|
29
|
-
import { catchError, map, switchMap
|
|
29
|
+
import { catchError, map, switchMap } from 'rxjs/operators'
|
|
30
30
|
import {
|
|
31
31
|
DatasetOnlineResource,
|
|
32
32
|
DatasetServiceDistribution,
|
|
@@ -347,14 +347,11 @@ export class DataService {
|
|
|
347
347
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
348
348
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString())
|
|
349
349
|
return from(
|
|
350
|
-
openDataset(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
},
|
|
356
|
-
cacheActive
|
|
357
|
-
)
|
|
350
|
+
openDataset(wfsUrlEndpoint, {
|
|
351
|
+
typeHint: 'wfs',
|
|
352
|
+
wfsFeatureType: link.name,
|
|
353
|
+
enableCache: cacheActive,
|
|
354
|
+
})
|
|
358
355
|
)
|
|
359
356
|
} else if (link.type === 'download') {
|
|
360
357
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString())
|
|
@@ -364,7 +361,10 @@ export class DataService {
|
|
|
364
361
|
? (format as SupportedType)
|
|
365
362
|
: undefined
|
|
366
363
|
return from(
|
|
367
|
-
openDataset(linkProxifiedUrl,
|
|
364
|
+
openDataset(linkProxifiedUrl, {
|
|
365
|
+
typeHint: supportedType,
|
|
366
|
+
enableCache: cacheActive,
|
|
367
|
+
})
|
|
368
368
|
).pipe()
|
|
369
369
|
} else if (
|
|
370
370
|
link.type === 'service' &&
|
|
@@ -374,7 +374,9 @@ export class DataService {
|
|
|
374
374
|
link.url.toString(),
|
|
375
375
|
'geojson'
|
|
376
376
|
)
|
|
377
|
-
return from(
|
|
377
|
+
return from(
|
|
378
|
+
openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })
|
|
379
|
+
).pipe()
|
|
378
380
|
} else if (
|
|
379
381
|
link.type === 'service' &&
|
|
380
382
|
link.accessServiceProtocol === 'ogcFeatures'
|
|
@@ -395,12 +397,10 @@ export class DataService {
|
|
|
395
397
|
}
|
|
396
398
|
const urlWithoutLimit = new URL(geojsonUrl)
|
|
397
399
|
urlWithoutLimit.searchParams.delete('limit')
|
|
398
|
-
return openDataset(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
cacheActive
|
|
403
|
-
)
|
|
400
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
401
|
+
typeHint: 'geojson',
|
|
402
|
+
enableCache: cacheActive,
|
|
403
|
+
})
|
|
404
404
|
})
|
|
405
405
|
)
|
|
406
406
|
}
|
|
@@ -18,10 +18,12 @@ export class NotificationsService {
|
|
|
18
18
|
error && console.error(error)
|
|
19
19
|
const id = Math.floor(Math.random() * 1000000)
|
|
20
20
|
this.notifications$.next([...this.notifications$.value, { ...content, id }])
|
|
21
|
-
if (typeof timeoutMs
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
if (typeof timeoutMs !== 'undefined') {
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
this.removeNotificationById(id)
|
|
24
|
+
}, timeoutMs)
|
|
25
|
+
}
|
|
26
|
+
return id
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
removeNotificationById(id: number) {
|
|
@@ -3,6 +3,7 @@ import { createAction, props } from '@ngrx/store'
|
|
|
3
3
|
import {
|
|
4
4
|
CatalogRecord,
|
|
5
5
|
DatasetFeatureCatalog,
|
|
6
|
+
LinkedRecord,
|
|
6
7
|
UserFeedback,
|
|
7
8
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
8
9
|
|
|
@@ -54,14 +55,9 @@ export const setRelated = createAction(
|
|
|
54
55
|
props<{ related: CatalogRecord[] }>()
|
|
55
56
|
)
|
|
56
57
|
|
|
57
|
-
export const
|
|
58
|
-
'[Metadata view] Set
|
|
59
|
-
props<{
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
export const setSourceOf = createAction(
|
|
63
|
-
'[Metadata view] Set has sources',
|
|
64
|
-
props<{ sourceOf: CatalogRecord[] }>()
|
|
58
|
+
export const setLinkedRecords = createAction(
|
|
59
|
+
'[Metadata view] Set associated records',
|
|
60
|
+
props<{ linkedRecords: LinkedRecord[] }>()
|
|
65
61
|
)
|
|
66
62
|
|
|
67
63
|
/*
|
|
@@ -70,25 +70,16 @@ export class MdViewEffects {
|
|
|
70
70
|
)
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
loadLinkedRecords$ = createEffect(() =>
|
|
74
74
|
this.actions$.pipe(
|
|
75
75
|
ofType(MdViewActions.loadFullMetadataSuccess),
|
|
76
|
-
switchMap(({ full }) => this.recordsRepository.
|
|
77
|
-
map((
|
|
78
|
-
return MdViewActions.
|
|
76
|
+
switchMap(({ full }) => this.recordsRepository.getLinkedRecords(full)),
|
|
77
|
+
map((linkedRecords) => {
|
|
78
|
+
return MdViewActions.setLinkedRecords({ linkedRecords })
|
|
79
79
|
}),
|
|
80
|
-
catchError(() =>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
loadSourceOf$ = createEffect(() =>
|
|
85
|
-
this.actions$.pipe(
|
|
86
|
-
ofType(MdViewActions.loadFullMetadataSuccess),
|
|
87
|
-
switchMap(({ full }) => this.recordsRepository.getSourceOf(full)),
|
|
88
|
-
map((sourceOf) => {
|
|
89
|
-
return MdViewActions.setSourceOf({ sourceOf })
|
|
90
|
-
}),
|
|
91
|
-
catchError(() => of(MdViewActions.setSourceOf({ sourceOf: null })))
|
|
80
|
+
catchError(() =>
|
|
81
|
+
of(MdViewActions.setLinkedRecords({ linkedRecords: null }))
|
|
82
|
+
)
|
|
92
83
|
)
|
|
93
84
|
)
|
|
94
85
|
|
|
@@ -74,9 +74,7 @@ export class MdViewFacade {
|
|
|
74
74
|
|
|
75
75
|
related$ = this.store.pipe(select(MdViewSelectors.getRelated))
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
sourceOf$ = this.store.pipe(select(MdViewSelectors.getSourceOf))
|
|
77
|
+
linkedRecords$ = this.store.pipe(select(MdViewSelectors.getLinkedRecords))
|
|
80
78
|
|
|
81
79
|
chartConfig$ = this.store.pipe(select(MdViewSelectors.getChartConfig))
|
|
82
80
|
|
|
@@ -4,6 +4,7 @@ import { DatavizChartConfigModel } from '../../../../../../libs/common/domain/sr
|
|
|
4
4
|
import {
|
|
5
5
|
CatalogRecord,
|
|
6
6
|
DatasetFeatureCatalog,
|
|
7
|
+
LinkedRecord,
|
|
7
8
|
UserFeedback,
|
|
8
9
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
9
10
|
|
|
@@ -14,8 +15,7 @@ export interface MetadataViewState {
|
|
|
14
15
|
error: { notFound?: boolean; otherError?: string } | null
|
|
15
16
|
metadata?: Partial<CatalogRecord>
|
|
16
17
|
related?: CatalogRecord[]
|
|
17
|
-
|
|
18
|
-
sourceOf?: CatalogRecord[]
|
|
18
|
+
linkedRecords?: LinkedRecord[]
|
|
19
19
|
userFeedbacks?: UserFeedback[]
|
|
20
20
|
allUserFeedbacksLoading: boolean
|
|
21
21
|
addUserFeedbackLoading: boolean
|
|
@@ -77,14 +77,9 @@ const metadataViewReducer = createReducer(
|
|
|
77
77
|
related,
|
|
78
78
|
})),
|
|
79
79
|
|
|
80
|
-
on(MetadataViewActions.
|
|
80
|
+
on(MetadataViewActions.setLinkedRecords, (state, { linkedRecords }) => ({
|
|
81
81
|
...state,
|
|
82
|
-
|
|
83
|
-
})),
|
|
84
|
-
|
|
85
|
-
on(MetadataViewActions.setSourceOf, (state, { sourceOf }) => ({
|
|
86
|
-
...state,
|
|
87
|
-
sourceOf,
|
|
82
|
+
linkedRecords,
|
|
88
83
|
})),
|
|
89
84
|
|
|
90
85
|
/*
|
|
@@ -41,14 +41,9 @@ export const getRelated = createSelector(
|
|
|
41
41
|
(state: MetadataViewState) => state.related
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
export const
|
|
44
|
+
export const getLinkedRecords = createSelector(
|
|
45
45
|
getMdViewState,
|
|
46
|
-
(state: MetadataViewState) => state.
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
export const getSourceOf = createSelector(
|
|
50
|
-
getMdViewState,
|
|
51
|
-
(state: MetadataViewState) => state.sourceOf
|
|
46
|
+
(state: MetadataViewState) => state.linkedRecords
|
|
52
47
|
)
|
|
53
48
|
/*
|
|
54
49
|
Metadata selectors
|
|
@@ -22,7 +22,7 @@ export function flattenQueryParams(
|
|
|
22
22
|
const start = (flattened[key] as DateRange).start
|
|
23
23
|
const end = (flattened[key] as DateRange).end
|
|
24
24
|
flattened[key] = [
|
|
25
|
-
`${start ? formatDate(start) : ''}..${formatDate(end)
|
|
25
|
+
`${start ? formatDate(start) : ''}..${end ? formatDate(end) : ''}`,
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
@if (fieldType === 'dateRange') {
|
|
2
2
|
<gn-ui-date-range-dropdown
|
|
3
3
|
[title]="title"
|
|
4
|
-
[
|
|
5
|
-
|
|
6
|
-
(startDateChange)="onStartDateChange($event)"
|
|
7
|
-
(endDateChange)="onEndDateChange($event)"
|
|
4
|
+
[dateRange]="(selectedDateRange$ | async) ?? {}"
|
|
5
|
+
(dateRangeChange)="onDateRangeChange($event)"
|
|
8
6
|
></gn-ui-date-range-dropdown>
|
|
7
|
+
} @else if (fieldType === 'spatialExtent') {
|
|
8
|
+
<gn-ui-spatial-extent-dropdown
|
|
9
|
+
[title]="title"
|
|
10
|
+
[initialBbox]="(selectedBoundingBox$ | async) ?? null"
|
|
11
|
+
[maxFileSizeMb]="spatialExtentMaxFileSize"
|
|
12
|
+
(bboxChange)="onBboxChange($event)"
|
|
13
|
+
(errorChange)="onSpatialExtentError($event)"
|
|
14
|
+
[attr.data-cy-field]="fieldName"
|
|
15
|
+
></gn-ui-spatial-extent-dropdown>
|
|
9
16
|
} @else {
|
|
10
17
|
<gn-ui-dropdown-multiselect
|
|
11
18
|
class="w-full"
|