geonetwork-ui 2.11.0-dev.a83f0f51a → 2.11.0-dev.ca7fb6b05
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 +627 -499
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +83 -50
- package/index.d.ts.map +1 -1
- package/package.json +11 -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 +1 -0
- 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/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/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/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/map/src/lib/components/map-container/map-container.component.ts +4 -5
- 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/translations/de.json +1 -0
- package/translations/en.json +1 -0
- package/translations/es.json +1 -0
- package/translations/fr.json +1 -0
- package/translations/it.json +1 -0
- package/translations/nl.json +1 -0
- package/translations/pt.json +1 -0
- package/translations/sk.json +1 -0
- package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
|
@@ -6,7 +6,7 @@ import GeoJSON from 'ol/format/GeoJSON.js';
|
|
|
6
6
|
import { parse as parse$1 } from 'ol/xml.js';
|
|
7
7
|
import { format } from 'date-fns/format';
|
|
8
8
|
import { Namespace, Literal, lit, parse as parse$2, sym, BlankNode, graph } from 'rdflib';
|
|
9
|
-
import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, combineLatest, from, exhaustMap, throwError,
|
|
9
|
+
import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, forkJoin, combineLatest, from, exhaustMap, throwError, takeLast, firstValueFrom, merge, BehaviorSubject, timer, ReplaySubject, Subscription, first, distinctUntilChanged as distinctUntilChanged$1, animationFrameScheduler, debounceTime as debounceTime$1, Observable, buffer, tap as tap$2, combineLatestWith, take as take$1, catchError as catchError$1, takeUntil, EMPTY, mergeMap as mergeMap$1, withLatestFrom as withLatestFrom$1 } from 'rxjs';
|
|
10
10
|
import * as i0 from '@angular/core';
|
|
11
11
|
import { InjectionToken, inject, Injectable, NgModule, Injector, provideAppInitializer, makeEnvironmentProviders, ElementRef, HostListener, Input, Directive, Renderer2, DestroyRef, EventEmitter, Output, ViewChild, ChangeDetectionStrategy, Component, ViewEncapsulation, ChangeDetectorRef, HostBinding, ViewContainerRef, TemplateRef, ViewChildren, ContentChild, input, output, signal, viewChild, computed, ContentChildren, NgZone, ComponentFactoryResolver, afterNextRender, viewChildren } from '@angular/core';
|
|
12
12
|
import { HttpClient, HttpHeaders, HttpParams, HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi, HttpEventType } from '@angular/common/http';
|
|
@@ -79,10 +79,10 @@ import { ScrollingModule, CdkScrollable } from '@angular/cdk/scrolling';
|
|
|
79
79
|
import Duration from 'duration-relativetimeformat';
|
|
80
80
|
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
|
81
81
|
import { trigger, transition, animate, keyframes, style } from '@angular/animations';
|
|
82
|
-
import * as
|
|
83
|
-
import {
|
|
84
|
-
import
|
|
85
|
-
import
|
|
82
|
+
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
83
|
+
import { Vector, DataType } from 'apache-arrow';
|
|
84
|
+
import WFS from 'ol/format/WFS';
|
|
85
|
+
import GeoJSON$1 from 'ol/format/GeoJSON';
|
|
86
86
|
import { Chart, BarController, BarElement, CategoryScale, LinearScale, LineController, LineElement, PointElement, PieController, ArcElement, ScatterController, Tooltip, Colors, Legend } from 'chart.js';
|
|
87
87
|
import * as i2$4 from '@angular/material/sort';
|
|
88
88
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
|
@@ -90,7 +90,6 @@ import * as i1$b from '@angular/material/table';
|
|
|
90
90
|
import { MatTableModule } from '@angular/material/table';
|
|
91
91
|
import * as i3 from '@angular/material/paginator';
|
|
92
92
|
import { MatPaginatorIntl, MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
|
93
|
-
import { LetDirective } from '@ngrx/component';
|
|
94
93
|
import { Meta } from '@angular/platform-browser';
|
|
95
94
|
import { tablerFolderOpen } from '@ng-icons/tabler-icons';
|
|
96
95
|
import * as i3$1 from '@angular/material/radio';
|
|
@@ -18907,14 +18906,29 @@ class Gn4FieldMapper {
|
|
|
18907
18906
|
featureTypes: selectField(source, 'featureTypes'),
|
|
18908
18907
|
}, output),
|
|
18909
18908
|
related: (output, source) => {
|
|
18910
|
-
const
|
|
18909
|
+
const related = selectField(source, 'related');
|
|
18910
|
+
const fcatSource = selectField(getFirstValue(selectField(related, 'fcats')) ?? {}, '_source');
|
|
18911
18911
|
const featureCatalogIdentifier = selectField(fcatSource, 'uuid');
|
|
18912
|
-
const sourceOfLinks = getAsArray(selectField(
|
|
18912
|
+
const sourceOfLinks = getAsArray(selectField(related, 'hassources'));
|
|
18913
18913
|
const sourceOfIdentifiers = sourceOfLinks
|
|
18914
18914
|
.filter((link) => link['origin'] === 'catalog')
|
|
18915
18915
|
.map((link) => {
|
|
18916
18916
|
return selectField(selectField(link, '_source'), 'uuid');
|
|
18917
18917
|
});
|
|
18918
|
+
const siblingLinks = getAsArray(selectField(related, 'siblings'));
|
|
18919
|
+
const siblings = siblingLinks
|
|
18920
|
+
.filter((link) => link['origin'] === 'catalog')
|
|
18921
|
+
.map((link) => ({
|
|
18922
|
+
uniqueIdentifier: selectField(selectField(link, '_source'), 'uuid'),
|
|
18923
|
+
associationType: getAssociationTypeFromCode(selectField(selectField(link, 'properties'), 'associationType')),
|
|
18924
|
+
}));
|
|
18925
|
+
const associatedLinks = getAsArray(selectField(related, 'associated'));
|
|
18926
|
+
const associatedIdentifiers = associatedLinks
|
|
18927
|
+
.filter((link) => link['origin'] === 'catalog')
|
|
18928
|
+
.map((link) => {
|
|
18929
|
+
return selectField(selectField(link, '_source'), 'uuid');
|
|
18930
|
+
})
|
|
18931
|
+
.filter((uuid) => !siblings.some((sibling) => sibling.uniqueIdentifier === uuid));
|
|
18918
18932
|
const extraValues = {};
|
|
18919
18933
|
if (featureCatalogIdentifier) {
|
|
18920
18934
|
extraValues.featureCatalogIdentifier = featureCatalogIdentifier;
|
|
@@ -18922,6 +18936,12 @@ class Gn4FieldMapper {
|
|
|
18922
18936
|
if (sourceOfIdentifiers && sourceOfIdentifiers.length > 0) {
|
|
18923
18937
|
extraValues.sourceOfIdentifiers = sourceOfIdentifiers;
|
|
18924
18938
|
}
|
|
18939
|
+
if (associatedIdentifiers && associatedIdentifiers.length > 0) {
|
|
18940
|
+
extraValues.associatedIdentifiers = associatedIdentifiers;
|
|
18941
|
+
}
|
|
18942
|
+
if (siblings && siblings.length > 0) {
|
|
18943
|
+
extraValues.siblings = siblings;
|
|
18944
|
+
}
|
|
18925
18945
|
return Object.keys(extraValues).length > 0
|
|
18926
18946
|
? this.addExtra(extraValues, output)
|
|
18927
18947
|
: output;
|
|
@@ -20369,7 +20389,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20369
20389
|
}] } });
|
|
20370
20390
|
|
|
20371
20391
|
var name = "geonetwork-ui";
|
|
20372
|
-
var version = "2.11.0-dev.
|
|
20392
|
+
var version = "2.11.0-dev.ca7fb6b05";
|
|
20373
20393
|
var engines = {
|
|
20374
20394
|
node: ">=24"
|
|
20375
20395
|
};
|
|
@@ -20399,20 +20419,21 @@ var peerDependencies = {
|
|
|
20399
20419
|
"@ngrx/store": "19.x || 20.x || 21.x",
|
|
20400
20420
|
"@ngrx/store-devtools": "19.x || 20.x || 21.x",
|
|
20401
20421
|
"@ngrx/operators": "19.x || 20.x || 21.x",
|
|
20402
|
-
"@ngx-translate/core": "
|
|
20403
|
-
"@ngx-translate/http-loader": "
|
|
20404
|
-
"flag-icons": "
|
|
20422
|
+
"@ngx-translate/core": "17.x",
|
|
20423
|
+
"@ngx-translate/http-loader": "17.x",
|
|
20424
|
+
"flag-icons": "~7.5.0",
|
|
20405
20425
|
rxjs: "7.x",
|
|
20406
20426
|
"zone.js": "*",
|
|
20407
20427
|
tailwindcss: "3.x"
|
|
20408
20428
|
};
|
|
20409
20429
|
var dependencies = {
|
|
20410
20430
|
"@biesbjerg/ngx-translate-extract-marker": "~1.0.0",
|
|
20411
|
-
"@camptocamp/ogc-client": "1.3.1-dev.
|
|
20412
|
-
"@
|
|
20413
|
-
"@geospatial-sdk/
|
|
20414
|
-
"@geospatial-sdk/
|
|
20415
|
-
"@geospatial-sdk/
|
|
20431
|
+
"@camptocamp/ogc-client": "1.3.1-dev.200c02a",
|
|
20432
|
+
"@duckdb/duckdb-wasm": "~1.32.0",
|
|
20433
|
+
"@geospatial-sdk/core": "0.0.5-dev.78",
|
|
20434
|
+
"@geospatial-sdk/geocoding": "0.0.5-dev.78",
|
|
20435
|
+
"@geospatial-sdk/legend": "0.0.5-dev.78",
|
|
20436
|
+
"@geospatial-sdk/openlayers": "0.0.5-dev.78",
|
|
20416
20437
|
"@ltd/j-toml": "~1.38.0",
|
|
20417
20438
|
"@messageformat/core": "~3.4.0",
|
|
20418
20439
|
"@ng-icons/core": "~32.5.0",
|
|
@@ -20420,7 +20441,7 @@ var dependencies = {
|
|
|
20420
20441
|
"@ng-icons/material-icons": "~32.5.0",
|
|
20421
20442
|
"@ng-icons/tabler-icons": "~32.4.0",
|
|
20422
20443
|
"@rgrove/parse-xml": "4.2.0",
|
|
20423
|
-
|
|
20444
|
+
"apache-arrow": "~17.0.0",
|
|
20424
20445
|
"chart.js": "~4.5.1",
|
|
20425
20446
|
"chroma-js": "~3.2.0",
|
|
20426
20447
|
"date-fns": "4.1.0",
|
|
@@ -20433,7 +20454,6 @@ var dependencies = {
|
|
|
20433
20454
|
"ngx-dropzone": "~3.1.0",
|
|
20434
20455
|
"ngx-translate-messageformat-compiler": "~7.2.0",
|
|
20435
20456
|
ol: "~10.8.0",
|
|
20436
|
-
papaparse: "~5.5.3",
|
|
20437
20457
|
proj4: "~2.20.4",
|
|
20438
20458
|
rdflib: "~2.3.5",
|
|
20439
20459
|
semver: "~7.7.4",
|
|
@@ -20678,6 +20698,7 @@ class ElasticsearchService {
|
|
|
20678
20698
|
values: uuids,
|
|
20679
20699
|
},
|
|
20680
20700
|
},
|
|
20701
|
+
size: uuids.length,
|
|
20681
20702
|
};
|
|
20682
20703
|
}
|
|
20683
20704
|
getRelatedRecordPayload(record, size = 6, _source = [...ES_SOURCE_SUMMARY, 'createDate']) {
|
|
@@ -21229,7 +21250,7 @@ class Gn4Repository {
|
|
|
21229
21250
|
}
|
|
21230
21251
|
getRecord(uniqueIdentifier) {
|
|
21231
21252
|
return this.gn4SearchApi
|
|
21232
|
-
.search('bucket', ['fcats', 'hassources'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21253
|
+
.search('bucket', ['fcats', 'hassources', 'siblings', 'associated'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21233
21254
|
.pipe(map$1((results) => results.hits.hits[0]), switchMap((record) => record ? this.gn4Mapper.readRecord(record) : of(null)));
|
|
21234
21255
|
}
|
|
21235
21256
|
getMultipleRecords(uniqueIdentifiers) {
|
|
@@ -21283,19 +21304,28 @@ class Gn4Repository {
|
|
|
21283
21304
|
.search('bucket', null, JSON.stringify(this.gn4SearchHelper.getRelatedRecordPayload(similarTo, 3)))
|
|
21284
21305
|
.pipe(switchMap((results) => this.gn4Mapper.readRecords(results.hits.hits)));
|
|
21285
21306
|
}
|
|
21286
|
-
|
|
21287
|
-
const
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
|
|
21296
|
-
|
|
21307
|
+
getLinkedRecords(record) {
|
|
21308
|
+
const siblings = (record.extras?.['siblings'] ?? []);
|
|
21309
|
+
const relations = [
|
|
21310
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? [])],
|
|
21311
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? [])],
|
|
21312
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
21313
|
+
[
|
|
21314
|
+
'associated',
|
|
21315
|
+
(record.extras?.['associatedIdentifiers'] ?? []),
|
|
21316
|
+
],
|
|
21317
|
+
];
|
|
21318
|
+
const requested = relations.filter(([, identifiers]) => identifiers.length > 0);
|
|
21319
|
+
if (requested.length === 0) {
|
|
21320
|
+
return of([]);
|
|
21297
21321
|
}
|
|
21298
|
-
return
|
|
21322
|
+
return forkJoin(requested.map(([relation, identifiers]) => this.getMultipleRecords(identifiers).pipe(map$1((records) => (records ?? []).map((record) => ({
|
|
21323
|
+
record,
|
|
21324
|
+
relation,
|
|
21325
|
+
associationType: relation === 'sibling'
|
|
21326
|
+
? siblings.find(({ uniqueIdentifier }) => uniqueIdentifier === record.uniqueIdentifier)?.associationType
|
|
21327
|
+
: undefined,
|
|
21328
|
+
}))), catchError(() => of([]))))).pipe(map$1((groups) => groups.flat()));
|
|
21299
21329
|
}
|
|
21300
21330
|
aggregate(params) {
|
|
21301
21331
|
// if aggregations are empty, return an empty object right away
|
|
@@ -22556,9 +22586,8 @@ const VECTOR_STYLE_DEFAULT = new InjectionToken('vectorStyleDefault', {
|
|
|
22556
22586
|
});
|
|
22557
22587
|
|
|
22558
22588
|
const DEFAULT_BASEMAP_LAYER = {
|
|
22559
|
-
type: '
|
|
22560
|
-
|
|
22561
|
-
attributions: `<span>© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, © <a href="https://carto.com/">Carto</a></span>`,
|
|
22589
|
+
type: 'maplibre-style',
|
|
22590
|
+
styleUrl: `https://basemaps.cartocdn.com/gl/positron-gl-style/style.json`,
|
|
22562
22591
|
};
|
|
22563
22592
|
const DEFAULT_VIEW = {
|
|
22564
22593
|
center: [0, 15],
|
|
@@ -32512,8 +32541,7 @@ const closeMetadata = createAction('[Metadata view] close');
|
|
|
32512
32541
|
Related actions
|
|
32513
32542
|
*/
|
|
32514
32543
|
const setRelated = createAction('[Metadata view] Set related records', props());
|
|
32515
|
-
const
|
|
32516
|
-
const setSourceOf = createAction('[Metadata view] Set has sources', props());
|
|
32544
|
+
const setLinkedRecords = createAction('[Metadata view] Set associated records', props());
|
|
32517
32545
|
/*
|
|
32518
32546
|
ChartConfig actions
|
|
32519
32547
|
*/
|
|
@@ -32545,9 +32573,8 @@ var mdview_actions = /*#__PURE__*/Object.freeze({
|
|
|
32545
32573
|
loadUserFeedbacksSuccess: loadUserFeedbacksSuccess,
|
|
32546
32574
|
setChartConfig: setChartConfig,
|
|
32547
32575
|
setIncompleteMetadata: setIncompleteMetadata,
|
|
32548
|
-
|
|
32549
|
-
|
|
32550
|
-
setSources: setSources
|
|
32576
|
+
setLinkedRecords: setLinkedRecords,
|
|
32577
|
+
setRelated: setRelated
|
|
32551
32578
|
});
|
|
32552
32579
|
|
|
32553
32580
|
const METADATA_VIEW_FEATURE_STATE_KEY = 'metadataView';
|
|
@@ -32590,12 +32617,9 @@ on(loadFullMetadata, (state) => ({
|
|
|
32590
32617
|
on(setRelated, (state, { related }) => ({
|
|
32591
32618
|
...state,
|
|
32592
32619
|
related,
|
|
32593
|
-
})), on(
|
|
32594
|
-
...state,
|
|
32595
|
-
sources,
|
|
32596
|
-
})), on(setSourceOf, (state, { sourceOf }) => ({
|
|
32620
|
+
})), on(setLinkedRecords, (state, { linkedRecords }) => ({
|
|
32597
32621
|
...state,
|
|
32598
|
-
|
|
32622
|
+
linkedRecords,
|
|
32599
32623
|
})),
|
|
32600
32624
|
/*
|
|
32601
32625
|
ChartConfig reducers
|
|
@@ -32657,8 +32681,7 @@ const getMetadataError = createSelector(getMdViewState, (state) => state.error);
|
|
|
32657
32681
|
Related selectors
|
|
32658
32682
|
*/
|
|
32659
32683
|
const getRelated = createSelector(getMdViewState, (state) => state.related);
|
|
32660
|
-
const
|
|
32661
|
-
const getSourceOf = createSelector(getMdViewState, (state) => state.sourceOf);
|
|
32684
|
+
const getLinkedRecords = createSelector(getMdViewState, (state) => state.linkedRecords);
|
|
32662
32685
|
/*
|
|
32663
32686
|
Metadata selectors
|
|
32664
32687
|
*/
|
|
@@ -32759,212 +32782,10 @@ function parseHeaders(httpHeaders) {
|
|
|
32759
32782
|
return result;
|
|
32760
32783
|
}
|
|
32761
32784
|
|
|
32762
|
-
|
|
32763
|
-
|
|
32764
|
-
|
|
32765
|
-
? fileExtensionMatches[1].toLowerCase()
|
|
32766
|
-
: null;
|
|
32767
|
-
// 1. type hint
|
|
32768
|
-
if (typeHint)
|
|
32769
|
-
return Promise.resolve(typeHint);
|
|
32770
|
-
// 2. content-type header
|
|
32771
|
-
const headers = await fetchHeaders(url);
|
|
32772
|
-
if ('supportedType' in headers)
|
|
32773
|
-
return headers.supportedType;
|
|
32774
|
-
// 3. file extension from url
|
|
32775
|
-
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
32776
|
-
return fileExtension;
|
|
32777
|
-
// no type inferred or hinted
|
|
32778
|
-
if ('mimeType' in headers)
|
|
32779
|
-
throw FetchError.unsupportedType(headers.mimeType);
|
|
32780
|
-
else
|
|
32781
|
-
throw FetchError.unknownType();
|
|
32782
|
-
}
|
|
32783
|
-
function fetchHeaders(url) {
|
|
32784
|
-
return sharedFetch(url, 'HEAD')
|
|
32785
|
-
.catch((error) => {
|
|
32786
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32787
|
-
})
|
|
32788
|
-
.then((response) => {
|
|
32789
|
-
if (!response.ok) {
|
|
32790
|
-
throw FetchError.http(response.status);
|
|
32791
|
-
}
|
|
32792
|
-
return parseHeaders(response.headers);
|
|
32793
|
-
});
|
|
32794
|
-
}
|
|
32795
|
-
function fetchDataAsText(url, cacheActive) {
|
|
32796
|
-
const fetchFactory = () => sharedFetch(url)
|
|
32797
|
-
.catch((error) => {
|
|
32798
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32799
|
-
})
|
|
32800
|
-
.then(async (response) => {
|
|
32801
|
-
if (!response.ok) {
|
|
32802
|
-
const clonedResponse = response.clone();
|
|
32803
|
-
throw FetchError.http(response.status, await clonedResponse.text());
|
|
32804
|
-
}
|
|
32805
|
-
const clonedResponse = response.clone();
|
|
32806
|
-
return clonedResponse.text();
|
|
32807
|
-
});
|
|
32808
|
-
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
32809
|
-
}
|
|
32810
|
-
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
32811
|
-
const fetchFactory = () => sharedFetch(url)
|
|
32812
|
-
.catch((error) => {
|
|
32813
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32814
|
-
})
|
|
32815
|
-
.then(async (response) => {
|
|
32816
|
-
if (!response.ok) {
|
|
32817
|
-
throw FetchError.http(response.status, await response.text());
|
|
32818
|
-
}
|
|
32819
|
-
// convert to a numeric array so that we can store the response in cache
|
|
32820
|
-
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
32821
|
-
});
|
|
32822
|
-
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
32823
|
-
return new Uint8Array(array).buffer;
|
|
32824
|
-
});
|
|
32825
|
-
}
|
|
32826
|
-
function tryParseDate(input) {
|
|
32827
|
-
if (typeof input !== 'string')
|
|
32828
|
-
return null;
|
|
32829
|
-
function tryIso(value) {
|
|
32830
|
-
const parsed = parseISO(value);
|
|
32831
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32832
|
-
}
|
|
32833
|
-
function tryFormat(value, format) {
|
|
32834
|
-
const parsed = parse$4(value, format, new Date());
|
|
32835
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32836
|
-
}
|
|
32837
|
-
return (tryIso(input) ||
|
|
32838
|
-
tryFormat(input, 'dd/MM/yyyy') ||
|
|
32839
|
-
tryFormat(input, 'dd.MM.yyyy') ||
|
|
32840
|
-
tryFormat(input, 'MM/dd/yyyy') ||
|
|
32841
|
-
null);
|
|
32842
|
-
}
|
|
32843
|
-
function tryParseNumber(input) {
|
|
32844
|
-
if (isNaN(input))
|
|
32845
|
-
return null;
|
|
32846
|
-
const parsed = parseFloat(input);
|
|
32847
|
-
return isNaN(parsed) ? null : parsed;
|
|
32848
|
-
}
|
|
32849
|
-
function jsonToGeojsonFeature(object) {
|
|
32850
|
-
const { id, properties } = Object.keys(object)
|
|
32851
|
-
.map((property) => (property ? property : 'unknown')) //prevent empty strings
|
|
32852
|
-
.reduce((prev, curr) => curr.toLowerCase().endsWith('id')
|
|
32853
|
-
? {
|
|
32854
|
-
...prev,
|
|
32855
|
-
id: object[curr],
|
|
32856
|
-
}
|
|
32857
|
-
: {
|
|
32858
|
-
...prev,
|
|
32859
|
-
properties: { ...prev.properties, [curr]: object[curr] },
|
|
32860
|
-
}, { id: undefined, properties: {} });
|
|
32861
|
-
return {
|
|
32862
|
-
type: 'Feature',
|
|
32863
|
-
geometry: null,
|
|
32864
|
-
properties,
|
|
32865
|
-
...(id !== undefined && { id }),
|
|
32866
|
-
};
|
|
32867
|
-
}
|
|
32868
|
-
function mutateProperties(items, mutators) {
|
|
32869
|
-
const mutatorKeys = Object.keys(mutators);
|
|
32870
|
-
for (let i = 0, ii = items.length; i < ii; i++) {
|
|
32871
|
-
const item = items[i];
|
|
32872
|
-
for (const mutatorField of mutatorKeys) {
|
|
32873
|
-
if (!(mutatorField in item.properties))
|
|
32874
|
-
continue;
|
|
32875
|
-
item.properties[mutatorField] = mutators[mutatorField](item.properties[mutatorField]);
|
|
32876
|
-
}
|
|
32877
|
-
}
|
|
32878
|
-
return items;
|
|
32879
|
-
}
|
|
32880
|
-
const SAMPLE_SIZE = 20;
|
|
32881
|
-
/**
|
|
32882
|
-
* This will infer field types from a list of data items and cast the values accordingly
|
|
32883
|
-
* @param items
|
|
32884
|
-
* @param inferTypes
|
|
32885
|
-
*/
|
|
32886
|
-
function processItemProperties(items, inferTypes = false) {
|
|
32887
|
-
const foundFields = {};
|
|
32888
|
-
for (let i = 0, ii = Math.min(SAMPLE_SIZE, items.length); i < ii; i++) {
|
|
32889
|
-
const item = items[i];
|
|
32890
|
-
const fields = Object.keys(item.properties);
|
|
32891
|
-
for (const field of fields) {
|
|
32892
|
-
if (!(field in foundFields)) {
|
|
32893
|
-
foundFields[field] = {
|
|
32894
|
-
label: field,
|
|
32895
|
-
name: field,
|
|
32896
|
-
type: null,
|
|
32897
|
-
};
|
|
32898
|
-
}
|
|
32899
|
-
const value = item.properties[field];
|
|
32900
|
-
const info = foundFields[field];
|
|
32901
|
-
if (value === undefined || value === '' || value === null)
|
|
32902
|
-
continue;
|
|
32903
|
-
if (!inferTypes) {
|
|
32904
|
-
if (info.type === null && typeof value === 'number') {
|
|
32905
|
-
info.type = 'number';
|
|
32906
|
-
}
|
|
32907
|
-
else if (info.type === 'number' && typeof value !== 'number') {
|
|
32908
|
-
info.type = 'string';
|
|
32909
|
-
}
|
|
32910
|
-
continue;
|
|
32911
|
-
}
|
|
32912
|
-
const parsedNumber = tryParseNumber(value);
|
|
32913
|
-
if (info.type === null && parsedNumber !== null) {
|
|
32914
|
-
info.type = 'number';
|
|
32915
|
-
continue;
|
|
32916
|
-
}
|
|
32917
|
-
else if (info.type === 'number' && parsedNumber === null) {
|
|
32918
|
-
info.type = 'string';
|
|
32919
|
-
continue;
|
|
32920
|
-
}
|
|
32921
|
-
const parsedDate = tryParseDate(value);
|
|
32922
|
-
if (info.type === null && parsedDate !== null) {
|
|
32923
|
-
info.type = 'date';
|
|
32924
|
-
}
|
|
32925
|
-
else if (info.type === 'date' && parsedDate === null) {
|
|
32926
|
-
info.type = 'string';
|
|
32927
|
-
}
|
|
32928
|
-
}
|
|
32929
|
-
}
|
|
32930
|
-
const properties = [];
|
|
32931
|
-
const mutators = {};
|
|
32932
|
-
for (const field in foundFields) {
|
|
32933
|
-
const info = foundFields[field];
|
|
32934
|
-
if (info.type === 'number') {
|
|
32935
|
-
mutators[field] = tryParseNumber;
|
|
32936
|
-
}
|
|
32937
|
-
else if (info.type === 'date') {
|
|
32938
|
-
mutators[field] = tryParseDate;
|
|
32939
|
-
}
|
|
32940
|
-
properties.push({ ...info, type: info.type || 'string' });
|
|
32941
|
-
}
|
|
32942
|
-
if (inferTypes) {
|
|
32943
|
-
mutateProperties(items, mutators);
|
|
32944
|
-
}
|
|
32945
|
-
return { items, properties };
|
|
32946
|
-
}
|
|
32947
|
-
/**
|
|
32948
|
-
* This creates a Proxy that allows reading and writing to the data item properties
|
|
32949
|
-
* as if it was a simple array of JSON objects
|
|
32950
|
-
* @param items
|
|
32951
|
-
*/
|
|
32952
|
-
function getJsonDataItemsProxy(items) {
|
|
32953
|
-
return new Proxy(items, {
|
|
32954
|
-
get(target, p) {
|
|
32955
|
-
if (typeof p === 'string' &&
|
|
32956
|
-
!Number.isNaN(parseInt(p)) &&
|
|
32957
|
-
target[p]?.properties) {
|
|
32958
|
-
return target[p].properties;
|
|
32959
|
-
}
|
|
32960
|
-
return target[p];
|
|
32961
|
-
},
|
|
32962
|
-
set() {
|
|
32963
|
-
throw new Error('This object is read-only');
|
|
32964
|
-
},
|
|
32965
|
-
});
|
|
32785
|
+
const GEOMETRY_COLUMN_ALIAS = '__geometry__';
|
|
32786
|
+
function fieldToSql(name) {
|
|
32787
|
+
return `"${name.replace(/"/g, '""')}"`; // escape double quotes in field names
|
|
32966
32788
|
}
|
|
32967
|
-
|
|
32968
32789
|
function filterToSql(filter) {
|
|
32969
32790
|
const operator = filter[0];
|
|
32970
32791
|
const args = filter.slice(1);
|
|
@@ -32979,10 +32800,10 @@ function filterToSql(filter) {
|
|
|
32979
32800
|
case '=':
|
|
32980
32801
|
case '!=':
|
|
32981
32802
|
case 'like':
|
|
32982
|
-
return
|
|
32803
|
+
return `${fieldToSql(args[0])} ${operator.toUpperCase()} ${valueToSql(args[1])}`;
|
|
32983
32804
|
case 'in': {
|
|
32984
32805
|
const values = args.slice(1);
|
|
32985
|
-
return
|
|
32806
|
+
return `${fieldToSql(args[0])} IN (${values.map(valueToSql).join(', ')})`;
|
|
32986
32807
|
}
|
|
32987
32808
|
case 'and':
|
|
32988
32809
|
case 'or': {
|
|
@@ -33001,17 +32822,18 @@ function aggregationToSql(aggregation) {
|
|
|
33001
32822
|
const field = aggregation[1];
|
|
33002
32823
|
switch (operation) {
|
|
33003
32824
|
case 'average':
|
|
33004
|
-
return `AVG(
|
|
32825
|
+
return `CAST(AVG(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`average(${field})`)}`;
|
|
33005
32826
|
case 'sum':
|
|
33006
32827
|
case 'max':
|
|
33007
32828
|
case 'min':
|
|
33008
|
-
return
|
|
32829
|
+
return `CAST(${operation.toUpperCase()}(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`${operation}(${field})`)}`;
|
|
33009
32830
|
case 'count':
|
|
33010
|
-
return 'COUNT(*) as
|
|
32831
|
+
return 'CAST(COUNT(*) AS INTEGER) as "count()"'; // we don't need Bigint precision here
|
|
33011
32832
|
}
|
|
33012
32833
|
}
|
|
33013
32834
|
/**
|
|
33014
32835
|
* Leave arguments at null if not used
|
|
32836
|
+
* @param tableName
|
|
33015
32837
|
* @param selected
|
|
33016
32838
|
* @param filter
|
|
33017
32839
|
* @param sort
|
|
@@ -33020,22 +32842,25 @@ function aggregationToSql(aggregation) {
|
|
|
33020
32842
|
* @param groupBy
|
|
33021
32843
|
* @param aggregations
|
|
33022
32844
|
*/
|
|
33023
|
-
function generateSqlQuery(selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null) {
|
|
32845
|
+
function generateSqlQuery(tableName, selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null, geometryColumn = null) {
|
|
33024
32846
|
let sqlSelect = 'SELECT *';
|
|
33025
|
-
const sqlFrom =
|
|
32847
|
+
const sqlFrom = ` FROM ${tableName}`;
|
|
33026
32848
|
let sqlOrderBy = '';
|
|
33027
32849
|
let sqlWhere = '';
|
|
33028
32850
|
let sqlLimit = '';
|
|
33029
32851
|
let sqlGroupBy = '';
|
|
33030
32852
|
if (selected !== null) {
|
|
33031
|
-
sqlSelect = `SELECT ${selected.map(
|
|
32853
|
+
sqlSelect = `SELECT ${selected.map(fieldToSql).join(', ')}`;
|
|
32854
|
+
}
|
|
32855
|
+
if (geometryColumn !== null) {
|
|
32856
|
+
sqlSelect += `, ST_AsGeoJSON(${fieldToSql(geometryColumn)}) as ${GEOMETRY_COLUMN_ALIAS}`;
|
|
33032
32857
|
}
|
|
33033
32858
|
if (filter !== null) {
|
|
33034
32859
|
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33035
32860
|
}
|
|
33036
32861
|
if (sort?.length) {
|
|
33037
32862
|
sqlOrderBy = ` ORDER BY ${sort
|
|
33038
|
-
.map((sort) =>
|
|
32863
|
+
.map((sort) => `${fieldToSql(sort[1])} ${sort[0].toUpperCase()}`)
|
|
33039
32864
|
.join(', ')}`;
|
|
33040
32865
|
}
|
|
33041
32866
|
if (startIndex !== null && count !== null) {
|
|
@@ -33045,17 +32870,212 @@ function generateSqlQuery(selected = null, filter = null, sort = null, startInde
|
|
|
33045
32870
|
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33046
32871
|
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33047
32872
|
const sqlGroupByFields = groupedByDistinct
|
|
33048
|
-
.map((group) =>
|
|
32873
|
+
.map((group) => fieldToSql(group[1]))
|
|
33049
32874
|
.join(', ');
|
|
33050
32875
|
const sqlGroupBySelect = groupedByDistinct
|
|
33051
|
-
.map((group) =>
|
|
32876
|
+
.map((group) => `${fieldToSql(group[1])} as ${fieldToSql(`distinct(${group[1]})`)}`)
|
|
33052
32877
|
.join(', ');
|
|
33053
32878
|
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33054
32879
|
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33055
32880
|
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33056
32881
|
}
|
|
33057
32882
|
}
|
|
33058
|
-
return sqlSelect + sqlFrom + sqlGroupBy +
|
|
32883
|
+
return sqlSelect + sqlFrom + sqlGroupBy + sqlWhere + sqlOrderBy + sqlLimit;
|
|
32884
|
+
}
|
|
32885
|
+
|
|
32886
|
+
function arrowTableToDataItems(table) {
|
|
32887
|
+
const fields = table.schema.fields;
|
|
32888
|
+
return table.toArray().map((row) => {
|
|
32889
|
+
const rowJson = row.toJSON();
|
|
32890
|
+
const feature = {
|
|
32891
|
+
type: 'Feature',
|
|
32892
|
+
geometry: null,
|
|
32893
|
+
properties: {},
|
|
32894
|
+
};
|
|
32895
|
+
const keys = Object.keys(rowJson);
|
|
32896
|
+
for (let i = 0; i < keys.length; i++) {
|
|
32897
|
+
const key = keys[i];
|
|
32898
|
+
const dataType = fields[i].type;
|
|
32899
|
+
let value = rowJson[key];
|
|
32900
|
+
// this might happen if we get an array inside a field
|
|
32901
|
+
if (value instanceof Vector) {
|
|
32902
|
+
value = Array.from(value);
|
|
32903
|
+
}
|
|
32904
|
+
// cast bigints to ints
|
|
32905
|
+
if (typeof value === 'bigint') {
|
|
32906
|
+
value = Number(value);
|
|
32907
|
+
}
|
|
32908
|
+
// rename columns with empty name
|
|
32909
|
+
if (!key) {
|
|
32910
|
+
feature.properties['unknown'] = value;
|
|
32911
|
+
continue;
|
|
32912
|
+
}
|
|
32913
|
+
// assign properties that look like an id to the geojson `id` field
|
|
32914
|
+
if (/^(object|feature)?_?id$/.test(key.toLowerCase()) &&
|
|
32915
|
+
(typeof value == 'string' || typeof value === 'number')) {
|
|
32916
|
+
feature.id = value;
|
|
32917
|
+
}
|
|
32918
|
+
// if a date is in timestamp (number) format, convert it to native
|
|
32919
|
+
if (typeof value === 'number' &&
|
|
32920
|
+
(DataType.isTimestamp(dataType) || DataType.isDate(dataType))) {
|
|
32921
|
+
value = new Date(value);
|
|
32922
|
+
}
|
|
32923
|
+
// if a binary field (most likely a geometry column): skip
|
|
32924
|
+
if (DataType.isBinary(dataType)) {
|
|
32925
|
+
continue;
|
|
32926
|
+
}
|
|
32927
|
+
// geometry column
|
|
32928
|
+
if (key === GEOMETRY_COLUMN_ALIAS && DataType.isUtf8(dataType)) {
|
|
32929
|
+
feature.geometry = JSON.parse(value);
|
|
32930
|
+
continue;
|
|
32931
|
+
}
|
|
32932
|
+
feature.properties[key] = value;
|
|
32933
|
+
}
|
|
32934
|
+
return feature;
|
|
32935
|
+
});
|
|
32936
|
+
}
|
|
32937
|
+
|
|
32938
|
+
// init code taken from https://github.com/duckdb/duckdb-wasm/blob/main/packages/duckdb-wasm/README.md
|
|
32939
|
+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
|
|
32940
|
+
// from https://duckdb.org/docs/current/sql/data_types/overview
|
|
32941
|
+
const typesMapping = {
|
|
32942
|
+
INTEGER: 'number',
|
|
32943
|
+
SMALLINT: 'number',
|
|
32944
|
+
TINYINT: 'number',
|
|
32945
|
+
BIGINT: 'number',
|
|
32946
|
+
HUGEINT: 'number',
|
|
32947
|
+
UINTEGER: 'number',
|
|
32948
|
+
USMALLINT: 'number',
|
|
32949
|
+
UTINYINT: 'number',
|
|
32950
|
+
UBIGINT: 'number',
|
|
32951
|
+
UHUGEINT: 'number',
|
|
32952
|
+
DOUBLE: 'number',
|
|
32953
|
+
BIGNUM: 'number',
|
|
32954
|
+
DECIMAL: 'number',
|
|
32955
|
+
NUMERIC: 'number',
|
|
32956
|
+
FLOAT: 'number',
|
|
32957
|
+
REAL: 'number',
|
|
32958
|
+
'TIMESTAMP WITH TIME ZONE': 'date',
|
|
32959
|
+
TIMESTAMP: 'date',
|
|
32960
|
+
DATE: 'date',
|
|
32961
|
+
CHAR: 'string',
|
|
32962
|
+
VARCHAR: 'string',
|
|
32963
|
+
TEXT: 'string',
|
|
32964
|
+
UUID: 'string',
|
|
32965
|
+
BLOB: 'string',
|
|
32966
|
+
BIT: 'string',
|
|
32967
|
+
INTERVAL: 'string',
|
|
32968
|
+
LIST: 'string',
|
|
32969
|
+
BOOLEAN: 'boolean',
|
|
32970
|
+
};
|
|
32971
|
+
class Engine {
|
|
32972
|
+
async makeInit() {
|
|
32973
|
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
|
|
32974
|
+
let worker_url = bundle.mainWorker;
|
|
32975
|
+
// this is necessary to let browsers execute WASM code coming from a cross-origin host
|
|
32976
|
+
if (worker_url.startsWith('https://')) {
|
|
32977
|
+
worker_url = URL.createObjectURL(new Blob([`importScripts("${worker_url}");`], {
|
|
32978
|
+
type: 'text/javascript',
|
|
32979
|
+
}));
|
|
32980
|
+
}
|
|
32981
|
+
const worker = new Worker(worker_url);
|
|
32982
|
+
const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
|
|
32983
|
+
this.db = new duckdb.AsyncDuckDB(logger, worker);
|
|
32984
|
+
await this.db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
32985
|
+
// setup duckdb options
|
|
32986
|
+
const conn = await this.db.connect();
|
|
32987
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
32988
|
+
await conn.query(`INSTALL spatial; LOAD spatial;`);
|
|
32989
|
+
await conn.query(`SET TimeZone = '${timezone}';`);
|
|
32990
|
+
await conn.query(`SET threads = 1;`);
|
|
32991
|
+
// await conn.query(`SET memory_limit = '2GB';`) // used for experimenting when we encounter memory issues
|
|
32992
|
+
await conn.query(`SET preserve_insertion_order = false;`); // this reduces memory usage when loading a dataset
|
|
32993
|
+
conn.close();
|
|
32994
|
+
return this;
|
|
32995
|
+
}
|
|
32996
|
+
isReady() {
|
|
32997
|
+
if (!this.init_) {
|
|
32998
|
+
this.init_ = this.makeInit();
|
|
32999
|
+
}
|
|
33000
|
+
return this.init_;
|
|
33001
|
+
}
|
|
33002
|
+
/**
|
|
33003
|
+
* Returns information about a dataset once it's loaded:
|
|
33004
|
+
* - a list of properties description
|
|
33005
|
+
* - the name of the dataset geometry column (null if no geometry present)
|
|
33006
|
+
* @param datasetId name of the table under which the dataset will be stored
|
|
33007
|
+
* @param loadQuery duckdb-specific query for creating a table out of the data
|
|
33008
|
+
* @param forceReload if true, any existing data will be dropped and redownloaded
|
|
33009
|
+
*/
|
|
33010
|
+
async loadFile(datasetId, loadQuery, forceReload = false) {
|
|
33011
|
+
const conn = await this.db.connect();
|
|
33012
|
+
let results;
|
|
33013
|
+
// either we want to recreate the table, or we keep it if it already exists
|
|
33014
|
+
const safeLoadQuery = forceReload
|
|
33015
|
+
? `DROP TABLE IF EXISTS ${datasetId};
|
|
33016
|
+
${loadQuery}`
|
|
33017
|
+
: loadQuery.replace(/CREATE TABLE(?! IF NOT EXISTS)/gi, 'CREATE TABLE IF NOT EXISTS');
|
|
33018
|
+
// create the table
|
|
33019
|
+
try {
|
|
33020
|
+
results = await conn.query(safeLoadQuery);
|
|
33021
|
+
}
|
|
33022
|
+
catch (e) {
|
|
33023
|
+
throw new FetchError('parse', `DuckDB encountered an error when loading the data: ${e.message}`);
|
|
33024
|
+
}
|
|
33025
|
+
// read rows count
|
|
33026
|
+
results = await conn.query(`SELECT count(*) FROM ${datasetId}`);
|
|
33027
|
+
const { 'count_star()': recordsCount } = results.toArray()[0].toJSON();
|
|
33028
|
+
// read columns
|
|
33029
|
+
results = await conn.query(`SELECT * FROM information_schema.columns WHERE table_name = '${datasetId}';`);
|
|
33030
|
+
let geometryColumn = null;
|
|
33031
|
+
const properties = results
|
|
33032
|
+
.toArray()
|
|
33033
|
+
.map((row) => {
|
|
33034
|
+
const rowObj = row.toJSON();
|
|
33035
|
+
if (rowObj['data_type'] === 'GEOMETRY') {
|
|
33036
|
+
// the geometry is not part of the properties
|
|
33037
|
+
// note: right now we only keep one geometry column name, but if there are multiple they will
|
|
33038
|
+
// all get discarded
|
|
33039
|
+
geometryColumn = rowObj['column_name'];
|
|
33040
|
+
return null;
|
|
33041
|
+
}
|
|
33042
|
+
return {
|
|
33043
|
+
name: rowObj['column_name'],
|
|
33044
|
+
label: rowObj['column_name'],
|
|
33045
|
+
type: typesMapping[rowObj['data_type']] ?? 'other',
|
|
33046
|
+
};
|
|
33047
|
+
})
|
|
33048
|
+
.filter((prop) => prop !== null);
|
|
33049
|
+
conn.close();
|
|
33050
|
+
return {
|
|
33051
|
+
properties,
|
|
33052
|
+
geometryColumn,
|
|
33053
|
+
rowsCount: Number(recordsCount),
|
|
33054
|
+
};
|
|
33055
|
+
}
|
|
33056
|
+
// register a Uint8 buffer using a handle in the duckdb instance
|
|
33057
|
+
async registerData(name, buffer) {
|
|
33058
|
+
return this.db.registerFileBuffer(name, buffer);
|
|
33059
|
+
}
|
|
33060
|
+
/**
|
|
33061
|
+
* @param query duckdb-specific query for fetching items
|
|
33062
|
+
*/
|
|
33063
|
+
async queryItems(query) {
|
|
33064
|
+
const conn = await this.db.connect();
|
|
33065
|
+
const results = await conn.query(query);
|
|
33066
|
+
conn.close();
|
|
33067
|
+
return arrowTableToDataItems(results);
|
|
33068
|
+
}
|
|
33069
|
+
close() {
|
|
33070
|
+
this.db?.terminate();
|
|
33071
|
+
}
|
|
33072
|
+
}
|
|
33073
|
+
let engine = null;
|
|
33074
|
+
async function getEngine() {
|
|
33075
|
+
if (!engine) {
|
|
33076
|
+
engine = new Engine();
|
|
33077
|
+
}
|
|
33078
|
+
return engine.isReady();
|
|
33059
33079
|
}
|
|
33060
33080
|
|
|
33061
33081
|
class BaseReader {
|
|
@@ -33068,10 +33088,18 @@ class BaseReader {
|
|
|
33068
33088
|
this.sort = null;
|
|
33069
33089
|
this.startIndex = null;
|
|
33070
33090
|
this.count = null;
|
|
33091
|
+
this.loadPromise_ = Promise.resolve();
|
|
33092
|
+
this.cacheEnabled = false;
|
|
33093
|
+
}
|
|
33094
|
+
enableCache(enabled) {
|
|
33095
|
+
this.cacheEnabled = enabled;
|
|
33071
33096
|
}
|
|
33072
33097
|
load() {
|
|
33073
33098
|
throw new Error('not implemented');
|
|
33074
33099
|
}
|
|
33100
|
+
get isLoaded() {
|
|
33101
|
+
return this.loadPromise_;
|
|
33102
|
+
}
|
|
33075
33103
|
get properties() {
|
|
33076
33104
|
throw new Error('not implemented');
|
|
33077
33105
|
}
|
|
@@ -33120,178 +33148,237 @@ class BaseReader {
|
|
|
33120
33148
|
}
|
|
33121
33149
|
}
|
|
33122
33150
|
|
|
33123
|
-
|
|
33124
|
-
|
|
33125
|
-
|
|
33126
|
-
|
|
33127
|
-
|
|
33128
|
-
|
|
33129
|
-
|
|
33130
|
-
|
|
33131
|
-
|
|
33132
|
-
|
|
33133
|
-
|
|
33134
|
-
|
|
33135
|
-
|
|
33151
|
+
/**
|
|
33152
|
+
* This reader handles file formats supported natively by DuckDB
|
|
33153
|
+
*/
|
|
33154
|
+
class BaseFileReader extends BaseReader {
|
|
33155
|
+
// a table id should not exceed 63 chars
|
|
33156
|
+
generateDatasetId() {
|
|
33157
|
+
// generate a hash out of the url
|
|
33158
|
+
let hash = 0;
|
|
33159
|
+
for (const char of this.url) {
|
|
33160
|
+
hash = (hash << 5) - hash + char.charCodeAt(0);
|
|
33161
|
+
hash = hash >>> 0; // make it unsigned
|
|
33162
|
+
}
|
|
33163
|
+
return `datafetcher_${hash.toString(16)}`;
|
|
33164
|
+
}
|
|
33165
|
+
async getLoadQuery() {
|
|
33136
33166
|
throw new Error('not implemented');
|
|
33137
33167
|
}
|
|
33138
|
-
load() {
|
|
33139
|
-
this.
|
|
33168
|
+
async load() {
|
|
33169
|
+
this.datasetId = this.generateDatasetId();
|
|
33170
|
+
this.loadPromise_ = getEngine()
|
|
33171
|
+
.then((engine) => {
|
|
33172
|
+
this.engine = engine;
|
|
33173
|
+
return this.getLoadQuery();
|
|
33174
|
+
})
|
|
33175
|
+
.then((loadQuery) => this.engine.loadFile(this.datasetId, loadQuery, !this.cacheEnabled))
|
|
33176
|
+
.then((datasetInfo) => {
|
|
33177
|
+
this.properties_ = datasetInfo.properties;
|
|
33178
|
+
this.geometryColumn = datasetInfo.geometryColumn;
|
|
33179
|
+
this.rowsCount = datasetInfo.rowsCount;
|
|
33180
|
+
// returns void for the loaded promise
|
|
33181
|
+
});
|
|
33140
33182
|
}
|
|
33141
33183
|
get properties() {
|
|
33142
|
-
return this.
|
|
33184
|
+
return this.isLoaded.then(() => this.properties_);
|
|
33143
33185
|
}
|
|
33144
33186
|
get info() {
|
|
33145
|
-
return this.
|
|
33146
|
-
itemsCount:
|
|
33187
|
+
return this.isLoaded.then(() => ({
|
|
33188
|
+
itemsCount: this.rowsCount,
|
|
33189
|
+
hasGeometry: !!this.geometryColumn,
|
|
33147
33190
|
}));
|
|
33148
33191
|
}
|
|
33149
33192
|
async read() {
|
|
33150
|
-
|
|
33151
|
-
//
|
|
33152
|
-
|
|
33153
|
-
|
|
33154
|
-
|
|
33155
|
-
this.sort == null &&
|
|
33156
|
-
this.filter == null &&
|
|
33157
|
-
this.startIndex == null &&
|
|
33158
|
-
this.count == null) {
|
|
33159
|
-
return items;
|
|
33160
|
-
}
|
|
33161
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33162
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33163
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33164
|
-
return result.map(jsonToGeojsonFeature);
|
|
33165
|
-
}
|
|
33166
|
-
}
|
|
33167
|
-
|
|
33168
|
-
function parseCsv(text) {
|
|
33169
|
-
// first parse the header to guess the delimiter
|
|
33170
|
-
// note that we do that to not rely on Papaparse logic for guessing delimiter
|
|
33171
|
-
let delimiter;
|
|
33172
|
-
try {
|
|
33173
|
-
const header = text.split('\n')[0];
|
|
33174
|
-
const result = Papa.parse(header, {
|
|
33175
|
-
header: false,
|
|
33176
|
-
});
|
|
33177
|
-
delimiter = result.meta.delimiter;
|
|
33178
|
-
}
|
|
33179
|
-
catch (e) {
|
|
33180
|
-
throw new Error('CSV parsing failed: the delimiter could not be guessed');
|
|
33181
|
-
}
|
|
33182
|
-
const parsed = Papa.parse(text, {
|
|
33183
|
-
header: true,
|
|
33184
|
-
skipEmptyLines: true,
|
|
33185
|
-
delimiter,
|
|
33186
|
-
});
|
|
33187
|
-
if (parsed.errors.length) {
|
|
33188
|
-
throw new Error('CSV parsing failed for the following reasons:\n' +
|
|
33189
|
-
parsed.errors
|
|
33190
|
-
.map((error) => `* ${error.message} at row ${error.row}, column ${error.index}`)
|
|
33191
|
-
.join('\n'));
|
|
33193
|
+
await this.isLoaded;
|
|
33194
|
+
// if only certain fields are selected, omit the geometry
|
|
33195
|
+
const geometryColumn = this.selected === null ? this.geometryColumn : null;
|
|
33196
|
+
const query = generateSqlQuery(this.datasetId, this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations, geometryColumn);
|
|
33197
|
+
return this.engine.queryItems(query);
|
|
33192
33198
|
}
|
|
33193
|
-
const items = parsed.data.map(jsonToGeojsonFeature);
|
|
33194
|
-
return processItemProperties(items, true);
|
|
33195
33199
|
}
|
|
33200
|
+
|
|
33196
33201
|
class CsvReader extends BaseFileReader {
|
|
33197
|
-
|
|
33198
|
-
|
|
33202
|
+
async getLoadQuery() {
|
|
33203
|
+
// first we get a list of columns hich have a detected type of VARCHAR
|
|
33204
|
+
// then we make sure that for those columns, we don't cast an empty string to null; instead we want to keep the empty string
|
|
33205
|
+
return `
|
|
33206
|
+
SET VARIABLE strColumns = (SELECT list(name) FROM (SELECT unnest(Columns, recursive := true) FROM sniff_csv("${this.url}")) WHERE type = 'VARCHAR');
|
|
33207
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_csv("${this.url}",
|
|
33208
|
+
force_not_null = getvariable('strColumns'),
|
|
33209
|
+
auto_type_candidates = ['NULL', 'BOOLEAN', 'INTEGER', 'DOUBLE', 'DATE', 'VARCHAR']
|
|
33210
|
+
);`;
|
|
33199
33211
|
}
|
|
33200
33212
|
}
|
|
33201
33213
|
|
|
33202
|
-
/**
|
|
33203
|
-
* This parser only supports arrays of simple flat objects with properties
|
|
33204
|
-
* @param text
|
|
33205
|
-
*/
|
|
33206
|
-
function parseJson(text) {
|
|
33207
|
-
const parsed = JSON.parse(text);
|
|
33208
|
-
if (!Array.isArray(parsed)) {
|
|
33209
|
-
throw new Error('Could not parse JSON, expected an array at root level');
|
|
33210
|
-
}
|
|
33211
|
-
return processItemProperties(parsed.map(jsonToGeojsonFeature));
|
|
33212
|
-
}
|
|
33213
33214
|
class JsonReader extends BaseFileReader {
|
|
33214
|
-
|
|
33215
|
-
return
|
|
33215
|
+
async getLoadQuery() {
|
|
33216
|
+
return `
|
|
33217
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_json("${this.url}",
|
|
33218
|
+
maximum_object_size = 536870912 -- 500MB
|
|
33219
|
+
);`;
|
|
33216
33220
|
}
|
|
33217
33221
|
}
|
|
33218
33222
|
|
|
33219
|
-
/**
|
|
33220
|
-
* This parser supports both Geojson Feature collections or arrays
|
|
33221
|
-
* of Features
|
|
33222
|
-
* @param text
|
|
33223
|
-
*/
|
|
33224
|
-
function parseGeojson(text) {
|
|
33225
|
-
const parsed = JSON.parse(text);
|
|
33226
|
-
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33227
|
-
if (!Array.isArray(features)) {
|
|
33228
|
-
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33229
|
-
}
|
|
33230
|
-
return processItemProperties(features);
|
|
33231
|
-
}
|
|
33232
33223
|
class GeojsonReader extends BaseFileReader {
|
|
33233
|
-
|
|
33234
|
-
return
|
|
33224
|
+
async getLoadQuery() {
|
|
33225
|
+
return `
|
|
33226
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33227
|
+
allowed_drivers = ['GeoJSON']
|
|
33228
|
+
);`;
|
|
33235
33229
|
}
|
|
33236
33230
|
}
|
|
33237
33231
|
|
|
33232
|
+
async function inferDatasetType(url, typeHint) {
|
|
33233
|
+
const fileExtensionMatches = new URL(url, typeof window !== 'undefined' ? window.location.toString() : undefined).pathname.match(/\.(.+)$/);
|
|
33234
|
+
const fileExtension = fileExtensionMatches && fileExtensionMatches.length
|
|
33235
|
+
? fileExtensionMatches[1].toLowerCase()
|
|
33236
|
+
: null;
|
|
33237
|
+
// 1. type hint
|
|
33238
|
+
if (typeHint)
|
|
33239
|
+
return Promise.resolve(typeHint);
|
|
33240
|
+
// 2. content-type header
|
|
33241
|
+
const headers = await fetchHeaders(url);
|
|
33242
|
+
if ('supportedType' in headers)
|
|
33243
|
+
return headers.supportedType;
|
|
33244
|
+
// 3. file extension from url
|
|
33245
|
+
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
33246
|
+
return fileExtension;
|
|
33247
|
+
// no type inferred or hinted
|
|
33248
|
+
if ('mimeType' in headers)
|
|
33249
|
+
throw FetchError.unsupportedType(headers.mimeType);
|
|
33250
|
+
else
|
|
33251
|
+
throw FetchError.unknownType();
|
|
33252
|
+
}
|
|
33253
|
+
function fetchHeaders(url) {
|
|
33254
|
+
return sharedFetch(url, 'HEAD')
|
|
33255
|
+
.catch((error) => {
|
|
33256
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33257
|
+
})
|
|
33258
|
+
.then((response) => {
|
|
33259
|
+
if (!response.ok) {
|
|
33260
|
+
throw FetchError.http(response.status);
|
|
33261
|
+
}
|
|
33262
|
+
return parseHeaders(response.headers);
|
|
33263
|
+
});
|
|
33264
|
+
}
|
|
33265
|
+
function fetchDataAsText(url, cacheActive) {
|
|
33266
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33267
|
+
.catch((error) => {
|
|
33268
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33269
|
+
})
|
|
33270
|
+
.then(async (response) => {
|
|
33271
|
+
if (!response.ok) {
|
|
33272
|
+
const clonedResponse = response.clone();
|
|
33273
|
+
throw FetchError.http(response.status, await clonedResponse.text());
|
|
33274
|
+
}
|
|
33275
|
+
const clonedResponse = response.clone();
|
|
33276
|
+
return clonedResponse.text();
|
|
33277
|
+
});
|
|
33278
|
+
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
33279
|
+
}
|
|
33280
|
+
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
33281
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33282
|
+
.catch((error) => {
|
|
33283
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33284
|
+
})
|
|
33285
|
+
.then(async (response) => {
|
|
33286
|
+
if (!response.ok) {
|
|
33287
|
+
throw FetchError.http(response.status, await response.text());
|
|
33288
|
+
}
|
|
33289
|
+
// convert to a numeric array so that we can store the response in cache
|
|
33290
|
+
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
33291
|
+
});
|
|
33292
|
+
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
33293
|
+
return new Uint8Array(array).buffer;
|
|
33294
|
+
});
|
|
33295
|
+
}
|
|
33238
33296
|
/**
|
|
33239
|
-
* This
|
|
33240
|
-
*
|
|
33241
|
-
* @param
|
|
33297
|
+
* This creates a Proxy that allows reading and writing to the data item properties
|
|
33298
|
+
* as if it was a simple array of JSON objects
|
|
33299
|
+
* @param items
|
|
33242
33300
|
*/
|
|
33243
|
-
function
|
|
33244
|
-
return
|
|
33245
|
-
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
|
|
33249
|
-
|
|
33250
|
-
|
|
33251
|
-
|
|
33301
|
+
function getJsonDataItemsProxy(items) {
|
|
33302
|
+
return new Proxy(items, {
|
|
33303
|
+
get(target, p) {
|
|
33304
|
+
if (typeof p === 'string' &&
|
|
33305
|
+
!Number.isNaN(parseInt(p)) &&
|
|
33306
|
+
target[p]?.properties) {
|
|
33307
|
+
return target[p].properties;
|
|
33308
|
+
}
|
|
33309
|
+
return target[p];
|
|
33310
|
+
},
|
|
33311
|
+
set() {
|
|
33312
|
+
throw new Error('This object is read-only');
|
|
33313
|
+
},
|
|
33252
33314
|
});
|
|
33253
33315
|
}
|
|
33316
|
+
|
|
33254
33317
|
class ExcelReader extends BaseFileReader {
|
|
33255
|
-
|
|
33256
|
-
|
|
33318
|
+
async getLoadQuery() {
|
|
33319
|
+
// we download the file as an array buffer first, in order to be able to check if it's an XLS file
|
|
33320
|
+
let buffer = await fetchDataAsArrayBuffer(this.url, this.cacheEnabled);
|
|
33321
|
+
const bufferHandle = `B${this.datasetId}`;
|
|
33322
|
+
// checking against the magic number at the beginning of XLS files, see https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
33323
|
+
const magicNumber = new Uint8Array(buffer, 0, 8); // first 8 bytes
|
|
33324
|
+
const isXls = Array.from(magicNumber)
|
|
33325
|
+
.map((n) => n.toString(16).toUpperCase())
|
|
33326
|
+
.join(' ') === 'D0 CF 11 E0 A1 B1 1A E1';
|
|
33327
|
+
// uh oh, this is an XLS file (not supported by duckdb); convert it to CSV using the xlsx package
|
|
33328
|
+
if (isXls) {
|
|
33329
|
+
buffer = await import('xlsx').then(({ read, utils }) => {
|
|
33330
|
+
const workbook = read(buffer);
|
|
33331
|
+
const json = utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
|
|
33332
|
+
return new TextEncoder().encode(JSON.stringify(json)).buffer;
|
|
33333
|
+
});
|
|
33334
|
+
}
|
|
33335
|
+
const duckDbFn = isXls ? 'read_json' : 'read_xlsx';
|
|
33336
|
+
await this.engine.registerData(bufferHandle, new Uint8Array(buffer));
|
|
33337
|
+
return `
|
|
33338
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM ${duckDbFn}("${bufferHandle}", ignore_errors = true);`;
|
|
33339
|
+
}
|
|
33340
|
+
}
|
|
33341
|
+
|
|
33342
|
+
class GmlReader extends BaseFileReader {
|
|
33343
|
+
async getLoadQuery() {
|
|
33344
|
+
return `
|
|
33345
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33346
|
+
allowed_drivers = ['GML']
|
|
33347
|
+
);`;
|
|
33257
33348
|
}
|
|
33258
33349
|
}
|
|
33259
33350
|
|
|
33260
|
-
|
|
33261
|
-
|
|
33262
|
-
const
|
|
33351
|
+
const formatGeojson = new GeoJSON$1();
|
|
33352
|
+
function parseGeojson(text) {
|
|
33353
|
+
const parsed = JSON.parse(text);
|
|
33354
|
+
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33355
|
+
if (!Array.isArray(features)) {
|
|
33356
|
+
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33357
|
+
}
|
|
33358
|
+
return features;
|
|
33359
|
+
}
|
|
33360
|
+
function parseGml(text, featureType, version) {
|
|
33361
|
+
const parts = featureType.split(':');
|
|
33362
|
+
const regex = new RegExp(`xmlns:${parts[0]}=["']([^'"]*)["']`);
|
|
33263
33363
|
const match = regex.exec(text);
|
|
33264
33364
|
if (match && match.length >= 2) {
|
|
33265
|
-
const
|
|
33365
|
+
const wfs = new WFS({
|
|
33266
33366
|
featureNS: match[1],
|
|
33267
|
-
featureType:
|
|
33367
|
+
featureType: parts[1],
|
|
33268
33368
|
version: version,
|
|
33269
33369
|
});
|
|
33270
33370
|
let features;
|
|
33271
33371
|
try {
|
|
33272
|
-
features =
|
|
33372
|
+
features = wfs.readFeatures(text);
|
|
33273
33373
|
}
|
|
33274
33374
|
catch (e) {
|
|
33275
|
-
throw Error(
|
|
33375
|
+
throw Error(`Couldn't parse WFS with GML features: ${e.message}`);
|
|
33276
33376
|
}
|
|
33277
|
-
const geojsonItem =
|
|
33278
|
-
return
|
|
33377
|
+
const geojsonItem = formatGeojson.writeFeaturesObject(features);
|
|
33378
|
+
return geojsonItem.features;
|
|
33279
33379
|
}
|
|
33280
33380
|
throw Error("Couldn't retrieve namespace url");
|
|
33281
33381
|
}
|
|
33282
|
-
class GmlReader extends BaseFileReader {
|
|
33283
|
-
constructor(url, namespace, version, cacheActive = true) {
|
|
33284
|
-
super(url);
|
|
33285
|
-
this.url = url;
|
|
33286
|
-
this.namespace = namespace;
|
|
33287
|
-
this.version = version;
|
|
33288
|
-
this.cacheActive = cacheActive;
|
|
33289
|
-
}
|
|
33290
|
-
getData() {
|
|
33291
|
-
return fetchDataAsText(this.url, this.cacheActive).then((text) => parseGml(text, this.namespace, this.version));
|
|
33292
|
-
}
|
|
33293
|
-
}
|
|
33294
|
-
|
|
33295
33382
|
async function getWfsEndpoint(wfsUrl) {
|
|
33296
33383
|
try {
|
|
33297
33384
|
return await new WfsEndpoint(wfsUrl).isReady();
|
|
@@ -33319,21 +33406,65 @@ async function getWfsEndpoint(wfsUrl) {
|
|
|
33319
33406
|
}
|
|
33320
33407
|
}
|
|
33321
33408
|
}
|
|
33322
|
-
class WfsReader extends
|
|
33323
|
-
constructor(url,
|
|
33324
|
-
super(url
|
|
33325
|
-
this.endpoint =
|
|
33326
|
-
this.
|
|
33327
|
-
|
|
33409
|
+
class WfsReader extends BaseReader {
|
|
33410
|
+
constructor(url, featureTypeName) {
|
|
33411
|
+
super(url);
|
|
33412
|
+
this.endpoint = getWfsEndpoint(url);
|
|
33413
|
+
this.featureType = this.endpoint
|
|
33414
|
+
.then((endpoint) => {
|
|
33415
|
+
const featureTypes = endpoint.getFeatureTypes();
|
|
33416
|
+
return endpoint.getFeatureTypeFull(featureTypes.length === 1 && !featureTypeName
|
|
33417
|
+
? featureTypes[0].name
|
|
33418
|
+
: featureTypeName);
|
|
33419
|
+
})
|
|
33420
|
+
.then((featureType) => {
|
|
33421
|
+
if (!featureType) {
|
|
33422
|
+
throw new Error('wfs.featuretype.notfound');
|
|
33423
|
+
}
|
|
33424
|
+
return featureType;
|
|
33425
|
+
});
|
|
33426
|
+
}
|
|
33427
|
+
get backupReader() {
|
|
33428
|
+
if (this.backupReader_) {
|
|
33429
|
+
return this.backupReader_;
|
|
33430
|
+
}
|
|
33431
|
+
this.backupReader_ = Promise.all([this.endpoint, this.featureType]).then(([endpoint, featureType]) => {
|
|
33432
|
+
let reader;
|
|
33433
|
+
if (endpoint.supportsJson(featureType.name)) {
|
|
33434
|
+
reader = new GeojsonReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33435
|
+
asJson: true,
|
|
33436
|
+
outputCrs: 'EPSG:4326',
|
|
33437
|
+
}));
|
|
33438
|
+
}
|
|
33439
|
+
else {
|
|
33440
|
+
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33441
|
+
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33442
|
+
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33443
|
+
reader = new GmlReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33444
|
+
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33445
|
+
outputCrs: 'EPSG:4326',
|
|
33446
|
+
}));
|
|
33447
|
+
}
|
|
33448
|
+
}
|
|
33449
|
+
reader.enableCache(this.cacheEnabled);
|
|
33450
|
+
reader.load();
|
|
33451
|
+
return reader;
|
|
33452
|
+
});
|
|
33453
|
+
return this.backupReader_;
|
|
33328
33454
|
}
|
|
33329
33455
|
get properties() {
|
|
33330
|
-
return this.
|
|
33331
|
-
.getFeatureTypeFull(this.featureTypeName)
|
|
33332
|
-
.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33456
|
+
return this.featureType.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33333
33457
|
const originalType = featureType.properties[prop];
|
|
33334
|
-
|
|
33335
|
-
|
|
33336
|
-
|
|
33458
|
+
let type;
|
|
33459
|
+
if (originalType === 'float' || originalType === 'integer') {
|
|
33460
|
+
type = 'number';
|
|
33461
|
+
}
|
|
33462
|
+
else if (originalType === 'boolean') {
|
|
33463
|
+
type = 'string'; // we don't handle booleans yet in the data fetcher
|
|
33464
|
+
}
|
|
33465
|
+
else {
|
|
33466
|
+
type = originalType;
|
|
33467
|
+
}
|
|
33337
33468
|
return {
|
|
33338
33469
|
name: prop,
|
|
33339
33470
|
label: prop,
|
|
@@ -33342,83 +33473,63 @@ class WfsReader extends BaseCacheReader {
|
|
|
33342
33473
|
}));
|
|
33343
33474
|
}
|
|
33344
33475
|
get info() {
|
|
33345
|
-
return this.
|
|
33476
|
+
return this.featureType.then((result) => ({
|
|
33346
33477
|
itemsCount: result.objectCount,
|
|
33478
|
+
hasGeometry: !!result.geometryName,
|
|
33347
33479
|
}));
|
|
33348
33480
|
}
|
|
33349
|
-
|
|
33350
|
-
|
|
33351
|
-
const featureTypes = wfsEndpoint.getFeatureTypes();
|
|
33352
|
-
const featureType = wfsEndpoint.getFeatureTypeSummary(featureTypes.length === 1 && !featureTypeName
|
|
33353
|
-
? featureTypes[0].name
|
|
33354
|
-
: featureTypeName);
|
|
33355
|
-
if (!featureType) {
|
|
33356
|
-
throw new Error('wfs.featuretype.notfound');
|
|
33357
|
-
}
|
|
33358
|
-
if (wfsEndpoint.supportsStartIndex()) {
|
|
33359
|
-
return new WfsReader(wfsUrlEndpoint, wfsEndpoint, featureType.name);
|
|
33360
|
-
}
|
|
33361
|
-
else if (wfsEndpoint.supportsJson(featureType.name)) {
|
|
33362
|
-
return new GeojsonReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33363
|
-
asJson: true,
|
|
33364
|
-
outputCrs: 'EPSG:4326',
|
|
33365
|
-
}));
|
|
33366
|
-
}
|
|
33367
|
-
else {
|
|
33368
|
-
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33369
|
-
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33370
|
-
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33371
|
-
return new GmlReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33372
|
-
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33373
|
-
outputCrs: 'EPSG:4326',
|
|
33374
|
-
}), featureType.name, wfsEndpoint.getVersion());
|
|
33375
|
-
}
|
|
33376
|
-
throw new Error('wfs.geojsongml.notsupported');
|
|
33377
|
-
}
|
|
33481
|
+
load() {
|
|
33482
|
+
// Nothing to load for Wfs
|
|
33378
33483
|
}
|
|
33379
|
-
async
|
|
33380
|
-
|
|
33381
|
-
|
|
33484
|
+
async read() {
|
|
33485
|
+
const endpoint = await this.endpoint;
|
|
33486
|
+
const featureType = await this.featureType;
|
|
33487
|
+
// if we can't use the WFS protocol we fall back to the backup reader
|
|
33488
|
+
if (this.aggregations || this.groupedBy || !endpoint.supportsStartIndex()) {
|
|
33489
|
+
const backupReader = await this.backupReader;
|
|
33490
|
+
backupReader.selectAll();
|
|
33491
|
+
if (this.selected) {
|
|
33492
|
+
backupReader.select(...this.selected);
|
|
33493
|
+
}
|
|
33494
|
+
if (this.aggregations) {
|
|
33495
|
+
backupReader.aggregate(...this.aggregations);
|
|
33496
|
+
}
|
|
33497
|
+
if (this.groupedBy) {
|
|
33498
|
+
backupReader.groupBy(...this.groupedBy);
|
|
33499
|
+
}
|
|
33500
|
+
if (this.sort) {
|
|
33501
|
+
backupReader.orderBy(...this.sort);
|
|
33502
|
+
}
|
|
33503
|
+
if (this.startIndex !== null && this.count !== null) {
|
|
33504
|
+
backupReader.limit(this.startIndex, this.count);
|
|
33505
|
+
}
|
|
33506
|
+
return backupReader.read();
|
|
33382
33507
|
}
|
|
33383
|
-
const asJson =
|
|
33508
|
+
const asJson = endpoint.supportsJson(featureType.name);
|
|
33384
33509
|
const attributes = this.selected ?? undefined;
|
|
33385
|
-
let
|
|
33510
|
+
let sortBy = null;
|
|
33511
|
+
if (this.sort) {
|
|
33512
|
+
const mapSort = (s) => [s[0] === 'desc' ? 'D' : 'A', s[1]];
|
|
33513
|
+
sortBy = Array.isArray(this.sort[0])
|
|
33514
|
+
? this.sort.map(mapSort)
|
|
33515
|
+
: mapSort(this.sort);
|
|
33516
|
+
}
|
|
33517
|
+
const url = endpoint.getFeatureUrl(featureType.name, {
|
|
33386
33518
|
...(this.startIndex !== null && { startIndex: this.startIndex }),
|
|
33387
33519
|
...(this.count !== null && { maxFeatures: this.count }),
|
|
33388
33520
|
asJson,
|
|
33389
33521
|
outputCrs: 'EPSG:4326',
|
|
33390
33522
|
attributes,
|
|
33391
|
-
|
|
33523
|
+
sortBy,
|
|
33392
33524
|
});
|
|
33393
|
-
|
|
33394
|
-
const finalUrl = new URL(url);
|
|
33395
|
-
const sorts = this.sort
|
|
33396
|
-
.map((fieldSort) => `${fieldSort[1]}+${fieldSort[0] === 'asc' ? 'A' : 'D'}`)
|
|
33397
|
-
.join(',');
|
|
33398
|
-
// Direct update on string url to prevent encoding of +A and +D
|
|
33399
|
-
url = `${url}${finalUrl.search ? '&' : ''}SORTBY=${sorts}`;
|
|
33400
|
-
}
|
|
33401
|
-
return fetchDataAsText(url, this.cacheActive).then((text) => asJson
|
|
33525
|
+
return fetchDataAsText(url, this.cacheEnabled).then((text) => asJson
|
|
33402
33526
|
? parseGeojson(text)
|
|
33403
|
-
: parseGml(text,
|
|
33404
|
-
}
|
|
33405
|
-
async getQueryData() {
|
|
33406
|
-
const items = (await this.getData()).items;
|
|
33407
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33408
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33409
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33410
|
-
return result.map(jsonToGeojsonFeature);
|
|
33411
|
-
}
|
|
33412
|
-
load() {
|
|
33413
|
-
// Nothing to load for Wfs
|
|
33414
|
-
}
|
|
33415
|
-
async read() {
|
|
33416
|
-
return (await this.getData(this.aggregations, this.groupedBy)).items;
|
|
33527
|
+
: parseGml(text, featureType.name, endpoint.getVersion()));
|
|
33417
33528
|
}
|
|
33418
33529
|
}
|
|
33419
33530
|
|
|
33420
|
-
async function openDataset(url,
|
|
33421
|
-
const fileType = await inferDatasetType(url, typeHint);
|
|
33531
|
+
async function openDataset(url, options) {
|
|
33532
|
+
const fileType = await inferDatasetType(url, options?.typeHint);
|
|
33422
33533
|
let reader;
|
|
33423
33534
|
try {
|
|
33424
33535
|
switch (fileType) {
|
|
@@ -33435,18 +33546,18 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33435
33546
|
reader = new ExcelReader(url);
|
|
33436
33547
|
break;
|
|
33437
33548
|
case 'gml':
|
|
33438
|
-
reader = new GmlReader(url
|
|
33549
|
+
reader = new GmlReader(url);
|
|
33439
33550
|
break;
|
|
33440
33551
|
case 'wfs':
|
|
33441
|
-
reader =
|
|
33552
|
+
reader = new WfsReader(url, options?.wfsFeatureType);
|
|
33442
33553
|
break;
|
|
33443
33554
|
}
|
|
33444
|
-
reader.
|
|
33555
|
+
reader.enableCache(options?.enableCache ?? true);
|
|
33445
33556
|
reader.load();
|
|
33446
33557
|
return reader;
|
|
33447
33558
|
}
|
|
33448
33559
|
catch (e) {
|
|
33449
|
-
//WfsReader may already raise a FetchError
|
|
33560
|
+
// WfsReader may already raise a FetchError
|
|
33450
33561
|
if (e instanceof FetchError)
|
|
33451
33562
|
throw e;
|
|
33452
33563
|
else
|
|
@@ -33457,13 +33568,13 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33457
33568
|
* This fetches the full dataset at the given URL and parses it according to its mime type.
|
|
33458
33569
|
* All items in the dataset are converted to GeoJSON features, even if they do not bear any spatial geometry.
|
|
33459
33570
|
* File type can be either inferred (from the HTTP headers or the URL), or hinted using the 2nd argument
|
|
33460
|
-
* File type is determined
|
|
33571
|
+
* File type is determined like so:
|
|
33461
33572
|
* 1. if a type hint is given, use it
|
|
33462
33573
|
* 2. otherwise, look for a Content-Type header in the response with a supported mime type
|
|
33463
33574
|
* 3. if no valid mime type was found, look for an explicit file extension in the url (.csv, .geojson etc.)
|
|
33464
33575
|
*/
|
|
33465
|
-
async function readDataset(url,
|
|
33466
|
-
const reader = await openDataset(url,
|
|
33576
|
+
async function readDataset(url, options) {
|
|
33577
|
+
const reader = await openDataset(url, options);
|
|
33467
33578
|
try {
|
|
33468
33579
|
return await reader.read();
|
|
33469
33580
|
}
|
|
@@ -33694,9 +33805,11 @@ class DataService {
|
|
|
33694
33805
|
getDataset(link, cacheActive) {
|
|
33695
33806
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
33696
33807
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString());
|
|
33697
|
-
return from(openDataset(wfsUrlEndpoint,
|
|
33808
|
+
return from(openDataset(wfsUrlEndpoint, {
|
|
33809
|
+
typeHint: 'wfs',
|
|
33698
33810
|
wfsFeatureType: link.name,
|
|
33699
|
-
|
|
33811
|
+
enableCache: cacheActive,
|
|
33812
|
+
}));
|
|
33700
33813
|
}
|
|
33701
33814
|
else if (link.type === 'download') {
|
|
33702
33815
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString());
|
|
@@ -33704,12 +33817,15 @@ class DataService {
|
|
|
33704
33817
|
const supportedType = SupportedTypes.indexOf(format) > -1
|
|
33705
33818
|
? format
|
|
33706
33819
|
: undefined;
|
|
33707
|
-
return from(openDataset(linkProxifiedUrl,
|
|
33820
|
+
return from(openDataset(linkProxifiedUrl, {
|
|
33821
|
+
typeHint: supportedType,
|
|
33822
|
+
enableCache: cacheActive,
|
|
33823
|
+
})).pipe();
|
|
33708
33824
|
}
|
|
33709
33825
|
else if (link.type === 'service' &&
|
|
33710
33826
|
link.accessServiceProtocol === 'esriRest') {
|
|
33711
33827
|
const url = this.getDownloadUrlFromEsriRest(link.url.toString(), 'geojson');
|
|
33712
|
-
return from(openDataset(url, 'geojson',
|
|
33828
|
+
return from(openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })).pipe();
|
|
33713
33829
|
}
|
|
33714
33830
|
else if (link.type === 'service' &&
|
|
33715
33831
|
link.accessServiceProtocol === 'ogcFeatures') {
|
|
@@ -33723,7 +33839,10 @@ class DataService {
|
|
|
33723
33839
|
}
|
|
33724
33840
|
const urlWithoutLimit = new URL(geojsonUrl);
|
|
33725
33841
|
urlWithoutLimit.searchParams.delete('limit');
|
|
33726
|
-
return openDataset(urlWithoutLimit.toString(),
|
|
33842
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
33843
|
+
typeHint: 'geojson',
|
|
33844
|
+
enableCache: cacheActive,
|
|
33845
|
+
});
|
|
33727
33846
|
}));
|
|
33728
33847
|
}
|
|
33729
33848
|
return throwError(() => 'protocol not supported');
|
|
@@ -33975,20 +34094,31 @@ class DataTableComponent {
|
|
|
33975
34094
|
this.eltRef = inject(ElementRef);
|
|
33976
34095
|
this.cdr = inject(ChangeDetectorRef);
|
|
33977
34096
|
this.translateService = inject(TranslateService);
|
|
33978
|
-
this.
|
|
34097
|
+
this.columnsFromFeatureCatalog = null;
|
|
34098
|
+
this.columnsFromDataset = [];
|
|
33979
34099
|
this.selected = new EventEmitter();
|
|
33980
|
-
this.properties$ = new BehaviorSubject(null);
|
|
33981
34100
|
this.loading$ = new BehaviorSubject(false);
|
|
33982
34101
|
this.error = null;
|
|
33983
34102
|
}
|
|
33984
34103
|
set featureAttributes(value) {
|
|
33985
|
-
this.
|
|
33986
|
-
|
|
34104
|
+
this.columnsFromFeatureCatalog = value.map((attrs) => ({
|
|
34105
|
+
name: attrs.value,
|
|
34106
|
+
label: attrs.label,
|
|
34107
|
+
}));
|
|
33987
34108
|
}
|
|
33988
34109
|
set dataset(value) {
|
|
33989
34110
|
this.dataset_ = value;
|
|
33990
34111
|
this.dataset_.load();
|
|
33991
|
-
this.dataset_.info.then((info) =>
|
|
34112
|
+
this.dataset_.info.then((info) => {
|
|
34113
|
+
this.count = info.itemsCount;
|
|
34114
|
+
this.cdr.detectChanges();
|
|
34115
|
+
});
|
|
34116
|
+
}
|
|
34117
|
+
get columns() {
|
|
34118
|
+
return this.columnsFromFeatureCatalog ?? this.columnsFromDataset;
|
|
34119
|
+
}
|
|
34120
|
+
get columnNames() {
|
|
34121
|
+
return this.columns.map((c) => c.name);
|
|
33992
34122
|
}
|
|
33993
34123
|
ngOnInit() {
|
|
33994
34124
|
this.dataSource = new DataTableDataSource();
|
|
@@ -34023,11 +34153,12 @@ class DataTableComponent {
|
|
|
34023
34153
|
}
|
|
34024
34154
|
async readData() {
|
|
34025
34155
|
this.loading$.next(true);
|
|
34026
|
-
// wait for properties to be read
|
|
34027
|
-
const properties = await firstValueFrom(this.properties$.pipe(filter((p) => !!p)));
|
|
34028
|
-
const propsWithoutGeom = properties.filter((p) => !p.toLowerCase().startsWith('geom'));
|
|
34029
|
-
this.dataset_.select(...propsWithoutGeom);
|
|
34030
34156
|
try {
|
|
34157
|
+
// wait for properties to be read
|
|
34158
|
+
if (!this.columnsFromFeatureCatalog) {
|
|
34159
|
+
this.columnsFromDataset = await this.dataset_.properties;
|
|
34160
|
+
}
|
|
34161
|
+
this.dataset_.select(...this.columnNames);
|
|
34031
34162
|
await this.dataSource.showData(this.dataset_.read());
|
|
34032
34163
|
this.error = null;
|
|
34033
34164
|
}
|
|
@@ -34057,7 +34188,7 @@ class DataTableComponent {
|
|
|
34057
34188
|
}
|
|
34058
34189
|
}
|
|
34059
34190
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
34060
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DataTableComponent, isStandalone: true, selector: "gn-ui-data-table", inputs: { featureAttributes: "featureAttributes", dataset: "dataset", activeId: "activeId" }, outputs: { selected: "selected" }, providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n
|
|
34191
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DataTableComponent, isStandalone: true, selector: "gn-ui-data-table", inputs: { featureAttributes: "featureAttributes", dataset: "dataset", activeId: "activeId" }, outputs: { selected: "selected" }, providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n >\n @for (column of columns; track column) {\n <ng-container [matColumnDef]=\"column.name\">\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n class=\"text-sm text-black bg-white\"\n >\n {{ column.label }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n class=\"whitespace-nowrap pr-1 truncate\"\n >\n {{ element[column.name] }}\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"columnNames; sticky: true\"></tr>\n <tr\n [id]=\"getRowEltId(row.id)\"\n mat-row\n *matRowDef=\"let row; columns: columnNames\"\n (click)=\"selected.emit(row)\"\n [class.active]=\"\n activeId !== undefined && activeId !== null && row.id === activeId\n \"\n ></tr>\n </table>\n @if (loading$ | async) {\n <gn-ui-loading-mask\n class=\"sticky inset-0\"\n [message]=\"'table.loading.data' | translate\"\n ></gn-ui-loading-mask>\n }\n @if (error) {\n <gn-ui-popup-alert\n type=\"warning\"\n icon=\"matErrorOutlineOutline\"\n class=\"absolute m-2 inset-0 z-[100]\"\n >\n <span translate>{{ error }}</span>\n </gn-ui-popup-alert>\n }\n </div>\n <div class=\"flex justify-between items-center overflow-hidden\">\n <div class=\"text-gray-900 px-4 py-2 text-sm\">\n <span class=\"count font-extrabold text-primary\">{{ count }}</span\n > <span translate>table.object.count</span>.\n </div>\n\n <mat-paginator\n class=\"my-[-16px]\"\n (page)=\"setPagination()\"\n [length]=\"count\"\n [pageSize]=\"10\"\n [showFirstLastButtons]=\"true\"\n [hidePageSize]=\"true\"\n ></mat-paginator>\n </div>\n</div>\n", styles: ["table{width:100%;background:#fff}th.mat-mdc-header-cell,td.mat-mdc-cell,td.mat-mdc-footer-cell{padding-right:20px}tr.mat-mdc-row,tr.mat-mdc-footer-row{height:36px}tr:hover{background:#f5f5f5}tr.mat-mdc-header-row{height:48px}[mat-header-cell]{color:#0000008a;font-size:12px;font-weight:500}tr{cursor:pointer}.active .mat-mdc-cell{color:var(--color-primary)}.mat-mdc-paginator{background:none}\n"], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$b.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$b.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$b.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$b.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$b.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$b.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$b.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$b.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i1$b.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$b.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2$4.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2$4.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: LoadingMaskComponent, selector: "gn-ui-loading-mask", inputs: ["message"] }, { kind: "component", type: PopupAlertComponent, selector: "gn-ui-popup-alert", inputs: ["icon", "type", "position"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
34061
34192
|
}
|
|
34062
34193
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, decorators: [{
|
|
34063
34194
|
type: Component,
|
|
@@ -34069,10 +34200,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
34069
34200
|
CommonModule,
|
|
34070
34201
|
LoadingMaskComponent,
|
|
34071
34202
|
PopupAlertComponent,
|
|
34072
|
-
LetDirective,
|
|
34073
34203
|
TranslatePipe,
|
|
34074
34204
|
TranslateDirective,
|
|
34075
|
-
], providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], selector: 'gn-ui-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n
|
|
34205
|
+
], providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], selector: 'gn-ui-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n >\n @for (column of columns; track column) {\n <ng-container [matColumnDef]=\"column.name\">\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n class=\"text-sm text-black bg-white\"\n >\n {{ column.label }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n class=\"whitespace-nowrap pr-1 truncate\"\n >\n {{ element[column.name] }}\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"columnNames; sticky: true\"></tr>\n <tr\n [id]=\"getRowEltId(row.id)\"\n mat-row\n *matRowDef=\"let row; columns: columnNames\"\n (click)=\"selected.emit(row)\"\n [class.active]=\"\n activeId !== undefined && activeId !== null && row.id === activeId\n \"\n ></tr>\n </table>\n @if (loading$ | async) {\n <gn-ui-loading-mask\n class=\"sticky inset-0\"\n [message]=\"'table.loading.data' | translate\"\n ></gn-ui-loading-mask>\n }\n @if (error) {\n <gn-ui-popup-alert\n type=\"warning\"\n icon=\"matErrorOutlineOutline\"\n class=\"absolute m-2 inset-0 z-[100]\"\n >\n <span translate>{{ error }}</span>\n </gn-ui-popup-alert>\n }\n </div>\n <div class=\"flex justify-between items-center overflow-hidden\">\n <div class=\"text-gray-900 px-4 py-2 text-sm\">\n <span class=\"count font-extrabold text-primary\">{{ count }}</span\n > <span translate>table.object.count</span>.\n </div>\n\n <mat-paginator\n class=\"my-[-16px]\"\n (page)=\"setPagination()\"\n [length]=\"count\"\n [pageSize]=\"10\"\n [showFirstLastButtons]=\"true\"\n [hidePageSize]=\"true\"\n ></mat-paginator>\n </div>\n</div>\n", styles: ["table{width:100%;background:#fff}th.mat-mdc-header-cell,td.mat-mdc-cell,td.mat-mdc-footer-cell{padding-right:20px}tr.mat-mdc-row,tr.mat-mdc-footer-row{height:36px}tr:hover{background:#f5f5f5}tr.mat-mdc-header-row{height:48px}[mat-header-cell]{color:#0000008a;font-size:12px;font-weight:500}tr{cursor:pointer}.active .mat-mdc-cell{color:var(--color-primary)}.mat-mdc-paginator{background:none}\n"] }]
|
|
34076
34206
|
}], propDecorators: { featureAttributes: [{
|
|
34077
34207
|
type: Input
|
|
34078
34208
|
}], dataset: [{
|
|
@@ -34175,7 +34305,7 @@ class ChartViewComponent {
|
|
|
34175
34305
|
}), shareReplay$1(1));
|
|
34176
34306
|
this.properties$ = combineLatest([this.dataset$, this.featureCatalog$]).pipe(switchMap$1(([dataset, catalog]) => this.setProperties(dataset, catalog)), shareReplay$1(1));
|
|
34177
34307
|
this.yChoices$ = this.properties$.pipe(map$1((properties) => properties
|
|
34178
|
-
.filter((prop) => prop.type === 'number'
|
|
34308
|
+
.filter((prop) => prop.type === 'number')
|
|
34179
34309
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))), tap$1((choices) => {
|
|
34180
34310
|
if (!choices.find((choice) => choice.value === this.yProperty$.value)) {
|
|
34181
34311
|
const newProp = choices[0]?.value || '';
|
|
@@ -34202,6 +34332,7 @@ class ChartViewComponent {
|
|
|
34202
34332
|
this.aggregation$,
|
|
34203
34333
|
]).pipe(filter$1(([_, x, y]) => !!x || !!y), switchMap$1(([dataset, xProp, yProp, aggregation]) => {
|
|
34204
34334
|
const fieldAgg = aggregation === 'count' ? ['count'] : [aggregation, yProp];
|
|
34335
|
+
this.loading = true;
|
|
34205
34336
|
return dataset
|
|
34206
34337
|
.groupBy(['distinct', xProp])
|
|
34207
34338
|
.aggregate(fieldAgg)
|
|
@@ -34443,6 +34574,7 @@ class GeoTableViewComponent {
|
|
|
34443
34574
|
}));
|
|
34444
34575
|
}
|
|
34445
34576
|
async initMapContext() {
|
|
34577
|
+
this.dataset.load();
|
|
34446
34578
|
this.dataset.selectAll();
|
|
34447
34579
|
return {
|
|
34448
34580
|
layers: [
|
|
@@ -34616,8 +34748,7 @@ class MdViewFacade {
|
|
|
34616
34748
|
}));
|
|
34617
34749
|
this.error$ = this.store.pipe(select(getMetadataError));
|
|
34618
34750
|
this.related$ = this.store.pipe(select(getRelated));
|
|
34619
|
-
this.
|
|
34620
|
-
this.sourceOf$ = this.store.pipe(select(getSourceOf));
|
|
34751
|
+
this.linkedRecords$ = this.store.pipe(select(getLinkedRecords));
|
|
34621
34752
|
this.chartConfig$ = this.store.pipe(select(getChartConfig));
|
|
34622
34753
|
this.allLinks$ = this.metadata$.pipe(map$1((record) => 'onlineResources' in record ? record.onlineResources : []), shareReplay$1(1));
|
|
34623
34754
|
this.resourceDoi$ = this.metadata$.pipe(map$1((record) => {
|
|
@@ -34736,12 +34867,9 @@ class MdViewEffects {
|
|
|
34736
34867
|
this.loadRelatedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSimilarRecords(full)), map$1((related) => {
|
|
34737
34868
|
return setRelated({ related });
|
|
34738
34869
|
}), catchError(() => of(setRelated({ related: null })))));
|
|
34739
|
-
this.
|
|
34740
|
-
return
|
|
34741
|
-
}), catchError(() => of(
|
|
34742
|
-
this.loadSourceOf$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSourceOf(full)), map$1((sourceOf) => {
|
|
34743
|
-
return setSourceOf({ sourceOf });
|
|
34744
|
-
}), catchError(() => of(setSourceOf({ sourceOf: null })))));
|
|
34870
|
+
this.loadLinkedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getLinkedRecords(full)), map$1((linkedRecords) => {
|
|
34871
|
+
return setLinkedRecords({ linkedRecords });
|
|
34872
|
+
}), catchError(() => of(setLinkedRecords({ linkedRecords: null })))));
|
|
34745
34873
|
/*
|
|
34746
34874
|
UserFeedback effects
|
|
34747
34875
|
*/
|
|
@@ -40377,5 +40505,5 @@ const CHART_TYPE_VALUES = [
|
|
|
40377
40505
|
* Generated bundle index. Do not edit.
|
|
40378
40506
|
*/
|
|
40379
40507
|
|
|
40380
|
-
export { ADD_RESULTS, ADD_SEARCH, AVAILABLE_LICENSES, AbstractAction, AbstractSearchField, ActionMenuComponent, AddLayerFromCatalogComponent, AddLayerRecordPreviewComponent, AddResults, AddSearch, AnchorLinkDirective, ApiCardComponent, ApplicationBannerComponent, AuthService, AutocompleteComponent, AvailableServicesField, AvatarComponent, AvatarServiceInterface, BASEMAP_LAYERS, BadgeComponent, BaseConverter, BaseFileReader, BaseReader, BlockListComponent, ButtonComponent, CHART_TYPE_VALUES, CLEAR_ERROR, CLEAR_RESULTS, CarouselComponent, CatalogTitleComponent, CellPopinComponent, ChartComponent, ChartViewComponent, CheckToggleComponent, CheckboxComponent, ClearError, ClearResults, ColorScaleComponent, ConfirmationDialogComponent, ContactDetailsComponent, ContactDetailsFormComponent, ContactPillComponent, ContentGhostComponent, CopyTextButtonComponent, DEFAULT_CONFIGURATION, DEFAULT_GN4_LOGIN_URL, DEFAULT_GN4_LOGOUT_URL, DEFAULT_GN4_SETTINGS_URL, DEFAULT_LANG, DEFAULT_PAGE_SIZE, DEFAULT_RECORD_CONVERTER, DEFAULT_RESULTS_LAYOUT_CONFIG, DEFAULT_SEARCH_KEY, DEFAULT_SPATIAL_EXTENT_STYLE, DISABLE_AUTH, DISABLE_DRAFT, DO_NOT_USE_DEFAULT_BASEMAP, DataService, DataTableComponent, DataViewComponent, DataViewPermalinkComponent, DataViewShareComponent, DataViewWebComponentComponent, DatePickerComponent, DateRangeDropdownComponent, DateRangeInputsComponent, DateRangePickerComponent, DateRangeSearchField, DateService, DcatApConverter, DefaultRouterModule, DownloadItemComponent, DownloadsListComponent, DragAndDropFileInputComponent, DropdownMultiselectComponent, DropdownSelectorComponent, EDITOR_FEATURE_KEY, ES_QUERY_FIELDS_PRIORITY, ES_RESOURCES_VALUES, ES_SOURCE_SUMMARY, EXTERNAL_VIEWER_OPEN_NEW_TAB, EXTERNAL_VIEWER_URL_TEMPLATE, EditableLabelDirective, EditorFacade, EditorService, ElasticsearchService, ErrorComponent, ErrorType, ExpandablePanelButtonComponent, ExpandablePanelComponent, ExternalLinkCardComponent, ExternalViewerButtonComponent, FIELDS_BRIEF, FIELDS_SUMMARY, FILTER_GEOMETRY, FILTER_SUMMARY_IGNORE_LIST, FORMATS, FacetBlockComponent, FacetItemComponent, FacetListComponent, FacetsContainerComponent, FavoriteStarComponent, FavoritesService, FeatureCatalogListComponent, FeatureDetailComponent, FeatureEditorModule, FeatureMapModule, FeatureRecordModule, FeatureSearchModule, FetchError, FieldFocusDirective, FieldsService, FigureComponent, FigureContainerComponent, FileInputComponent, FileTranslateLoader, FilesDropDirective, FilterDropdownComponent, FormFieldArrayComponent, FormFieldComponent, FormFieldDateComponent, FormFieldFileComponent, FormFieldKeywordsComponent, FormFieldLicenseComponent, FormFieldObjectComponent, FormFieldRichComponent, FormFieldSimpleComponent, FormFieldSpatialExtentComponent, FormFieldTemporalExtentsComponent, FormFieldTopicsComponent, FormFieldWrapperComponent, FullTextSearchField, FuzzySearchComponent, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingComponent, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, MapStyleService, MapUtilsService, MapViewComponent, MarkdownEditorComponent, MarkdownParserComponent, MaxLinesComponent, mdview_actions as MdViewActions, MdViewFacade, MetadataCatalogComponent, MetadataContactComponent, MetadataDoiComponent, MetadataInfoComponent, MetadataLinkType, MetadataMapperContext, MetadataQualityComponent, MetadataQualityItemComponent, MetadataQualityPanelComponent, ModalDialogComponent, MultilingualPanelComponent, MultilingualSearchField, MyOrgService, NAMESPACES, NOT_APPLICABLE_CONSTRAINT, NOT_KNOWN_CONSTRAINT, NotificationComponent, NotificationsContainerComponent, NotificationsService, OPEN_DATA_LICENSE, ORGANIZATIONS_STRATEGY, ORGANIZATION_PAGE_URL_TOKEN, ORGANIZATION_URL_TOKEN, OnlineResourceCardComponent, OnlineServiceResourceInputComponent, OrganisationPreviewComponent, OrganisationsComponent, OrganisationsFilterComponent, OrganisationsResultComponent, OrganizationSearchField, OrganizationsFromGroupsService, OrganizationsFromMetadataService, OrganizationsServiceInterface, OwnerSearchField, PAGINATE, PARSE_DELIMITER, PATCH_RESULTS_AGGREGATIONS, PROXY_PATH, Paginate, PaginationButtonsComponent, PaginationComponent, PaginationDotsComponent, PatchResultsAggregations, PlatformServiceInterface, PopoverComponent, PopupAlertComponent, PossibleResourceTypes, PossibleResourceTypesDefinition, PreviousNextButtonsComponent, ProgressBarComponent, ProxyService, QUERY_FIELDS, RECORD_DATASET_URL_TOKEN, RECORD_REUSE_URL_TOKEN, RECORD_SERVICE_URL_TOKEN, REQUEST_MORE_ON_AGGREGATION, REQUEST_MORE_RESULTS, REQUEST_NEW_RESULTS, RESULTS_LAYOUT_CONFIG, REUSE_LIGHT_CONFIGURATION, ROUTER_CONFIG, ROUTER_ROUTE_DATASET, ROUTER_ROUTE_ORGANIZATION, ROUTER_ROUTE_REUSE, ROUTER_ROUTE_SEARCH, ROUTER_ROUTE_SERVICE, ROUTER_STATE_KEY, ROUTE_PARAMS, RecordApiFormComponent, RecordFormComponent, RecordKindField, RecordMetaComponent, RecordMetricComponent, RecordPreviewCardComponent, RecordPreviewComponent, RecordPreviewFeedComponent, RecordPreviewListComponent, RecordPreviewRowComponent, RecordPreviewTextComponent, RecordPreviewTitleComponent, RecordStatusValues, RecordsMetricsComponent, RecordsRepositoryInterface, RecordsService, RequestMoreOnAggregation, RequestMoreResults, RequestNewResults, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, defaultMapStyleFixture, defaultMapStyleHlFixture, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isConfigLoaded, isDateRange, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
|
|
40508
|
+
export { ADD_RESULTS, ADD_SEARCH, AVAILABLE_LICENSES, AbstractAction, AbstractSearchField, ActionMenuComponent, AddLayerFromCatalogComponent, AddLayerRecordPreviewComponent, AddResults, AddSearch, AnchorLinkDirective, ApiCardComponent, ApplicationBannerComponent, AuthService, AutocompleteComponent, AvailableServicesField, AvatarComponent, AvatarServiceInterface, BASEMAP_LAYERS, BadgeComponent, BaseConverter, BaseFileReader, BaseReader, BlockListComponent, ButtonComponent, CHART_TYPE_VALUES, CLEAR_ERROR, CLEAR_RESULTS, CarouselComponent, CatalogTitleComponent, CellPopinComponent, ChartComponent, ChartViewComponent, CheckToggleComponent, CheckboxComponent, ClearError, ClearResults, ColorScaleComponent, ConfirmationDialogComponent, ContactDetailsComponent, ContactDetailsFormComponent, ContactPillComponent, ContentGhostComponent, CopyTextButtonComponent, DEFAULT_BASEMAP_LAYER, DEFAULT_CONFIGURATION, DEFAULT_GN4_LOGIN_URL, DEFAULT_GN4_LOGOUT_URL, DEFAULT_GN4_SETTINGS_URL, DEFAULT_LANG, DEFAULT_PAGE_SIZE, DEFAULT_RECORD_CONVERTER, DEFAULT_RESULTS_LAYOUT_CONFIG, DEFAULT_SEARCH_KEY, DEFAULT_SPATIAL_EXTENT_STYLE, DISABLE_AUTH, DISABLE_DRAFT, DO_NOT_USE_DEFAULT_BASEMAP, DataService, DataTableComponent, DataViewComponent, DataViewPermalinkComponent, DataViewShareComponent, DataViewWebComponentComponent, DatePickerComponent, DateRangeDropdownComponent, DateRangeInputsComponent, DateRangePickerComponent, DateRangeSearchField, DateService, DcatApConverter, DefaultRouterModule, DownloadItemComponent, DownloadsListComponent, DragAndDropFileInputComponent, DropdownMultiselectComponent, DropdownSelectorComponent, EDITOR_FEATURE_KEY, ES_QUERY_FIELDS_PRIORITY, ES_RESOURCES_VALUES, ES_SOURCE_SUMMARY, EXTERNAL_VIEWER_OPEN_NEW_TAB, EXTERNAL_VIEWER_URL_TEMPLATE, EditableLabelDirective, EditorFacade, EditorService, ElasticsearchService, ErrorComponent, ErrorType, ExpandablePanelButtonComponent, ExpandablePanelComponent, ExternalLinkCardComponent, ExternalViewerButtonComponent, FIELDS_BRIEF, FIELDS_SUMMARY, FILTER_GEOMETRY, FILTER_SUMMARY_IGNORE_LIST, FORMATS, FacetBlockComponent, FacetItemComponent, FacetListComponent, FacetsContainerComponent, FavoriteStarComponent, FavoritesService, FeatureCatalogListComponent, FeatureDetailComponent, FeatureEditorModule, FeatureMapModule, FeatureRecordModule, FeatureSearchModule, FetchError, FieldFocusDirective, FieldsService, FigureComponent, FigureContainerComponent, FileInputComponent, FileTranslateLoader, FilesDropDirective, FilterDropdownComponent, FormFieldArrayComponent, FormFieldComponent, FormFieldDateComponent, FormFieldFileComponent, FormFieldKeywordsComponent, FormFieldLicenseComponent, FormFieldObjectComponent, FormFieldRichComponent, FormFieldSimpleComponent, FormFieldSpatialExtentComponent, FormFieldTemporalExtentsComponent, FormFieldTopicsComponent, FormFieldWrapperComponent, FullTextSearchField, FuzzySearchComponent, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingComponent, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, MapStyleService, MapUtilsService, MapViewComponent, MarkdownEditorComponent, MarkdownParserComponent, MaxLinesComponent, mdview_actions as MdViewActions, MdViewFacade, MetadataCatalogComponent, MetadataContactComponent, MetadataDoiComponent, MetadataInfoComponent, MetadataLinkType, MetadataMapperContext, MetadataQualityComponent, MetadataQualityItemComponent, MetadataQualityPanelComponent, ModalDialogComponent, MultilingualPanelComponent, MultilingualSearchField, MyOrgService, NAMESPACES, NOT_APPLICABLE_CONSTRAINT, NOT_KNOWN_CONSTRAINT, NotificationComponent, NotificationsContainerComponent, NotificationsService, OPEN_DATA_LICENSE, ORGANIZATIONS_STRATEGY, ORGANIZATION_PAGE_URL_TOKEN, ORGANIZATION_URL_TOKEN, OnlineResourceCardComponent, OnlineServiceResourceInputComponent, OrganisationPreviewComponent, OrganisationsComponent, OrganisationsFilterComponent, OrganisationsResultComponent, OrganizationSearchField, OrganizationsFromGroupsService, OrganizationsFromMetadataService, OrganizationsServiceInterface, OwnerSearchField, PAGINATE, PARSE_DELIMITER, PATCH_RESULTS_AGGREGATIONS, PROXY_PATH, Paginate, PaginationButtonsComponent, PaginationComponent, PaginationDotsComponent, PatchResultsAggregations, PlatformServiceInterface, PopoverComponent, PopupAlertComponent, PossibleResourceTypes, PossibleResourceTypesDefinition, PreviousNextButtonsComponent, ProgressBarComponent, ProxyService, QUERY_FIELDS, RECORD_DATASET_URL_TOKEN, RECORD_REUSE_URL_TOKEN, RECORD_SERVICE_URL_TOKEN, REQUEST_MORE_ON_AGGREGATION, REQUEST_MORE_RESULTS, REQUEST_NEW_RESULTS, RESULTS_LAYOUT_CONFIG, REUSE_LIGHT_CONFIGURATION, ROUTER_CONFIG, ROUTER_ROUTE_DATASET, ROUTER_ROUTE_ORGANIZATION, ROUTER_ROUTE_REUSE, ROUTER_ROUTE_SEARCH, ROUTER_ROUTE_SERVICE, ROUTER_STATE_KEY, ROUTE_PARAMS, RecordApiFormComponent, RecordFormComponent, RecordKindField, RecordMetaComponent, RecordMetricComponent, RecordPreviewCardComponent, RecordPreviewComponent, RecordPreviewFeedComponent, RecordPreviewListComponent, RecordPreviewRowComponent, RecordPreviewTextComponent, RecordPreviewTitleComponent, RecordStatusValues, RecordsMetricsComponent, RecordsRepositoryInterface, RecordsService, RequestMoreOnAggregation, RequestMoreResults, RequestNewResults, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, defaultMapStyleFixture, defaultMapStyleHlFixture, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isConfigLoaded, isDateRange, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
|
|
40381
40509
|
//# sourceMappingURL=geonetwork-ui.mjs.map
|