geonetwork-ui 2.11.0-dev.96d0c6ce2 → 2.11.0-dev.a5c259d9e
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 +971 -585
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +165 -59
- 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/metadata-converter/src/lib/gn4/types/elasticsearch.model.ts +13 -1
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +23 -3
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +40 -16
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +12 -0
- package/src/libs/common/domain/src/lib/model/search/sort-by.model.ts +1 -5
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -2
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
- package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +22 -22
- package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
- package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
- package/src/libs/feature/record/src/lib/state/mdview.actions.ts +4 -8
- package/src/libs/feature/record/src/lib/state/mdview.effects.ts +7 -16
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -3
- package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +4 -9
- package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +2 -7
- package/src/libs/feature/router/src/lib/default/router.service.ts +1 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +7 -0
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +39 -0
- package/src/libs/feature/search/src/lib/sort-by/sort-by.component.ts +1 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +3 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +16 -1
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
- package/src/libs/ui/inputs/src/index.ts +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +117 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +191 -0
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +4 -5
- package/src/libs/util/app-config/src/lib/app-config.ts +3 -0
- package/src/libs/util/app-config/src/lib/model.ts +1 -0
- package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +19 -22
- package/src/libs/util/data-fetcher/src/lib/engine/duckdb.ts +185 -0
- package/src/libs/util/data-fetcher/src/lib/engine/results.ts +63 -0
- package/src/libs/util/data-fetcher/src/lib/{sql-utils.ts → engine/sql-utils.ts} +28 -13
- package/src/libs/util/data-fetcher/src/lib/model.ts +2 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +53 -38
- package/src/libs/util/data-fetcher/src/lib/readers/base.ts +11 -0
- package/src/libs/util/data-fetcher/src/lib/readers/csv.ts +9 -47
- package/src/libs/util/data-fetcher/src/lib/readers/excel.ts +27 -27
- package/src/libs/util/data-fetcher/src/lib/readers/geojson.ts +5 -24
- package/src/libs/util/data-fetcher/src/lib/readers/gml.ts +5 -49
- package/src/libs/util/data-fetcher/src/lib/readers/json.ts +5 -23
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +184 -128
- package/src/libs/util/data-fetcher/src/lib/utils.ts +0 -143
- package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/tailwind.base.css +5 -0
- package/translations/de.json +11 -1
- package/translations/en.json +11 -1
- package/translations/es.json +10 -0
- package/translations/fr.json +11 -1
- package/translations/it.json +11 -1
- package/translations/nl.json +10 -0
- package/translations/pt.json +10 -0
- package/translations/sk.json +11 -1
- 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';
|
|
@@ -41,7 +41,7 @@ import * as TOML from '@ltd/j-toml';
|
|
|
41
41
|
import { Style, Stroke, Fill, Circle } from 'ol/style.js';
|
|
42
42
|
import CircleStyle from 'ol/style/Circle.js';
|
|
43
43
|
import EmblaCarousel from 'embla-carousel';
|
|
44
|
-
import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn,
|
|
44
|
+
import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirTrash, iconoirSquareDashed, iconoirImport, iconoirCheckCircle, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn, iconoirBadgeCheck, iconoirSystemShut, iconoirCircle, iconoirAttachment, iconoirRefresh } from '@ng-icons/iconoir';
|
|
45
45
|
import { MatButtonModule } from '@angular/material/button';
|
|
46
46
|
import * as i1$8 from '@angular/cdk/overlay';
|
|
47
47
|
import { OverlayContainer, ScrollStrategyOptions, OverlayModule, CdkConnectedOverlay, ScrollDispatcher, Overlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
|
@@ -61,7 +61,7 @@ import { DateAdapter, MatNativeDateModule, MAT_DATE_LOCALE } from '@angular/mate
|
|
|
61
61
|
import * as i1$6 from '@angular/material/datepicker';
|
|
62
62
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
63
63
|
import * as i1$7 from 'ngx-dropzone';
|
|
64
|
-
import { NgxDropzoneModule } from 'ngx-dropzone';
|
|
64
|
+
import { NgxDropzoneModule, NgxDropzoneComponent } from 'ngx-dropzone';
|
|
65
65
|
import * as i1$9 from '@angular/material/button-toggle';
|
|
66
66
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
67
67
|
import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
|
|
@@ -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;
|
|
@@ -19498,6 +19518,18 @@ function propagateToDocumentOnly(event) {
|
|
|
19498
19518
|
}, 0);
|
|
19499
19519
|
}
|
|
19500
19520
|
|
|
19521
|
+
function isFileExtensionValid(fileName, acceptedExtensions) {
|
|
19522
|
+
return acceptedExtensions.some((ext) => fileName.toLowerCase().endsWith(ext));
|
|
19523
|
+
}
|
|
19524
|
+
function readFileAsText(file) {
|
|
19525
|
+
return new Promise((resolve, reject) => {
|
|
19526
|
+
const reader = new FileReader();
|
|
19527
|
+
reader.onload = () => resolve(reader.result);
|
|
19528
|
+
reader.onerror = () => reject(reader.error);
|
|
19529
|
+
reader.readAsText(file);
|
|
19530
|
+
});
|
|
19531
|
+
}
|
|
19532
|
+
|
|
19501
19533
|
function formatUserInfo(userInfo, displayCount = false) {
|
|
19502
19534
|
const infos = (typeof userInfo === 'string' ? userInfo : '').split('|');
|
|
19503
19535
|
const count = displayCount ? ` ${infos[3].split(' ')[1]}` : '';
|
|
@@ -20369,7 +20401,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20369
20401
|
}] } });
|
|
20370
20402
|
|
|
20371
20403
|
var name = "geonetwork-ui";
|
|
20372
|
-
var version = "2.11.0-dev.
|
|
20404
|
+
var version = "2.11.0-dev.a5c259d9e";
|
|
20373
20405
|
var engines = {
|
|
20374
20406
|
node: ">=24"
|
|
20375
20407
|
};
|
|
@@ -20399,20 +20431,21 @@ var peerDependencies = {
|
|
|
20399
20431
|
"@ngrx/store": "19.x || 20.x || 21.x",
|
|
20400
20432
|
"@ngrx/store-devtools": "19.x || 20.x || 21.x",
|
|
20401
20433
|
"@ngrx/operators": "19.x || 20.x || 21.x",
|
|
20402
|
-
"@ngx-translate/core": "
|
|
20403
|
-
"@ngx-translate/http-loader": "
|
|
20404
|
-
"flag-icons": "
|
|
20434
|
+
"@ngx-translate/core": "17.x",
|
|
20435
|
+
"@ngx-translate/http-loader": "17.x",
|
|
20436
|
+
"flag-icons": "~7.5.0",
|
|
20405
20437
|
rxjs: "7.x",
|
|
20406
20438
|
"zone.js": "*",
|
|
20407
20439
|
tailwindcss: "3.x"
|
|
20408
20440
|
};
|
|
20409
20441
|
var dependencies = {
|
|
20410
20442
|
"@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/
|
|
20443
|
+
"@camptocamp/ogc-client": "1.3.1-dev.200c02a",
|
|
20444
|
+
"@duckdb/duckdb-wasm": "~1.32.0",
|
|
20445
|
+
"@geospatial-sdk/core": "0.0.5-dev.78",
|
|
20446
|
+
"@geospatial-sdk/geocoding": "0.0.5-dev.78",
|
|
20447
|
+
"@geospatial-sdk/legend": "0.0.5-dev.78",
|
|
20448
|
+
"@geospatial-sdk/openlayers": "0.0.5-dev.78",
|
|
20416
20449
|
"@ltd/j-toml": "~1.38.0",
|
|
20417
20450
|
"@messageformat/core": "~3.4.0",
|
|
20418
20451
|
"@ng-icons/core": "~32.5.0",
|
|
@@ -20420,7 +20453,7 @@ var dependencies = {
|
|
|
20420
20453
|
"@ng-icons/material-icons": "~32.5.0",
|
|
20421
20454
|
"@ng-icons/tabler-icons": "~32.4.0",
|
|
20422
20455
|
"@rgrove/parse-xml": "4.2.0",
|
|
20423
|
-
|
|
20456
|
+
"apache-arrow": "~17.0.0",
|
|
20424
20457
|
"chart.js": "~4.5.1",
|
|
20425
20458
|
"chroma-js": "~3.2.0",
|
|
20426
20459
|
"date-fns": "4.1.0",
|
|
@@ -20433,7 +20466,6 @@ var dependencies = {
|
|
|
20433
20466
|
"ngx-dropzone": "~3.1.0",
|
|
20434
20467
|
"ngx-translate-messageformat-compiler": "~7.2.0",
|
|
20435
20468
|
ol: "~10.8.0",
|
|
20436
|
-
papaparse: "~5.5.3",
|
|
20437
20469
|
proj4: "~2.20.4",
|
|
20438
20470
|
rdflib: "~2.3.5",
|
|
20439
20471
|
semver: "~7.7.4",
|
|
@@ -20678,6 +20710,7 @@ class ElasticsearchService {
|
|
|
20678
20710
|
values: uuids,
|
|
20679
20711
|
},
|
|
20680
20712
|
},
|
|
20713
|
+
size: uuids.length,
|
|
20681
20714
|
};
|
|
20682
20715
|
}
|
|
20683
20716
|
getRelatedRecordPayload(record, size = 6, _source = [...ES_SOURCE_SUMMARY, 'createDate']) {
|
|
@@ -20728,10 +20761,28 @@ class ElasticsearchService {
|
|
|
20728
20761
|
};
|
|
20729
20762
|
}
|
|
20730
20763
|
buildPayloadSort(sortBy) {
|
|
20731
|
-
if (sortBy ===
|
|
20764
|
+
if (!sortBy || sortBy.length === 0)
|
|
20732
20765
|
return undefined;
|
|
20733
|
-
const fields = Array.isArray(sortBy[0])
|
|
20734
|
-
|
|
20766
|
+
const fields = Array.isArray(sortBy[0])
|
|
20767
|
+
? sortBy
|
|
20768
|
+
: [sortBy];
|
|
20769
|
+
return fields.map((field) => {
|
|
20770
|
+
// Sort by nested array of dates only works with the explicit syntax
|
|
20771
|
+
if (field[1].endsWith('.date')) {
|
|
20772
|
+
const nestedPath = field[1].slice(0, field[1].lastIndexOf('.date'));
|
|
20773
|
+
return {
|
|
20774
|
+
[field[1]]: {
|
|
20775
|
+
order: field[0],
|
|
20776
|
+
mode: field[0] === 'desc' ? 'max' : 'min',
|
|
20777
|
+
missing: '_last',
|
|
20778
|
+
nested: {
|
|
20779
|
+
path: nestedPath,
|
|
20780
|
+
},
|
|
20781
|
+
},
|
|
20782
|
+
};
|
|
20783
|
+
}
|
|
20784
|
+
return { [field[1]]: field[0] };
|
|
20785
|
+
});
|
|
20735
20786
|
}
|
|
20736
20787
|
injectLangInQueryStringFields(queryFieldsPriority) {
|
|
20737
20788
|
const queryLang = this.getQueryLang();
|
|
@@ -21211,7 +21262,7 @@ class Gn4Repository {
|
|
|
21211
21262
|
}
|
|
21212
21263
|
getRecord(uniqueIdentifier) {
|
|
21213
21264
|
return this.gn4SearchApi
|
|
21214
|
-
.search('bucket', ['fcats', 'hassources'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21265
|
+
.search('bucket', ['fcats', 'hassources', 'siblings', 'associated'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21215
21266
|
.pipe(map$1((results) => results.hits.hits[0]), switchMap((record) => record ? this.gn4Mapper.readRecord(record) : of(null)));
|
|
21216
21267
|
}
|
|
21217
21268
|
getMultipleRecords(uniqueIdentifiers) {
|
|
@@ -21265,19 +21316,28 @@ class Gn4Repository {
|
|
|
21265
21316
|
.search('bucket', null, JSON.stringify(this.gn4SearchHelper.getRelatedRecordPayload(similarTo, 3)))
|
|
21266
21317
|
.pipe(switchMap((results) => this.gn4Mapper.readRecords(results.hits.hits)));
|
|
21267
21318
|
}
|
|
21268
|
-
|
|
21269
|
-
const
|
|
21270
|
-
|
|
21271
|
-
|
|
21272
|
-
|
|
21273
|
-
|
|
21274
|
-
|
|
21275
|
-
|
|
21276
|
-
|
|
21277
|
-
|
|
21278
|
-
|
|
21319
|
+
getLinkedRecords(record) {
|
|
21320
|
+
const siblings = (record.extras?.['siblings'] ?? []);
|
|
21321
|
+
const relations = [
|
|
21322
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? [])],
|
|
21323
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? [])],
|
|
21324
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
21325
|
+
[
|
|
21326
|
+
'associated',
|
|
21327
|
+
(record.extras?.['associatedIdentifiers'] ?? []),
|
|
21328
|
+
],
|
|
21329
|
+
];
|
|
21330
|
+
const requested = relations.filter(([, identifiers]) => identifiers.length > 0);
|
|
21331
|
+
if (requested.length === 0) {
|
|
21332
|
+
return of([]);
|
|
21279
21333
|
}
|
|
21280
|
-
return
|
|
21334
|
+
return forkJoin(requested.map(([relation, identifiers]) => this.getMultipleRecords(identifiers).pipe(map$1((records) => (records ?? []).map((record) => ({
|
|
21335
|
+
record,
|
|
21336
|
+
relation,
|
|
21337
|
+
associationType: relation === 'sibling'
|
|
21338
|
+
? siblings.find(({ uniqueIdentifier }) => uniqueIdentifier === record.uniqueIdentifier)?.associationType
|
|
21339
|
+
: undefined,
|
|
21340
|
+
}))), catchError(() => of([]))))).pipe(map$1((groups) => groups.flat()));
|
|
21281
21341
|
}
|
|
21282
21342
|
aggregate(params) {
|
|
21283
21343
|
// if aggregations are empty, return an empty object right away
|
|
@@ -22538,9 +22598,8 @@ const VECTOR_STYLE_DEFAULT = new InjectionToken('vectorStyleDefault', {
|
|
|
22538
22598
|
});
|
|
22539
22599
|
|
|
22540
22600
|
const DEFAULT_BASEMAP_LAYER = {
|
|
22541
|
-
type: '
|
|
22542
|
-
|
|
22543
|
-
attributions: `<span>© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, © <a href="https://carto.com/">Carto</a></span>`,
|
|
22601
|
+
type: 'maplibre-style',
|
|
22602
|
+
styleUrl: `https://basemaps.cartocdn.com/gl/positron-gl-style/style.json`,
|
|
22544
22603
|
};
|
|
22545
22604
|
const DEFAULT_VIEW = {
|
|
22546
22605
|
center: [0, 15],
|
|
@@ -23255,6 +23314,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23255
23314
|
'search_preset',
|
|
23256
23315
|
'advanced_filters',
|
|
23257
23316
|
'limit',
|
|
23317
|
+
'spatial_extent_max_file_size',
|
|
23258
23318
|
], warnings, errors);
|
|
23259
23319
|
const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
|
|
23260
23320
|
searchConfig =
|
|
@@ -23272,6 +23332,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23272
23332
|
})),
|
|
23273
23333
|
ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
|
|
23274
23334
|
LIMIT: parsedSearchSection.limit,
|
|
23335
|
+
SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
|
|
23275
23336
|
};
|
|
23276
23337
|
const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
|
|
23277
23338
|
metadataQualityConfig =
|
|
@@ -24669,28 +24730,68 @@ class DragAndDropFileInputComponent {
|
|
|
24669
24730
|
constructor() {
|
|
24670
24731
|
this.placeholder = placeholder;
|
|
24671
24732
|
this.accept = '*';
|
|
24733
|
+
this.maxFileSizeMb = null;
|
|
24734
|
+
this.icon = null;
|
|
24735
|
+
this.dropzoneBackgroundColor = null;
|
|
24736
|
+
this.textClass = '';
|
|
24737
|
+
this.extraClass = '';
|
|
24738
|
+
this.showFileName = true;
|
|
24672
24739
|
this.fileChange = new EventEmitter();
|
|
24740
|
+
this.errorChange = new EventEmitter();
|
|
24673
24741
|
this.selectedFile = null;
|
|
24674
24742
|
}
|
|
24675
24743
|
get fileName() {
|
|
24676
24744
|
return this.selectedFile && this.selectedFile.name;
|
|
24677
24745
|
}
|
|
24746
|
+
get maxFileSizeBytes() {
|
|
24747
|
+
return typeof this.maxFileSizeMb === 'number'
|
|
24748
|
+
? megabytesToBytes(this.maxFileSizeMb)
|
|
24749
|
+
: null;
|
|
24750
|
+
}
|
|
24678
24751
|
selectFile(event) {
|
|
24752
|
+
if (event.rejectedFiles?.length) {
|
|
24753
|
+
const reason = event.rejectedFiles[0].reason;
|
|
24754
|
+
this.errorChange.emit(reason === 'size' ? 'file-too-large' : 'invalid-extension');
|
|
24755
|
+
return;
|
|
24756
|
+
}
|
|
24679
24757
|
this.selectedFile = event.addedFiles[0];
|
|
24680
24758
|
this.fileChange.emit(this.selectedFile);
|
|
24681
24759
|
}
|
|
24760
|
+
openFileSelector() {
|
|
24761
|
+
this.dropzone.showFileSelector();
|
|
24762
|
+
}
|
|
24763
|
+
clear() {
|
|
24764
|
+
this.selectedFile = null;
|
|
24765
|
+
}
|
|
24682
24766
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24683
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept" }, outputs: { fileChange: "fileChange" }, ngImport: i0, template: "<div
|
|
24767
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept", maxFileSizeMb: "maxFileSizeMb", icon: "icon", dropzoneBackgroundColor: "dropzoneBackgroundColor", textClass: "textClass", extraClass: "extraClass", showFileName: "showFileName" }, outputs: { fileChange: "fileChange", errorChange: "errorChange" }, viewQueries: [{ propertyName: "dropzone", first: true, predicate: NgxDropzoneComponent, descendants: true }], ngImport: i0, template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: NgxDropzoneModule }, { kind: "component", type: i1$7.NgxDropzoneComponent, selector: "ngx-dropzone, [ngx-dropzone]", inputs: ["accept", "disabled", "multiple", "maxFileSize", "expandable", "disableClick", "processDirectoryDrop", "id", "aria-label", "aria-labelledby", "aria-describedby"], outputs: ["change"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
|
|
24684
24768
|
}
|
|
24685
24769
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, decorators: [{
|
|
24686
24770
|
type: Component,
|
|
24687
|
-
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule], template: "<div
|
|
24771
|
+
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule, NgIcon], template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"] }]
|
|
24688
24772
|
}], propDecorators: { placeholder: [{
|
|
24689
24773
|
type: Input
|
|
24690
24774
|
}], accept: [{
|
|
24691
24775
|
type: Input
|
|
24776
|
+
}], maxFileSizeMb: [{
|
|
24777
|
+
type: Input
|
|
24778
|
+
}], icon: [{
|
|
24779
|
+
type: Input
|
|
24780
|
+
}], dropzoneBackgroundColor: [{
|
|
24781
|
+
type: Input
|
|
24782
|
+
}], textClass: [{
|
|
24783
|
+
type: Input
|
|
24784
|
+
}], extraClass: [{
|
|
24785
|
+
type: Input
|
|
24786
|
+
}], showFileName: [{
|
|
24787
|
+
type: Input
|
|
24692
24788
|
}], fileChange: [{
|
|
24693
24789
|
type: Output
|
|
24790
|
+
}], errorChange: [{
|
|
24791
|
+
type: Output
|
|
24792
|
+
}], dropzone: [{
|
|
24793
|
+
type: ViewChild,
|
|
24794
|
+
args: [NgxDropzoneComponent]
|
|
24694
24795
|
}] } });
|
|
24695
24796
|
|
|
24696
24797
|
class DropdownMultiselectComponent {
|
|
@@ -25767,6 +25868,157 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
25767
25868
|
type: Output
|
|
25768
25869
|
}] } });
|
|
25769
25870
|
|
|
25871
|
+
marker('search.filters.spatialExtent.import');
|
|
25872
|
+
marker('search.filters.spatialExtent.helpText');
|
|
25873
|
+
marker('search.filters.spatialExtent.error.title');
|
|
25874
|
+
marker('search.filters.spatialExtent.bboxPrefix');
|
|
25875
|
+
marker('search.filters.spatialExtent.bboxDelete');
|
|
25876
|
+
class SpatialExtentDropdownComponent {
|
|
25877
|
+
constructor() {
|
|
25878
|
+
this.cd = inject(ChangeDetectorRef);
|
|
25879
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
25880
|
+
this.maxFileSizeMb = null;
|
|
25881
|
+
this.bboxChange = new EventEmitter();
|
|
25882
|
+
this.errorChange = new EventEmitter();
|
|
25883
|
+
this.bbox = null;
|
|
25884
|
+
this.fileName = '';
|
|
25885
|
+
this.overlayPositions = [
|
|
25886
|
+
{
|
|
25887
|
+
originX: 'start',
|
|
25888
|
+
originY: 'bottom',
|
|
25889
|
+
overlayX: 'start',
|
|
25890
|
+
overlayY: 'top',
|
|
25891
|
+
offsetY: 8,
|
|
25892
|
+
},
|
|
25893
|
+
{
|
|
25894
|
+
originX: 'start',
|
|
25895
|
+
originY: 'top',
|
|
25896
|
+
overlayX: 'start',
|
|
25897
|
+
overlayY: 'bottom',
|
|
25898
|
+
offsetY: -8,
|
|
25899
|
+
},
|
|
25900
|
+
];
|
|
25901
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
25902
|
+
this.overlayOpen = false;
|
|
25903
|
+
this.overlayMinWidth = 'none';
|
|
25904
|
+
this.errorKey = null;
|
|
25905
|
+
}
|
|
25906
|
+
get hasSelection() {
|
|
25907
|
+
return !!this.bbox;
|
|
25908
|
+
}
|
|
25909
|
+
openOverlay() {
|
|
25910
|
+
this.overlayMinWidth =
|
|
25911
|
+
this.overlayOrigin.elementRef.nativeElement.getBoundingClientRect()
|
|
25912
|
+
.width + 'px';
|
|
25913
|
+
this.overlayOpen = true;
|
|
25914
|
+
}
|
|
25915
|
+
closeOverlay() {
|
|
25916
|
+
this.overlayOpen = false;
|
|
25917
|
+
}
|
|
25918
|
+
toggleOverlay() {
|
|
25919
|
+
if (this.overlayOpen) {
|
|
25920
|
+
this.closeOverlay();
|
|
25921
|
+
}
|
|
25922
|
+
else {
|
|
25923
|
+
this.openOverlay();
|
|
25924
|
+
}
|
|
25925
|
+
}
|
|
25926
|
+
async handleFileSelected(file) {
|
|
25927
|
+
this.errorKey = null;
|
|
25928
|
+
let content;
|
|
25929
|
+
try {
|
|
25930
|
+
content = await readFileAsText(file);
|
|
25931
|
+
const parsed = JSON.parse(content);
|
|
25932
|
+
const geometry = getGeometryFromGeoJSON(parsed);
|
|
25933
|
+
if (!geometry) {
|
|
25934
|
+
this.setError(marker('search.filters.spatialExtent.error.noGeometry'));
|
|
25935
|
+
return;
|
|
25936
|
+
}
|
|
25937
|
+
const bbox = getGeometryBoundingBox(geometry);
|
|
25938
|
+
this.bbox = bbox;
|
|
25939
|
+
this.fileName = file.name;
|
|
25940
|
+
this.bboxChange.emit(bbox);
|
|
25941
|
+
this.cd.markForCheck();
|
|
25942
|
+
}
|
|
25943
|
+
catch {
|
|
25944
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
25945
|
+
return;
|
|
25946
|
+
}
|
|
25947
|
+
}
|
|
25948
|
+
handleFileError(error) {
|
|
25949
|
+
if (error === 'file-too-large') {
|
|
25950
|
+
this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
|
|
25951
|
+
maxSize: this.maxFileSizeMb,
|
|
25952
|
+
});
|
|
25953
|
+
}
|
|
25954
|
+
else {
|
|
25955
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
25956
|
+
}
|
|
25957
|
+
}
|
|
25958
|
+
setError(errorKey, params) {
|
|
25959
|
+
this.errorKey = errorKey;
|
|
25960
|
+
this.errorChange.emit({ key: errorKey, params });
|
|
25961
|
+
this.cd.markForCheck();
|
|
25962
|
+
}
|
|
25963
|
+
removeSelection(event) {
|
|
25964
|
+
this.bbox = null;
|
|
25965
|
+
this.fileName = '';
|
|
25966
|
+
this.errorKey = null;
|
|
25967
|
+
this.fileInput?.clear();
|
|
25968
|
+
this.bboxChange.emit(null);
|
|
25969
|
+
propagateToDocumentOnly(event);
|
|
25970
|
+
}
|
|
25971
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
25972
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SpatialExtentDropdownComponent, isStandalone: true, selector: "gn-ui-spatial-extent-dropdown", inputs: { title: "title", maxFileSizeMb: "maxFileSizeMb" }, outputs: { bboxChange: "bboxChange", errorChange: "errorChange" }, providers: [
|
|
25973
|
+
provideIcons({
|
|
25974
|
+
iconoirCheckCircle,
|
|
25975
|
+
iconoirImport,
|
|
25976
|
+
iconoirSquareDashed,
|
|
25977
|
+
iconoirTrash,
|
|
25978
|
+
matClose,
|
|
25979
|
+
matExpandLess,
|
|
25980
|
+
matExpandMore,
|
|
25981
|
+
}),
|
|
25982
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "fileInput", first: true, predicate: DragAndDropFileInputComponent, descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[8px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirImport\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"\n ('search.filters.spatialExtent.bboxDelete' | translate) +\n ' ' +\n fileName\n \"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxPrefix' | translate }}\n {{ fileName }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxDelete' | translate }}\n {{ fileName }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$8.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$8.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
25983
|
+
}
|
|
25984
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
|
|
25985
|
+
type: Component,
|
|
25986
|
+
args: [{ selector: 'gn-ui-spatial-extent-dropdown', standalone: true, imports: [
|
|
25987
|
+
ButtonComponent,
|
|
25988
|
+
NgIcon,
|
|
25989
|
+
OverlayModule,
|
|
25990
|
+
TranslatePipe,
|
|
25991
|
+
DragAndDropFileInputComponent,
|
|
25992
|
+
], providers: [
|
|
25993
|
+
provideIcons({
|
|
25994
|
+
iconoirCheckCircle,
|
|
25995
|
+
iconoirImport,
|
|
25996
|
+
iconoirSquareDashed,
|
|
25997
|
+
iconoirTrash,
|
|
25998
|
+
matClose,
|
|
25999
|
+
matExpandLess,
|
|
26000
|
+
matExpandMore,
|
|
26001
|
+
}),
|
|
26002
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[8px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirImport\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"\n ('search.filters.spatialExtent.bboxDelete' | translate) +\n ' ' +\n fileName\n \"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxPrefix' | translate }}\n {{ fileName }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxDelete' | translate }}\n {{ fileName }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n" }]
|
|
26003
|
+
}], propDecorators: { title: [{
|
|
26004
|
+
type: Input
|
|
26005
|
+
}], maxFileSizeMb: [{
|
|
26006
|
+
type: Input
|
|
26007
|
+
}], bboxChange: [{
|
|
26008
|
+
type: Output
|
|
26009
|
+
}], errorChange: [{
|
|
26010
|
+
type: Output
|
|
26011
|
+
}], overlayOrigin: [{
|
|
26012
|
+
type: ViewChild,
|
|
26013
|
+
args: ['overlayOrigin']
|
|
26014
|
+
}], overlay: [{
|
|
26015
|
+
type: ViewChild,
|
|
26016
|
+
args: [CdkConnectedOverlay]
|
|
26017
|
+
}], fileInput: [{
|
|
26018
|
+
type: ViewChild,
|
|
26019
|
+
args: [DragAndDropFileInputComponent]
|
|
26020
|
+
}] } });
|
|
26021
|
+
|
|
25770
26022
|
class CellPopinComponent {
|
|
25771
26023
|
constructor() {
|
|
25772
26024
|
this.scrollDispatcher = inject(ScrollDispatcher);
|
|
@@ -27933,6 +28185,18 @@ class DateRangeSearchField extends SimpleSearchField {
|
|
|
27933
28185
|
return 'dateRange';
|
|
27934
28186
|
}
|
|
27935
28187
|
}
|
|
28188
|
+
class SpatialExtentSearchField extends SimpleSearchField {
|
|
28189
|
+
constructor(injector) {
|
|
28190
|
+
super('spatialExtent', injector, 'asc');
|
|
28191
|
+
}
|
|
28192
|
+
getAvailableValues() {
|
|
28193
|
+
// TODO: return an array of spatial extents to show which ones are available in the dropdown
|
|
28194
|
+
return of([]);
|
|
28195
|
+
}
|
|
28196
|
+
getType() {
|
|
28197
|
+
return 'spatialExtent';
|
|
28198
|
+
}
|
|
28199
|
+
}
|
|
27936
28200
|
marker('search.filters.availableServices.view');
|
|
27937
28201
|
marker('search.filters.availableServices.download');
|
|
27938
28202
|
class AvailableServicesField extends SimpleSearchField {
|
|
@@ -28077,6 +28341,7 @@ marker('search.filters.producerOrg');
|
|
|
28077
28341
|
marker('search.filters.publisherOrg');
|
|
28078
28342
|
marker('search.filters.user');
|
|
28079
28343
|
marker('search.filters.changeDate');
|
|
28344
|
+
marker('search.filters.spatialExtent');
|
|
28080
28345
|
class FieldsService {
|
|
28081
28346
|
constructor() {
|
|
28082
28347
|
this.injector = inject(Injector);
|
|
@@ -28100,6 +28365,7 @@ class FieldsService {
|
|
|
28100
28365
|
user: new UserSearchField(this.injector),
|
|
28101
28366
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
28102
28367
|
availableServices: new AvailableServicesField(this.injector),
|
|
28368
|
+
spatialExtent: new SpatialExtentSearchField(this.injector),
|
|
28103
28369
|
};
|
|
28104
28370
|
}
|
|
28105
28371
|
get supportedFields() {
|
|
@@ -31284,19 +31550,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31284
31550
|
type: Output
|
|
31285
31551
|
}] } });
|
|
31286
31552
|
|
|
31553
|
+
class NotificationsService {
|
|
31554
|
+
constructor() {
|
|
31555
|
+
this.notifications$ = new BehaviorSubject([]);
|
|
31556
|
+
}
|
|
31557
|
+
showNotification(content, timeoutMs, error) {
|
|
31558
|
+
error && console.error(error);
|
|
31559
|
+
const id = Math.floor(Math.random() * 1000000);
|
|
31560
|
+
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31561
|
+
if (typeof timeoutMs !== 'undefined') {
|
|
31562
|
+
setTimeout(() => {
|
|
31563
|
+
this.removeNotificationById(id);
|
|
31564
|
+
}, timeoutMs);
|
|
31565
|
+
}
|
|
31566
|
+
return id;
|
|
31567
|
+
}
|
|
31568
|
+
removeNotificationById(id) {
|
|
31569
|
+
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31570
|
+
}
|
|
31571
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31572
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31573
|
+
}
|
|
31574
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31575
|
+
type: Injectable,
|
|
31576
|
+
args: [{
|
|
31577
|
+
providedIn: 'root',
|
|
31578
|
+
}]
|
|
31579
|
+
}] });
|
|
31580
|
+
|
|
31581
|
+
class NotificationsContainerComponent {
|
|
31582
|
+
constructor() {
|
|
31583
|
+
this.notificationsService = inject(NotificationsService);
|
|
31584
|
+
}
|
|
31585
|
+
trackById(index, notification) {
|
|
31586
|
+
return notification.id;
|
|
31587
|
+
}
|
|
31588
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31589
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NotificationsContainerComponent, isStandalone: true, selector: "gn-ui-notifications-container", ngImport: i0, template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationComponent, selector: "gn-ui-notification", inputs: ["type", "title", "text", "closeMessage"], outputs: ["notificationClose"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], animations: [
|
|
31590
|
+
trigger('enterExit', [
|
|
31591
|
+
transition(':enter', [
|
|
31592
|
+
animate('150ms', keyframes([
|
|
31593
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31594
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31595
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31596
|
+
])),
|
|
31597
|
+
]),
|
|
31598
|
+
transition(':leave', [
|
|
31599
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31600
|
+
]),
|
|
31601
|
+
]),
|
|
31602
|
+
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31603
|
+
}
|
|
31604
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31605
|
+
type: Component,
|
|
31606
|
+
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31607
|
+
trigger('enterExit', [
|
|
31608
|
+
transition(':enter', [
|
|
31609
|
+
animate('150ms', keyframes([
|
|
31610
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31611
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31612
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31613
|
+
])),
|
|
31614
|
+
]),
|
|
31615
|
+
transition(':leave', [
|
|
31616
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31617
|
+
]),
|
|
31618
|
+
]),
|
|
31619
|
+
], template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n" }]
|
|
31620
|
+
}] });
|
|
31621
|
+
|
|
31287
31622
|
class FilterDropdownComponent {
|
|
31288
31623
|
constructor() {
|
|
31289
31624
|
this.searchFacade = inject(SearchFacade);
|
|
31290
31625
|
this.searchService = inject(SearchService);
|
|
31291
31626
|
this.fieldsService = inject(FieldsService);
|
|
31627
|
+
this.notificationsService = inject(NotificationsService);
|
|
31628
|
+
this.translateService = inject(TranslateService);
|
|
31629
|
+
this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
|
|
31292
31630
|
this.selected$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$1((fieldValues) => fieldValues[this.fieldName]), filter$1((selected) => !!selected), startWith$1([]), catchError(() => of([])));
|
|
31293
31631
|
this.selectedDateRange$ = this.selected$.pipe(map$1((selectedDateRange) => selectedDateRange));
|
|
31632
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31294
31633
|
}
|
|
31295
31634
|
onSelectedValues(values) {
|
|
31296
31635
|
this.fieldsService
|
|
31297
31636
|
.buildFiltersFromFieldValues({ [this.fieldName]: values })
|
|
31298
31637
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31299
31638
|
}
|
|
31639
|
+
onBboxChange(bbox) {
|
|
31640
|
+
console.log(bbox);
|
|
31641
|
+
this.clearSpatialExtentErrorNotification();
|
|
31642
|
+
}
|
|
31643
|
+
onSpatialExtentError(error) {
|
|
31644
|
+
this.clearSpatialExtentErrorNotification();
|
|
31645
|
+
this.spatialExtentErrorNotificationId =
|
|
31646
|
+
this.notificationsService.showNotification({
|
|
31647
|
+
type: 'error',
|
|
31648
|
+
title: this.translateService.instant('search.filters.spatialExtent.error.title'),
|
|
31649
|
+
text: this.translateService.instant(error.key, error.params),
|
|
31650
|
+
});
|
|
31651
|
+
}
|
|
31652
|
+
clearSpatialExtentErrorNotification() {
|
|
31653
|
+
if (this.spatialExtentErrorNotificationId === null)
|
|
31654
|
+
return;
|
|
31655
|
+
this.notificationsService.removeNotificationById(this.spatialExtentErrorNotificationId);
|
|
31656
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31657
|
+
}
|
|
31300
31658
|
ngOnInit() {
|
|
31301
31659
|
this.fieldType = this.fieldsService.getFieldType(this.fieldName);
|
|
31302
31660
|
this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(startWith$1([]), map$1((values) => values.map((v) => ({
|
|
@@ -31324,7 +31682,7 @@ class FilterDropdownComponent {
|
|
|
31324
31682
|
}
|
|
31325
31683
|
}
|
|
31326
31684
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31327
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [startDate]=\"(selectedDateRange$ | async)?.start\"\n [endDate]=\"(selectedDateRange$ | async)?.end\"\n (startDateChange)=\"onStartDateChange($event)\"\n (endDateChange)=\"onEndDateChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DateRangeDropdownComponent, selector: "gn-ui-date-range-dropdown", inputs: ["title", "startDate", "endDate"], outputs: ["startDateChange", "endDateChange"] }, { kind: "component", type: DropdownMultiselectComponent, selector: "gn-ui-dropdown-multiselect", inputs: ["title", "choices", "selected", "allowSearch", "maxRows", "searchInputValue"], outputs: ["selectValues"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31685
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [startDate]=\"(selectedDateRange$ | async)?.start\"\n [endDate]=\"(selectedDateRange$ | async)?.end\"\n (startDateChange)=\"onStartDateChange($event)\"\n (endDateChange)=\"onEndDateChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DateRangeDropdownComponent, selector: "gn-ui-date-range-dropdown", inputs: ["title", "startDate", "endDate"], outputs: ["startDateChange", "endDateChange"] }, { kind: "component", type: DropdownMultiselectComponent, selector: "gn-ui-dropdown-multiselect", inputs: ["title", "choices", "selected", "allowSearch", "maxRows", "searchInputValue"], outputs: ["selectValues"] }, { kind: "component", type: SpatialExtentDropdownComponent, selector: "gn-ui-spatial-extent-dropdown", inputs: ["title", "maxFileSizeMb"], outputs: ["bboxChange", "errorChange"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31328
31686
|
}
|
|
31329
31687
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
|
|
31330
31688
|
type: Component,
|
|
@@ -31332,7 +31690,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31332
31690
|
CommonModule,
|
|
31333
31691
|
DateRangeDropdownComponent,
|
|
31334
31692
|
DropdownMultiselectComponent,
|
|
31335
|
-
|
|
31693
|
+
SpatialExtentDropdownComponent,
|
|
31694
|
+
], template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [startDate]=\"(selectedDateRange$ | async)?.start\"\n [endDate]=\"(selectedDateRange$ | async)?.end\"\n (startDateChange)=\"onStartDateChange($event)\"\n (endDateChange)=\"onEndDateChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n" }]
|
|
31336
31695
|
}], propDecorators: { fieldName: [{
|
|
31337
31696
|
type: Input
|
|
31338
31697
|
}], title: [{
|
|
@@ -31802,11 +32161,7 @@ const SortByEnum = {
|
|
|
31802
32161
|
RELEVANCY: ['desc', '_score'],
|
|
31803
32162
|
QUALITY_SCORE: ['desc', 'qualityScore'],
|
|
31804
32163
|
CHANGE_DATE: ['desc', 'changeDate'],
|
|
31805
|
-
|
|
31806
|
-
['desc', 'revisionDateForResource'],
|
|
31807
|
-
['desc', 'publicationDateForResource'],
|
|
31808
|
-
['desc', 'creationDateForResource'],
|
|
31809
|
-
],
|
|
32164
|
+
RESOURCE_DATE: ['desc', 'resourceDate.date'],
|
|
31810
32165
|
};
|
|
31811
32166
|
|
|
31812
32167
|
class SortByComponent {
|
|
@@ -31820,7 +32175,7 @@ class SortByComponent {
|
|
|
31820
32175
|
},
|
|
31821
32176
|
{
|
|
31822
32177
|
label: marker('results.sortBy.dateStamp'),
|
|
31823
|
-
value: SortByEnum.
|
|
32178
|
+
value: SortByEnum.RESOURCE_DATE,
|
|
31824
32179
|
},
|
|
31825
32180
|
{
|
|
31826
32181
|
label: marker('results.sortBy.popularity'),
|
|
@@ -31872,74 +32227,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31872
32227
|
args: ['gnUiSearchStateContainer']
|
|
31873
32228
|
}] } });
|
|
31874
32229
|
|
|
31875
|
-
class NotificationsService {
|
|
31876
|
-
constructor() {
|
|
31877
|
-
this.notifications$ = new BehaviorSubject([]);
|
|
31878
|
-
}
|
|
31879
|
-
showNotification(content, timeoutMs, error) {
|
|
31880
|
-
error && console.error(error);
|
|
31881
|
-
const id = Math.floor(Math.random() * 1000000);
|
|
31882
|
-
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31883
|
-
if (typeof timeoutMs === 'undefined')
|
|
31884
|
-
return;
|
|
31885
|
-
setTimeout(() => {
|
|
31886
|
-
this.removeNotificationById(id);
|
|
31887
|
-
}, timeoutMs);
|
|
31888
|
-
}
|
|
31889
|
-
removeNotificationById(id) {
|
|
31890
|
-
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31891
|
-
}
|
|
31892
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31893
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31894
|
-
}
|
|
31895
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31896
|
-
type: Injectable,
|
|
31897
|
-
args: [{
|
|
31898
|
-
providedIn: 'root',
|
|
31899
|
-
}]
|
|
31900
|
-
}] });
|
|
31901
|
-
|
|
31902
|
-
class NotificationsContainerComponent {
|
|
31903
|
-
constructor() {
|
|
31904
|
-
this.notificationsService = inject(NotificationsService);
|
|
31905
|
-
}
|
|
31906
|
-
trackById(index, notification) {
|
|
31907
|
-
return notification.id;
|
|
31908
|
-
}
|
|
31909
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31910
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NotificationsContainerComponent, isStandalone: true, selector: "gn-ui-notifications-container", ngImport: i0, template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationComponent, selector: "gn-ui-notification", inputs: ["type", "title", "text", "closeMessage"], outputs: ["notificationClose"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], animations: [
|
|
31911
|
-
trigger('enterExit', [
|
|
31912
|
-
transition(':enter', [
|
|
31913
|
-
animate('150ms', keyframes([
|
|
31914
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31915
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31916
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31917
|
-
])),
|
|
31918
|
-
]),
|
|
31919
|
-
transition(':leave', [
|
|
31920
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31921
|
-
]),
|
|
31922
|
-
]),
|
|
31923
|
-
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31924
|
-
}
|
|
31925
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31926
|
-
type: Component,
|
|
31927
|
-
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31928
|
-
trigger('enterExit', [
|
|
31929
|
-
transition(':enter', [
|
|
31930
|
-
animate('150ms', keyframes([
|
|
31931
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31932
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31933
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31934
|
-
])),
|
|
31935
|
-
]),
|
|
31936
|
-
transition(':leave', [
|
|
31937
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31938
|
-
]),
|
|
31939
|
-
]),
|
|
31940
|
-
], template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n" }]
|
|
31941
|
-
}] });
|
|
31942
|
-
|
|
31943
32230
|
class ResultsTableContainerComponent {
|
|
31944
32231
|
constructor() {
|
|
31945
32232
|
this.searchFacade = inject(SearchFacade);
|
|
@@ -32329,11 +32616,11 @@ class AddLayerFromFileComponent {
|
|
|
32329
32616
|
}, 5000);
|
|
32330
32617
|
}
|
|
32331
32618
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32332
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept"], outputs: ["fileChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
32619
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
32333
32620
|
}
|
|
32334
32621
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, decorators: [{
|
|
32335
32622
|
type: Component,
|
|
32336
|
-
args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
|
|
32623
|
+
args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
|
|
32337
32624
|
}] });
|
|
32338
32625
|
|
|
32339
32626
|
class LayersPanelComponent {
|
|
@@ -32498,8 +32785,7 @@ const closeMetadata = createAction('[Metadata view] close');
|
|
|
32498
32785
|
Related actions
|
|
32499
32786
|
*/
|
|
32500
32787
|
const setRelated = createAction('[Metadata view] Set related records', props());
|
|
32501
|
-
const
|
|
32502
|
-
const setSourceOf = createAction('[Metadata view] Set has sources', props());
|
|
32788
|
+
const setLinkedRecords = createAction('[Metadata view] Set associated records', props());
|
|
32503
32789
|
/*
|
|
32504
32790
|
ChartConfig actions
|
|
32505
32791
|
*/
|
|
@@ -32531,9 +32817,8 @@ var mdview_actions = /*#__PURE__*/Object.freeze({
|
|
|
32531
32817
|
loadUserFeedbacksSuccess: loadUserFeedbacksSuccess,
|
|
32532
32818
|
setChartConfig: setChartConfig,
|
|
32533
32819
|
setIncompleteMetadata: setIncompleteMetadata,
|
|
32534
|
-
|
|
32535
|
-
|
|
32536
|
-
setSources: setSources
|
|
32820
|
+
setLinkedRecords: setLinkedRecords,
|
|
32821
|
+
setRelated: setRelated
|
|
32537
32822
|
});
|
|
32538
32823
|
|
|
32539
32824
|
const METADATA_VIEW_FEATURE_STATE_KEY = 'metadataView';
|
|
@@ -32576,12 +32861,9 @@ on(loadFullMetadata, (state) => ({
|
|
|
32576
32861
|
on(setRelated, (state, { related }) => ({
|
|
32577
32862
|
...state,
|
|
32578
32863
|
related,
|
|
32579
|
-
})), on(
|
|
32864
|
+
})), on(setLinkedRecords, (state, { linkedRecords }) => ({
|
|
32580
32865
|
...state,
|
|
32581
|
-
|
|
32582
|
-
})), on(setSourceOf, (state, { sourceOf }) => ({
|
|
32583
|
-
...state,
|
|
32584
|
-
sourceOf,
|
|
32866
|
+
linkedRecords,
|
|
32585
32867
|
})),
|
|
32586
32868
|
/*
|
|
32587
32869
|
ChartConfig reducers
|
|
@@ -32643,8 +32925,7 @@ const getMetadataError = createSelector(getMdViewState, (state) => state.error);
|
|
|
32643
32925
|
Related selectors
|
|
32644
32926
|
*/
|
|
32645
32927
|
const getRelated = createSelector(getMdViewState, (state) => state.related);
|
|
32646
|
-
const
|
|
32647
|
-
const getSourceOf = createSelector(getMdViewState, (state) => state.sourceOf);
|
|
32928
|
+
const getLinkedRecords = createSelector(getMdViewState, (state) => state.linkedRecords);
|
|
32648
32929
|
/*
|
|
32649
32930
|
Metadata selectors
|
|
32650
32931
|
*/
|
|
@@ -32745,212 +33026,10 @@ function parseHeaders(httpHeaders) {
|
|
|
32745
33026
|
return result;
|
|
32746
33027
|
}
|
|
32747
33028
|
|
|
32748
|
-
|
|
32749
|
-
|
|
32750
|
-
|
|
32751
|
-
? fileExtensionMatches[1].toLowerCase()
|
|
32752
|
-
: null;
|
|
32753
|
-
// 1. type hint
|
|
32754
|
-
if (typeHint)
|
|
32755
|
-
return Promise.resolve(typeHint);
|
|
32756
|
-
// 2. content-type header
|
|
32757
|
-
const headers = await fetchHeaders(url);
|
|
32758
|
-
if ('supportedType' in headers)
|
|
32759
|
-
return headers.supportedType;
|
|
32760
|
-
// 3. file extension from url
|
|
32761
|
-
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
32762
|
-
return fileExtension;
|
|
32763
|
-
// no type inferred or hinted
|
|
32764
|
-
if ('mimeType' in headers)
|
|
32765
|
-
throw FetchError.unsupportedType(headers.mimeType);
|
|
32766
|
-
else
|
|
32767
|
-
throw FetchError.unknownType();
|
|
32768
|
-
}
|
|
32769
|
-
function fetchHeaders(url) {
|
|
32770
|
-
return sharedFetch(url, 'HEAD')
|
|
32771
|
-
.catch((error) => {
|
|
32772
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32773
|
-
})
|
|
32774
|
-
.then((response) => {
|
|
32775
|
-
if (!response.ok) {
|
|
32776
|
-
throw FetchError.http(response.status);
|
|
32777
|
-
}
|
|
32778
|
-
return parseHeaders(response.headers);
|
|
32779
|
-
});
|
|
33029
|
+
const GEOMETRY_COLUMN_ALIAS = '__geometry__';
|
|
33030
|
+
function fieldToSql(name) {
|
|
33031
|
+
return `"${name.replace(/"/g, '""')}"`; // escape double quotes in field names
|
|
32780
33032
|
}
|
|
32781
|
-
function fetchDataAsText(url, cacheActive) {
|
|
32782
|
-
const fetchFactory = () => sharedFetch(url)
|
|
32783
|
-
.catch((error) => {
|
|
32784
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32785
|
-
})
|
|
32786
|
-
.then(async (response) => {
|
|
32787
|
-
if (!response.ok) {
|
|
32788
|
-
const clonedResponse = response.clone();
|
|
32789
|
-
throw FetchError.http(response.status, await clonedResponse.text());
|
|
32790
|
-
}
|
|
32791
|
-
const clonedResponse = response.clone();
|
|
32792
|
-
return clonedResponse.text();
|
|
32793
|
-
});
|
|
32794
|
-
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
32795
|
-
}
|
|
32796
|
-
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
32797
|
-
const fetchFactory = () => sharedFetch(url)
|
|
32798
|
-
.catch((error) => {
|
|
32799
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32800
|
-
})
|
|
32801
|
-
.then(async (response) => {
|
|
32802
|
-
if (!response.ok) {
|
|
32803
|
-
throw FetchError.http(response.status, await response.text());
|
|
32804
|
-
}
|
|
32805
|
-
// convert to a numeric array so that we can store the response in cache
|
|
32806
|
-
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
32807
|
-
});
|
|
32808
|
-
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
32809
|
-
return new Uint8Array(array).buffer;
|
|
32810
|
-
});
|
|
32811
|
-
}
|
|
32812
|
-
function tryParseDate(input) {
|
|
32813
|
-
if (typeof input !== 'string')
|
|
32814
|
-
return null;
|
|
32815
|
-
function tryIso(value) {
|
|
32816
|
-
const parsed = parseISO(value);
|
|
32817
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32818
|
-
}
|
|
32819
|
-
function tryFormat(value, format) {
|
|
32820
|
-
const parsed = parse$4(value, format, new Date());
|
|
32821
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32822
|
-
}
|
|
32823
|
-
return (tryIso(input) ||
|
|
32824
|
-
tryFormat(input, 'dd/MM/yyyy') ||
|
|
32825
|
-
tryFormat(input, 'dd.MM.yyyy') ||
|
|
32826
|
-
tryFormat(input, 'MM/dd/yyyy') ||
|
|
32827
|
-
null);
|
|
32828
|
-
}
|
|
32829
|
-
function tryParseNumber(input) {
|
|
32830
|
-
if (isNaN(input))
|
|
32831
|
-
return null;
|
|
32832
|
-
const parsed = parseFloat(input);
|
|
32833
|
-
return isNaN(parsed) ? null : parsed;
|
|
32834
|
-
}
|
|
32835
|
-
function jsonToGeojsonFeature(object) {
|
|
32836
|
-
const { id, properties } = Object.keys(object)
|
|
32837
|
-
.map((property) => (property ? property : 'unknown')) //prevent empty strings
|
|
32838
|
-
.reduce((prev, curr) => curr.toLowerCase().endsWith('id')
|
|
32839
|
-
? {
|
|
32840
|
-
...prev,
|
|
32841
|
-
id: object[curr],
|
|
32842
|
-
}
|
|
32843
|
-
: {
|
|
32844
|
-
...prev,
|
|
32845
|
-
properties: { ...prev.properties, [curr]: object[curr] },
|
|
32846
|
-
}, { id: undefined, properties: {} });
|
|
32847
|
-
return {
|
|
32848
|
-
type: 'Feature',
|
|
32849
|
-
geometry: null,
|
|
32850
|
-
properties,
|
|
32851
|
-
...(id !== undefined && { id }),
|
|
32852
|
-
};
|
|
32853
|
-
}
|
|
32854
|
-
function mutateProperties(items, mutators) {
|
|
32855
|
-
const mutatorKeys = Object.keys(mutators);
|
|
32856
|
-
for (let i = 0, ii = items.length; i < ii; i++) {
|
|
32857
|
-
const item = items[i];
|
|
32858
|
-
for (const mutatorField of mutatorKeys) {
|
|
32859
|
-
if (!(mutatorField in item.properties))
|
|
32860
|
-
continue;
|
|
32861
|
-
item.properties[mutatorField] = mutators[mutatorField](item.properties[mutatorField]);
|
|
32862
|
-
}
|
|
32863
|
-
}
|
|
32864
|
-
return items;
|
|
32865
|
-
}
|
|
32866
|
-
const SAMPLE_SIZE = 20;
|
|
32867
|
-
/**
|
|
32868
|
-
* This will infer field types from a list of data items and cast the values accordingly
|
|
32869
|
-
* @param items
|
|
32870
|
-
* @param inferTypes
|
|
32871
|
-
*/
|
|
32872
|
-
function processItemProperties(items, inferTypes = false) {
|
|
32873
|
-
const foundFields = {};
|
|
32874
|
-
for (let i = 0, ii = Math.min(SAMPLE_SIZE, items.length); i < ii; i++) {
|
|
32875
|
-
const item = items[i];
|
|
32876
|
-
const fields = Object.keys(item.properties);
|
|
32877
|
-
for (const field of fields) {
|
|
32878
|
-
if (!(field in foundFields)) {
|
|
32879
|
-
foundFields[field] = {
|
|
32880
|
-
label: field,
|
|
32881
|
-
name: field,
|
|
32882
|
-
type: null,
|
|
32883
|
-
};
|
|
32884
|
-
}
|
|
32885
|
-
const value = item.properties[field];
|
|
32886
|
-
const info = foundFields[field];
|
|
32887
|
-
if (value === undefined || value === '' || value === null)
|
|
32888
|
-
continue;
|
|
32889
|
-
if (!inferTypes) {
|
|
32890
|
-
if (info.type === null && typeof value === 'number') {
|
|
32891
|
-
info.type = 'number';
|
|
32892
|
-
}
|
|
32893
|
-
else if (info.type === 'number' && typeof value !== 'number') {
|
|
32894
|
-
info.type = 'string';
|
|
32895
|
-
}
|
|
32896
|
-
continue;
|
|
32897
|
-
}
|
|
32898
|
-
const parsedNumber = tryParseNumber(value);
|
|
32899
|
-
if (info.type === null && parsedNumber !== null) {
|
|
32900
|
-
info.type = 'number';
|
|
32901
|
-
continue;
|
|
32902
|
-
}
|
|
32903
|
-
else if (info.type === 'number' && parsedNumber === null) {
|
|
32904
|
-
info.type = 'string';
|
|
32905
|
-
continue;
|
|
32906
|
-
}
|
|
32907
|
-
const parsedDate = tryParseDate(value);
|
|
32908
|
-
if (info.type === null && parsedDate !== null) {
|
|
32909
|
-
info.type = 'date';
|
|
32910
|
-
}
|
|
32911
|
-
else if (info.type === 'date' && parsedDate === null) {
|
|
32912
|
-
info.type = 'string';
|
|
32913
|
-
}
|
|
32914
|
-
}
|
|
32915
|
-
}
|
|
32916
|
-
const properties = [];
|
|
32917
|
-
const mutators = {};
|
|
32918
|
-
for (const field in foundFields) {
|
|
32919
|
-
const info = foundFields[field];
|
|
32920
|
-
if (info.type === 'number') {
|
|
32921
|
-
mutators[field] = tryParseNumber;
|
|
32922
|
-
}
|
|
32923
|
-
else if (info.type === 'date') {
|
|
32924
|
-
mutators[field] = tryParseDate;
|
|
32925
|
-
}
|
|
32926
|
-
properties.push({ ...info, type: info.type || 'string' });
|
|
32927
|
-
}
|
|
32928
|
-
if (inferTypes) {
|
|
32929
|
-
mutateProperties(items, mutators);
|
|
32930
|
-
}
|
|
32931
|
-
return { items, properties };
|
|
32932
|
-
}
|
|
32933
|
-
/**
|
|
32934
|
-
* This creates a Proxy that allows reading and writing to the data item properties
|
|
32935
|
-
* as if it was a simple array of JSON objects
|
|
32936
|
-
* @param items
|
|
32937
|
-
*/
|
|
32938
|
-
function getJsonDataItemsProxy(items) {
|
|
32939
|
-
return new Proxy(items, {
|
|
32940
|
-
get(target, p) {
|
|
32941
|
-
if (typeof p === 'string' &&
|
|
32942
|
-
!Number.isNaN(parseInt(p)) &&
|
|
32943
|
-
target[p]?.properties) {
|
|
32944
|
-
return target[p].properties;
|
|
32945
|
-
}
|
|
32946
|
-
return target[p];
|
|
32947
|
-
},
|
|
32948
|
-
set() {
|
|
32949
|
-
throw new Error('This object is read-only');
|
|
32950
|
-
},
|
|
32951
|
-
});
|
|
32952
|
-
}
|
|
32953
|
-
|
|
32954
33033
|
function filterToSql(filter) {
|
|
32955
33034
|
const operator = filter[0];
|
|
32956
33035
|
const args = filter.slice(1);
|
|
@@ -32965,10 +33044,10 @@ function filterToSql(filter) {
|
|
|
32965
33044
|
case '=':
|
|
32966
33045
|
case '!=':
|
|
32967
33046
|
case 'like':
|
|
32968
|
-
return
|
|
33047
|
+
return `${fieldToSql(args[0])} ${operator.toUpperCase()} ${valueToSql(args[1])}`;
|
|
32969
33048
|
case 'in': {
|
|
32970
33049
|
const values = args.slice(1);
|
|
32971
|
-
return
|
|
33050
|
+
return `${fieldToSql(args[0])} IN (${values.map(valueToSql).join(', ')})`;
|
|
32972
33051
|
}
|
|
32973
33052
|
case 'and':
|
|
32974
33053
|
case 'or': {
|
|
@@ -32987,17 +33066,18 @@ function aggregationToSql(aggregation) {
|
|
|
32987
33066
|
const field = aggregation[1];
|
|
32988
33067
|
switch (operation) {
|
|
32989
33068
|
case 'average':
|
|
32990
|
-
return `AVG(
|
|
33069
|
+
return `CAST(AVG(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`average(${field})`)}`;
|
|
32991
33070
|
case 'sum':
|
|
32992
33071
|
case 'max':
|
|
32993
33072
|
case 'min':
|
|
32994
|
-
return
|
|
33073
|
+
return `CAST(${operation.toUpperCase()}(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`${operation}(${field})`)}`;
|
|
32995
33074
|
case 'count':
|
|
32996
|
-
return 'COUNT(*) as
|
|
33075
|
+
return 'CAST(COUNT(*) AS INTEGER) as "count()"'; // we don't need Bigint precision here
|
|
32997
33076
|
}
|
|
32998
33077
|
}
|
|
32999
33078
|
/**
|
|
33000
33079
|
* Leave arguments at null if not used
|
|
33080
|
+
* @param tableName
|
|
33001
33081
|
* @param selected
|
|
33002
33082
|
* @param filter
|
|
33003
33083
|
* @param sort
|
|
@@ -33006,22 +33086,25 @@ function aggregationToSql(aggregation) {
|
|
|
33006
33086
|
* @param groupBy
|
|
33007
33087
|
* @param aggregations
|
|
33008
33088
|
*/
|
|
33009
|
-
function generateSqlQuery(selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null) {
|
|
33089
|
+
function generateSqlQuery(tableName, selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null, geometryColumn = null) {
|
|
33010
33090
|
let sqlSelect = 'SELECT *';
|
|
33011
|
-
const sqlFrom =
|
|
33091
|
+
const sqlFrom = ` FROM ${tableName}`;
|
|
33012
33092
|
let sqlOrderBy = '';
|
|
33013
33093
|
let sqlWhere = '';
|
|
33014
33094
|
let sqlLimit = '';
|
|
33015
33095
|
let sqlGroupBy = '';
|
|
33016
33096
|
if (selected !== null) {
|
|
33017
|
-
sqlSelect = `SELECT ${selected.map(
|
|
33097
|
+
sqlSelect = `SELECT ${selected.map(fieldToSql).join(', ')}`;
|
|
33098
|
+
}
|
|
33099
|
+
if (geometryColumn !== null) {
|
|
33100
|
+
sqlSelect += `, ST_AsGeoJSON(${fieldToSql(geometryColumn)}) as ${GEOMETRY_COLUMN_ALIAS}`;
|
|
33018
33101
|
}
|
|
33019
33102
|
if (filter !== null) {
|
|
33020
33103
|
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33021
33104
|
}
|
|
33022
33105
|
if (sort?.length) {
|
|
33023
33106
|
sqlOrderBy = ` ORDER BY ${sort
|
|
33024
|
-
.map((sort) =>
|
|
33107
|
+
.map((sort) => `${fieldToSql(sort[1])} ${sort[0].toUpperCase()}`)
|
|
33025
33108
|
.join(', ')}`;
|
|
33026
33109
|
}
|
|
33027
33110
|
if (startIndex !== null && count !== null) {
|
|
@@ -33031,17 +33114,212 @@ function generateSqlQuery(selected = null, filter = null, sort = null, startInde
|
|
|
33031
33114
|
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33032
33115
|
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33033
33116
|
const sqlGroupByFields = groupedByDistinct
|
|
33034
|
-
.map((group) =>
|
|
33117
|
+
.map((group) => fieldToSql(group[1]))
|
|
33035
33118
|
.join(', ');
|
|
33036
33119
|
const sqlGroupBySelect = groupedByDistinct
|
|
33037
|
-
.map((group) =>
|
|
33120
|
+
.map((group) => `${fieldToSql(group[1])} as ${fieldToSql(`distinct(${group[1]})`)}`)
|
|
33038
33121
|
.join(', ');
|
|
33039
33122
|
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33040
33123
|
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33041
33124
|
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33042
33125
|
}
|
|
33043
33126
|
}
|
|
33044
|
-
return sqlSelect + sqlFrom + sqlGroupBy +
|
|
33127
|
+
return sqlSelect + sqlFrom + sqlGroupBy + sqlWhere + sqlOrderBy + sqlLimit;
|
|
33128
|
+
}
|
|
33129
|
+
|
|
33130
|
+
function arrowTableToDataItems(table) {
|
|
33131
|
+
const fields = table.schema.fields;
|
|
33132
|
+
return table.toArray().map((row) => {
|
|
33133
|
+
const rowJson = row.toJSON();
|
|
33134
|
+
const feature = {
|
|
33135
|
+
type: 'Feature',
|
|
33136
|
+
geometry: null,
|
|
33137
|
+
properties: {},
|
|
33138
|
+
};
|
|
33139
|
+
const keys = Object.keys(rowJson);
|
|
33140
|
+
for (let i = 0; i < keys.length; i++) {
|
|
33141
|
+
const key = keys[i];
|
|
33142
|
+
const dataType = fields[i].type;
|
|
33143
|
+
let value = rowJson[key];
|
|
33144
|
+
// this might happen if we get an array inside a field
|
|
33145
|
+
if (value instanceof Vector) {
|
|
33146
|
+
value = Array.from(value);
|
|
33147
|
+
}
|
|
33148
|
+
// cast bigints to ints
|
|
33149
|
+
if (typeof value === 'bigint') {
|
|
33150
|
+
value = Number(value);
|
|
33151
|
+
}
|
|
33152
|
+
// rename columns with empty name
|
|
33153
|
+
if (!key) {
|
|
33154
|
+
feature.properties['unknown'] = value;
|
|
33155
|
+
continue;
|
|
33156
|
+
}
|
|
33157
|
+
// assign properties that look like an id to the geojson `id` field
|
|
33158
|
+
if (/^(object|feature)?_?id$/.test(key.toLowerCase()) &&
|
|
33159
|
+
(typeof value == 'string' || typeof value === 'number')) {
|
|
33160
|
+
feature.id = value;
|
|
33161
|
+
}
|
|
33162
|
+
// if a date is in timestamp (number) format, convert it to native
|
|
33163
|
+
if (typeof value === 'number' &&
|
|
33164
|
+
(DataType.isTimestamp(dataType) || DataType.isDate(dataType))) {
|
|
33165
|
+
value = new Date(value);
|
|
33166
|
+
}
|
|
33167
|
+
// if a binary field (most likely a geometry column): skip
|
|
33168
|
+
if (DataType.isBinary(dataType)) {
|
|
33169
|
+
continue;
|
|
33170
|
+
}
|
|
33171
|
+
// geometry column
|
|
33172
|
+
if (key === GEOMETRY_COLUMN_ALIAS && DataType.isUtf8(dataType)) {
|
|
33173
|
+
feature.geometry = JSON.parse(value);
|
|
33174
|
+
continue;
|
|
33175
|
+
}
|
|
33176
|
+
feature.properties[key] = value;
|
|
33177
|
+
}
|
|
33178
|
+
return feature;
|
|
33179
|
+
});
|
|
33180
|
+
}
|
|
33181
|
+
|
|
33182
|
+
// init code taken from https://github.com/duckdb/duckdb-wasm/blob/main/packages/duckdb-wasm/README.md
|
|
33183
|
+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
|
|
33184
|
+
// from https://duckdb.org/docs/current/sql/data_types/overview
|
|
33185
|
+
const typesMapping = {
|
|
33186
|
+
INTEGER: 'number',
|
|
33187
|
+
SMALLINT: 'number',
|
|
33188
|
+
TINYINT: 'number',
|
|
33189
|
+
BIGINT: 'number',
|
|
33190
|
+
HUGEINT: 'number',
|
|
33191
|
+
UINTEGER: 'number',
|
|
33192
|
+
USMALLINT: 'number',
|
|
33193
|
+
UTINYINT: 'number',
|
|
33194
|
+
UBIGINT: 'number',
|
|
33195
|
+
UHUGEINT: 'number',
|
|
33196
|
+
DOUBLE: 'number',
|
|
33197
|
+
BIGNUM: 'number',
|
|
33198
|
+
DECIMAL: 'number',
|
|
33199
|
+
NUMERIC: 'number',
|
|
33200
|
+
FLOAT: 'number',
|
|
33201
|
+
REAL: 'number',
|
|
33202
|
+
'TIMESTAMP WITH TIME ZONE': 'date',
|
|
33203
|
+
TIMESTAMP: 'date',
|
|
33204
|
+
DATE: 'date',
|
|
33205
|
+
CHAR: 'string',
|
|
33206
|
+
VARCHAR: 'string',
|
|
33207
|
+
TEXT: 'string',
|
|
33208
|
+
UUID: 'string',
|
|
33209
|
+
BLOB: 'string',
|
|
33210
|
+
BIT: 'string',
|
|
33211
|
+
INTERVAL: 'string',
|
|
33212
|
+
LIST: 'string',
|
|
33213
|
+
BOOLEAN: 'boolean',
|
|
33214
|
+
};
|
|
33215
|
+
class Engine {
|
|
33216
|
+
async makeInit() {
|
|
33217
|
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
|
|
33218
|
+
let worker_url = bundle.mainWorker;
|
|
33219
|
+
// this is necessary to let browsers execute WASM code coming from a cross-origin host
|
|
33220
|
+
if (worker_url.startsWith('https://')) {
|
|
33221
|
+
worker_url = URL.createObjectURL(new Blob([`importScripts("${worker_url}");`], {
|
|
33222
|
+
type: 'text/javascript',
|
|
33223
|
+
}));
|
|
33224
|
+
}
|
|
33225
|
+
const worker = new Worker(worker_url);
|
|
33226
|
+
const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
|
|
33227
|
+
this.db = new duckdb.AsyncDuckDB(logger, worker);
|
|
33228
|
+
await this.db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
33229
|
+
// setup duckdb options
|
|
33230
|
+
const conn = await this.db.connect();
|
|
33231
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
33232
|
+
await conn.query(`INSTALL spatial; LOAD spatial;`);
|
|
33233
|
+
await conn.query(`SET TimeZone = '${timezone}';`);
|
|
33234
|
+
await conn.query(`SET threads = 1;`);
|
|
33235
|
+
// await conn.query(`SET memory_limit = '2GB';`) // used for experimenting when we encounter memory issues
|
|
33236
|
+
await conn.query(`SET preserve_insertion_order = false;`); // this reduces memory usage when loading a dataset
|
|
33237
|
+
conn.close();
|
|
33238
|
+
return this;
|
|
33239
|
+
}
|
|
33240
|
+
isReady() {
|
|
33241
|
+
if (!this.init_) {
|
|
33242
|
+
this.init_ = this.makeInit();
|
|
33243
|
+
}
|
|
33244
|
+
return this.init_;
|
|
33245
|
+
}
|
|
33246
|
+
/**
|
|
33247
|
+
* Returns information about a dataset once it's loaded:
|
|
33248
|
+
* - a list of properties description
|
|
33249
|
+
* - the name of the dataset geometry column (null if no geometry present)
|
|
33250
|
+
* @param datasetId name of the table under which the dataset will be stored
|
|
33251
|
+
* @param loadQuery duckdb-specific query for creating a table out of the data
|
|
33252
|
+
* @param forceReload if true, any existing data will be dropped and redownloaded
|
|
33253
|
+
*/
|
|
33254
|
+
async loadFile(datasetId, loadQuery, forceReload = false) {
|
|
33255
|
+
const conn = await this.db.connect();
|
|
33256
|
+
let results;
|
|
33257
|
+
// either we want to recreate the table, or we keep it if it already exists
|
|
33258
|
+
const safeLoadQuery = forceReload
|
|
33259
|
+
? `DROP TABLE IF EXISTS ${datasetId};
|
|
33260
|
+
${loadQuery}`
|
|
33261
|
+
: loadQuery.replace(/CREATE TABLE(?! IF NOT EXISTS)/gi, 'CREATE TABLE IF NOT EXISTS');
|
|
33262
|
+
// create the table
|
|
33263
|
+
try {
|
|
33264
|
+
results = await conn.query(safeLoadQuery);
|
|
33265
|
+
}
|
|
33266
|
+
catch (e) {
|
|
33267
|
+
throw new FetchError('parse', `DuckDB encountered an error when loading the data: ${e.message}`);
|
|
33268
|
+
}
|
|
33269
|
+
// read rows count
|
|
33270
|
+
results = await conn.query(`SELECT count(*) FROM ${datasetId}`);
|
|
33271
|
+
const { 'count_star()': recordsCount } = results.toArray()[0].toJSON();
|
|
33272
|
+
// read columns
|
|
33273
|
+
results = await conn.query(`SELECT * FROM information_schema.columns WHERE table_name = '${datasetId}';`);
|
|
33274
|
+
let geometryColumn = null;
|
|
33275
|
+
const properties = results
|
|
33276
|
+
.toArray()
|
|
33277
|
+
.map((row) => {
|
|
33278
|
+
const rowObj = row.toJSON();
|
|
33279
|
+
if (rowObj['data_type'] === 'GEOMETRY') {
|
|
33280
|
+
// the geometry is not part of the properties
|
|
33281
|
+
// note: right now we only keep one geometry column name, but if there are multiple they will
|
|
33282
|
+
// all get discarded
|
|
33283
|
+
geometryColumn = rowObj['column_name'];
|
|
33284
|
+
return null;
|
|
33285
|
+
}
|
|
33286
|
+
return {
|
|
33287
|
+
name: rowObj['column_name'],
|
|
33288
|
+
label: rowObj['column_name'],
|
|
33289
|
+
type: typesMapping[rowObj['data_type']] ?? 'other',
|
|
33290
|
+
};
|
|
33291
|
+
})
|
|
33292
|
+
.filter((prop) => prop !== null);
|
|
33293
|
+
conn.close();
|
|
33294
|
+
return {
|
|
33295
|
+
properties,
|
|
33296
|
+
geometryColumn,
|
|
33297
|
+
rowsCount: Number(recordsCount),
|
|
33298
|
+
};
|
|
33299
|
+
}
|
|
33300
|
+
// register a Uint8 buffer using a handle in the duckdb instance
|
|
33301
|
+
async registerData(name, buffer) {
|
|
33302
|
+
return this.db.registerFileBuffer(name, buffer);
|
|
33303
|
+
}
|
|
33304
|
+
/**
|
|
33305
|
+
* @param query duckdb-specific query for fetching items
|
|
33306
|
+
*/
|
|
33307
|
+
async queryItems(query) {
|
|
33308
|
+
const conn = await this.db.connect();
|
|
33309
|
+
const results = await conn.query(query);
|
|
33310
|
+
conn.close();
|
|
33311
|
+
return arrowTableToDataItems(results);
|
|
33312
|
+
}
|
|
33313
|
+
close() {
|
|
33314
|
+
this.db?.terminate();
|
|
33315
|
+
}
|
|
33316
|
+
}
|
|
33317
|
+
let engine = null;
|
|
33318
|
+
async function getEngine() {
|
|
33319
|
+
if (!engine) {
|
|
33320
|
+
engine = new Engine();
|
|
33321
|
+
}
|
|
33322
|
+
return engine.isReady();
|
|
33045
33323
|
}
|
|
33046
33324
|
|
|
33047
33325
|
class BaseReader {
|
|
@@ -33054,10 +33332,18 @@ class BaseReader {
|
|
|
33054
33332
|
this.sort = null;
|
|
33055
33333
|
this.startIndex = null;
|
|
33056
33334
|
this.count = null;
|
|
33335
|
+
this.loadPromise_ = Promise.resolve();
|
|
33336
|
+
this.cacheEnabled = false;
|
|
33337
|
+
}
|
|
33338
|
+
enableCache(enabled) {
|
|
33339
|
+
this.cacheEnabled = enabled;
|
|
33057
33340
|
}
|
|
33058
33341
|
load() {
|
|
33059
33342
|
throw new Error('not implemented');
|
|
33060
33343
|
}
|
|
33344
|
+
get isLoaded() {
|
|
33345
|
+
return this.loadPromise_;
|
|
33346
|
+
}
|
|
33061
33347
|
get properties() {
|
|
33062
33348
|
throw new Error('not implemented');
|
|
33063
33349
|
}
|
|
@@ -33106,178 +33392,237 @@ class BaseReader {
|
|
|
33106
33392
|
}
|
|
33107
33393
|
}
|
|
33108
33394
|
|
|
33109
|
-
|
|
33110
|
-
|
|
33111
|
-
|
|
33112
|
-
|
|
33113
|
-
|
|
33114
|
-
|
|
33115
|
-
|
|
33116
|
-
|
|
33117
|
-
|
|
33118
|
-
|
|
33119
|
-
|
|
33120
|
-
|
|
33121
|
-
|
|
33395
|
+
/**
|
|
33396
|
+
* This reader handles file formats supported natively by DuckDB
|
|
33397
|
+
*/
|
|
33398
|
+
class BaseFileReader extends BaseReader {
|
|
33399
|
+
// a table id should not exceed 63 chars
|
|
33400
|
+
generateDatasetId() {
|
|
33401
|
+
// generate a hash out of the url
|
|
33402
|
+
let hash = 0;
|
|
33403
|
+
for (const char of this.url) {
|
|
33404
|
+
hash = (hash << 5) - hash + char.charCodeAt(0);
|
|
33405
|
+
hash = hash >>> 0; // make it unsigned
|
|
33406
|
+
}
|
|
33407
|
+
return `datafetcher_${hash.toString(16)}`;
|
|
33408
|
+
}
|
|
33409
|
+
async getLoadQuery() {
|
|
33122
33410
|
throw new Error('not implemented');
|
|
33123
33411
|
}
|
|
33124
|
-
load() {
|
|
33125
|
-
this.
|
|
33412
|
+
async load() {
|
|
33413
|
+
this.datasetId = this.generateDatasetId();
|
|
33414
|
+
this.loadPromise_ = getEngine()
|
|
33415
|
+
.then((engine) => {
|
|
33416
|
+
this.engine = engine;
|
|
33417
|
+
return this.getLoadQuery();
|
|
33418
|
+
})
|
|
33419
|
+
.then((loadQuery) => this.engine.loadFile(this.datasetId, loadQuery, !this.cacheEnabled))
|
|
33420
|
+
.then((datasetInfo) => {
|
|
33421
|
+
this.properties_ = datasetInfo.properties;
|
|
33422
|
+
this.geometryColumn = datasetInfo.geometryColumn;
|
|
33423
|
+
this.rowsCount = datasetInfo.rowsCount;
|
|
33424
|
+
// returns void for the loaded promise
|
|
33425
|
+
});
|
|
33126
33426
|
}
|
|
33127
33427
|
get properties() {
|
|
33128
|
-
return this.
|
|
33428
|
+
return this.isLoaded.then(() => this.properties_);
|
|
33129
33429
|
}
|
|
33130
33430
|
get info() {
|
|
33131
|
-
return this.
|
|
33132
|
-
itemsCount:
|
|
33431
|
+
return this.isLoaded.then(() => ({
|
|
33432
|
+
itemsCount: this.rowsCount,
|
|
33433
|
+
hasGeometry: !!this.geometryColumn,
|
|
33133
33434
|
}));
|
|
33134
33435
|
}
|
|
33135
33436
|
async read() {
|
|
33136
|
-
|
|
33137
|
-
//
|
|
33138
|
-
|
|
33139
|
-
|
|
33140
|
-
|
|
33141
|
-
this.sort == null &&
|
|
33142
|
-
this.filter == null &&
|
|
33143
|
-
this.startIndex == null &&
|
|
33144
|
-
this.count == null) {
|
|
33145
|
-
return items;
|
|
33146
|
-
}
|
|
33147
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33148
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33149
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33150
|
-
return result.map(jsonToGeojsonFeature);
|
|
33151
|
-
}
|
|
33152
|
-
}
|
|
33153
|
-
|
|
33154
|
-
function parseCsv(text) {
|
|
33155
|
-
// first parse the header to guess the delimiter
|
|
33156
|
-
// note that we do that to not rely on Papaparse logic for guessing delimiter
|
|
33157
|
-
let delimiter;
|
|
33158
|
-
try {
|
|
33159
|
-
const header = text.split('\n')[0];
|
|
33160
|
-
const result = Papa.parse(header, {
|
|
33161
|
-
header: false,
|
|
33162
|
-
});
|
|
33163
|
-
delimiter = result.meta.delimiter;
|
|
33437
|
+
await this.isLoaded;
|
|
33438
|
+
// if only certain fields are selected, omit the geometry
|
|
33439
|
+
const geometryColumn = this.selected === null ? this.geometryColumn : null;
|
|
33440
|
+
const query = generateSqlQuery(this.datasetId, this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations, geometryColumn);
|
|
33441
|
+
return this.engine.queryItems(query);
|
|
33164
33442
|
}
|
|
33165
|
-
catch (e) {
|
|
33166
|
-
throw new Error('CSV parsing failed: the delimiter could not be guessed');
|
|
33167
|
-
}
|
|
33168
|
-
const parsed = Papa.parse(text, {
|
|
33169
|
-
header: true,
|
|
33170
|
-
skipEmptyLines: true,
|
|
33171
|
-
delimiter,
|
|
33172
|
-
});
|
|
33173
|
-
if (parsed.errors.length) {
|
|
33174
|
-
throw new Error('CSV parsing failed for the following reasons:\n' +
|
|
33175
|
-
parsed.errors
|
|
33176
|
-
.map((error) => `* ${error.message} at row ${error.row}, column ${error.index}`)
|
|
33177
|
-
.join('\n'));
|
|
33178
|
-
}
|
|
33179
|
-
const items = parsed.data.map(jsonToGeojsonFeature);
|
|
33180
|
-
return processItemProperties(items, true);
|
|
33181
33443
|
}
|
|
33444
|
+
|
|
33182
33445
|
class CsvReader extends BaseFileReader {
|
|
33183
|
-
|
|
33184
|
-
|
|
33446
|
+
async getLoadQuery() {
|
|
33447
|
+
// first we get a list of columns hich have a detected type of VARCHAR
|
|
33448
|
+
// 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
|
|
33449
|
+
return `
|
|
33450
|
+
SET VARIABLE strColumns = (SELECT list(name) FROM (SELECT unnest(Columns, recursive := true) FROM sniff_csv("${this.url}")) WHERE type = 'VARCHAR');
|
|
33451
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_csv("${this.url}",
|
|
33452
|
+
force_not_null = getvariable('strColumns'),
|
|
33453
|
+
auto_type_candidates = ['NULL', 'BOOLEAN', 'INTEGER', 'DOUBLE', 'DATE', 'VARCHAR']
|
|
33454
|
+
);`;
|
|
33185
33455
|
}
|
|
33186
33456
|
}
|
|
33187
33457
|
|
|
33188
|
-
/**
|
|
33189
|
-
* This parser only supports arrays of simple flat objects with properties
|
|
33190
|
-
* @param text
|
|
33191
|
-
*/
|
|
33192
|
-
function parseJson(text) {
|
|
33193
|
-
const parsed = JSON.parse(text);
|
|
33194
|
-
if (!Array.isArray(parsed)) {
|
|
33195
|
-
throw new Error('Could not parse JSON, expected an array at root level');
|
|
33196
|
-
}
|
|
33197
|
-
return processItemProperties(parsed.map(jsonToGeojsonFeature));
|
|
33198
|
-
}
|
|
33199
33458
|
class JsonReader extends BaseFileReader {
|
|
33200
|
-
|
|
33201
|
-
return
|
|
33459
|
+
async getLoadQuery() {
|
|
33460
|
+
return `
|
|
33461
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_json("${this.url}",
|
|
33462
|
+
maximum_object_size = 536870912 -- 500MB
|
|
33463
|
+
);`;
|
|
33202
33464
|
}
|
|
33203
33465
|
}
|
|
33204
33466
|
|
|
33205
|
-
/**
|
|
33206
|
-
* This parser supports both Geojson Feature collections or arrays
|
|
33207
|
-
* of Features
|
|
33208
|
-
* @param text
|
|
33209
|
-
*/
|
|
33210
|
-
function parseGeojson(text) {
|
|
33211
|
-
const parsed = JSON.parse(text);
|
|
33212
|
-
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33213
|
-
if (!Array.isArray(features)) {
|
|
33214
|
-
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33215
|
-
}
|
|
33216
|
-
return processItemProperties(features);
|
|
33217
|
-
}
|
|
33218
33467
|
class GeojsonReader extends BaseFileReader {
|
|
33219
|
-
|
|
33220
|
-
return
|
|
33468
|
+
async getLoadQuery() {
|
|
33469
|
+
return `
|
|
33470
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33471
|
+
allowed_drivers = ['GeoJSON']
|
|
33472
|
+
);`;
|
|
33221
33473
|
}
|
|
33222
33474
|
}
|
|
33223
33475
|
|
|
33476
|
+
async function inferDatasetType(url, typeHint) {
|
|
33477
|
+
const fileExtensionMatches = new URL(url, typeof window !== 'undefined' ? window.location.toString() : undefined).pathname.match(/\.(.+)$/);
|
|
33478
|
+
const fileExtension = fileExtensionMatches && fileExtensionMatches.length
|
|
33479
|
+
? fileExtensionMatches[1].toLowerCase()
|
|
33480
|
+
: null;
|
|
33481
|
+
// 1. type hint
|
|
33482
|
+
if (typeHint)
|
|
33483
|
+
return Promise.resolve(typeHint);
|
|
33484
|
+
// 2. content-type header
|
|
33485
|
+
const headers = await fetchHeaders(url);
|
|
33486
|
+
if ('supportedType' in headers)
|
|
33487
|
+
return headers.supportedType;
|
|
33488
|
+
// 3. file extension from url
|
|
33489
|
+
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
33490
|
+
return fileExtension;
|
|
33491
|
+
// no type inferred or hinted
|
|
33492
|
+
if ('mimeType' in headers)
|
|
33493
|
+
throw FetchError.unsupportedType(headers.mimeType);
|
|
33494
|
+
else
|
|
33495
|
+
throw FetchError.unknownType();
|
|
33496
|
+
}
|
|
33497
|
+
function fetchHeaders(url) {
|
|
33498
|
+
return sharedFetch(url, 'HEAD')
|
|
33499
|
+
.catch((error) => {
|
|
33500
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33501
|
+
})
|
|
33502
|
+
.then((response) => {
|
|
33503
|
+
if (!response.ok) {
|
|
33504
|
+
throw FetchError.http(response.status);
|
|
33505
|
+
}
|
|
33506
|
+
return parseHeaders(response.headers);
|
|
33507
|
+
});
|
|
33508
|
+
}
|
|
33509
|
+
function fetchDataAsText(url, cacheActive) {
|
|
33510
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33511
|
+
.catch((error) => {
|
|
33512
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33513
|
+
})
|
|
33514
|
+
.then(async (response) => {
|
|
33515
|
+
if (!response.ok) {
|
|
33516
|
+
const clonedResponse = response.clone();
|
|
33517
|
+
throw FetchError.http(response.status, await clonedResponse.text());
|
|
33518
|
+
}
|
|
33519
|
+
const clonedResponse = response.clone();
|
|
33520
|
+
return clonedResponse.text();
|
|
33521
|
+
});
|
|
33522
|
+
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
33523
|
+
}
|
|
33524
|
+
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
33525
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33526
|
+
.catch((error) => {
|
|
33527
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33528
|
+
})
|
|
33529
|
+
.then(async (response) => {
|
|
33530
|
+
if (!response.ok) {
|
|
33531
|
+
throw FetchError.http(response.status, await response.text());
|
|
33532
|
+
}
|
|
33533
|
+
// convert to a numeric array so that we can store the response in cache
|
|
33534
|
+
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
33535
|
+
});
|
|
33536
|
+
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
33537
|
+
return new Uint8Array(array).buffer;
|
|
33538
|
+
});
|
|
33539
|
+
}
|
|
33224
33540
|
/**
|
|
33225
|
-
* This
|
|
33226
|
-
*
|
|
33227
|
-
* @param
|
|
33541
|
+
* This creates a Proxy that allows reading and writing to the data item properties
|
|
33542
|
+
* as if it was a simple array of JSON objects
|
|
33543
|
+
* @param items
|
|
33228
33544
|
*/
|
|
33229
|
-
function
|
|
33230
|
-
return
|
|
33231
|
-
|
|
33232
|
-
|
|
33233
|
-
|
|
33234
|
-
|
|
33235
|
-
|
|
33236
|
-
|
|
33237
|
-
|
|
33545
|
+
function getJsonDataItemsProxy(items) {
|
|
33546
|
+
return new Proxy(items, {
|
|
33547
|
+
get(target, p) {
|
|
33548
|
+
if (typeof p === 'string' &&
|
|
33549
|
+
!Number.isNaN(parseInt(p)) &&
|
|
33550
|
+
target[p]?.properties) {
|
|
33551
|
+
return target[p].properties;
|
|
33552
|
+
}
|
|
33553
|
+
return target[p];
|
|
33554
|
+
},
|
|
33555
|
+
set() {
|
|
33556
|
+
throw new Error('This object is read-only');
|
|
33557
|
+
},
|
|
33238
33558
|
});
|
|
33239
33559
|
}
|
|
33560
|
+
|
|
33240
33561
|
class ExcelReader extends BaseFileReader {
|
|
33241
|
-
|
|
33242
|
-
|
|
33562
|
+
async getLoadQuery() {
|
|
33563
|
+
// we download the file as an array buffer first, in order to be able to check if it's an XLS file
|
|
33564
|
+
let buffer = await fetchDataAsArrayBuffer(this.url, this.cacheEnabled);
|
|
33565
|
+
const bufferHandle = `B${this.datasetId}`;
|
|
33566
|
+
// checking against the magic number at the beginning of XLS files, see https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
33567
|
+
const magicNumber = new Uint8Array(buffer, 0, 8); // first 8 bytes
|
|
33568
|
+
const isXls = Array.from(magicNumber)
|
|
33569
|
+
.map((n) => n.toString(16).toUpperCase())
|
|
33570
|
+
.join(' ') === 'D0 CF 11 E0 A1 B1 1A E1';
|
|
33571
|
+
// uh oh, this is an XLS file (not supported by duckdb); convert it to CSV using the xlsx package
|
|
33572
|
+
if (isXls) {
|
|
33573
|
+
buffer = await import('xlsx').then(({ read, utils }) => {
|
|
33574
|
+
const workbook = read(buffer);
|
|
33575
|
+
const json = utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
|
|
33576
|
+
return new TextEncoder().encode(JSON.stringify(json)).buffer;
|
|
33577
|
+
});
|
|
33578
|
+
}
|
|
33579
|
+
const duckDbFn = isXls ? 'read_json' : 'read_xlsx';
|
|
33580
|
+
await this.engine.registerData(bufferHandle, new Uint8Array(buffer));
|
|
33581
|
+
return `
|
|
33582
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM ${duckDbFn}("${bufferHandle}", ignore_errors = true);`;
|
|
33243
33583
|
}
|
|
33244
33584
|
}
|
|
33245
33585
|
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
|
|
33586
|
+
class GmlReader extends BaseFileReader {
|
|
33587
|
+
async getLoadQuery() {
|
|
33588
|
+
return `
|
|
33589
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33590
|
+
allowed_drivers = ['GML']
|
|
33591
|
+
);`;
|
|
33592
|
+
}
|
|
33593
|
+
}
|
|
33594
|
+
|
|
33595
|
+
const formatGeojson = new GeoJSON$1();
|
|
33596
|
+
function parseGeojson(text) {
|
|
33597
|
+
const parsed = JSON.parse(text);
|
|
33598
|
+
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33599
|
+
if (!Array.isArray(features)) {
|
|
33600
|
+
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33601
|
+
}
|
|
33602
|
+
return features;
|
|
33603
|
+
}
|
|
33604
|
+
function parseGml(text, featureType, version) {
|
|
33605
|
+
const parts = featureType.split(':');
|
|
33606
|
+
const regex = new RegExp(`xmlns:${parts[0]}=["']([^'"]*)["']`);
|
|
33249
33607
|
const match = regex.exec(text);
|
|
33250
33608
|
if (match && match.length >= 2) {
|
|
33251
|
-
const
|
|
33609
|
+
const wfs = new WFS({
|
|
33252
33610
|
featureNS: match[1],
|
|
33253
|
-
featureType:
|
|
33611
|
+
featureType: parts[1],
|
|
33254
33612
|
version: version,
|
|
33255
33613
|
});
|
|
33256
33614
|
let features;
|
|
33257
33615
|
try {
|
|
33258
|
-
features =
|
|
33616
|
+
features = wfs.readFeatures(text);
|
|
33259
33617
|
}
|
|
33260
33618
|
catch (e) {
|
|
33261
|
-
throw Error(
|
|
33619
|
+
throw Error(`Couldn't parse WFS with GML features: ${e.message}`);
|
|
33262
33620
|
}
|
|
33263
|
-
const geojsonItem =
|
|
33264
|
-
return
|
|
33621
|
+
const geojsonItem = formatGeojson.writeFeaturesObject(features);
|
|
33622
|
+
return geojsonItem.features;
|
|
33265
33623
|
}
|
|
33266
33624
|
throw Error("Couldn't retrieve namespace url");
|
|
33267
33625
|
}
|
|
33268
|
-
class GmlReader extends BaseFileReader {
|
|
33269
|
-
constructor(url, namespace, version, cacheActive = true) {
|
|
33270
|
-
super(url);
|
|
33271
|
-
this.url = url;
|
|
33272
|
-
this.namespace = namespace;
|
|
33273
|
-
this.version = version;
|
|
33274
|
-
this.cacheActive = cacheActive;
|
|
33275
|
-
}
|
|
33276
|
-
getData() {
|
|
33277
|
-
return fetchDataAsText(this.url, this.cacheActive).then((text) => parseGml(text, this.namespace, this.version));
|
|
33278
|
-
}
|
|
33279
|
-
}
|
|
33280
|
-
|
|
33281
33626
|
async function getWfsEndpoint(wfsUrl) {
|
|
33282
33627
|
try {
|
|
33283
33628
|
return await new WfsEndpoint(wfsUrl).isReady();
|
|
@@ -33305,21 +33650,65 @@ async function getWfsEndpoint(wfsUrl) {
|
|
|
33305
33650
|
}
|
|
33306
33651
|
}
|
|
33307
33652
|
}
|
|
33308
|
-
class WfsReader extends
|
|
33309
|
-
constructor(url,
|
|
33310
|
-
super(url
|
|
33311
|
-
this.endpoint =
|
|
33312
|
-
this.
|
|
33313
|
-
|
|
33653
|
+
class WfsReader extends BaseReader {
|
|
33654
|
+
constructor(url, featureTypeName) {
|
|
33655
|
+
super(url);
|
|
33656
|
+
this.endpoint = getWfsEndpoint(url);
|
|
33657
|
+
this.featureType = this.endpoint
|
|
33658
|
+
.then((endpoint) => {
|
|
33659
|
+
const featureTypes = endpoint.getFeatureTypes();
|
|
33660
|
+
return endpoint.getFeatureTypeFull(featureTypes.length === 1 && !featureTypeName
|
|
33661
|
+
? featureTypes[0].name
|
|
33662
|
+
: featureTypeName);
|
|
33663
|
+
})
|
|
33664
|
+
.then((featureType) => {
|
|
33665
|
+
if (!featureType) {
|
|
33666
|
+
throw new Error('wfs.featuretype.notfound');
|
|
33667
|
+
}
|
|
33668
|
+
return featureType;
|
|
33669
|
+
});
|
|
33670
|
+
}
|
|
33671
|
+
get backupReader() {
|
|
33672
|
+
if (this.backupReader_) {
|
|
33673
|
+
return this.backupReader_;
|
|
33674
|
+
}
|
|
33675
|
+
this.backupReader_ = Promise.all([this.endpoint, this.featureType]).then(([endpoint, featureType]) => {
|
|
33676
|
+
let reader;
|
|
33677
|
+
if (endpoint.supportsJson(featureType.name)) {
|
|
33678
|
+
reader = new GeojsonReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33679
|
+
asJson: true,
|
|
33680
|
+
outputCrs: 'EPSG:4326',
|
|
33681
|
+
}));
|
|
33682
|
+
}
|
|
33683
|
+
else {
|
|
33684
|
+
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33685
|
+
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33686
|
+
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33687
|
+
reader = new GmlReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33688
|
+
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33689
|
+
outputCrs: 'EPSG:4326',
|
|
33690
|
+
}));
|
|
33691
|
+
}
|
|
33692
|
+
}
|
|
33693
|
+
reader.enableCache(this.cacheEnabled);
|
|
33694
|
+
reader.load();
|
|
33695
|
+
return reader;
|
|
33696
|
+
});
|
|
33697
|
+
return this.backupReader_;
|
|
33314
33698
|
}
|
|
33315
33699
|
get properties() {
|
|
33316
|
-
return this.
|
|
33317
|
-
.getFeatureTypeFull(this.featureTypeName)
|
|
33318
|
-
.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33700
|
+
return this.featureType.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33319
33701
|
const originalType = featureType.properties[prop];
|
|
33320
|
-
|
|
33321
|
-
|
|
33322
|
-
|
|
33702
|
+
let type;
|
|
33703
|
+
if (originalType === 'float' || originalType === 'integer') {
|
|
33704
|
+
type = 'number';
|
|
33705
|
+
}
|
|
33706
|
+
else if (originalType === 'boolean') {
|
|
33707
|
+
type = 'string'; // we don't handle booleans yet in the data fetcher
|
|
33708
|
+
}
|
|
33709
|
+
else {
|
|
33710
|
+
type = originalType;
|
|
33711
|
+
}
|
|
33323
33712
|
return {
|
|
33324
33713
|
name: prop,
|
|
33325
33714
|
label: prop,
|
|
@@ -33328,83 +33717,63 @@ class WfsReader extends BaseCacheReader {
|
|
|
33328
33717
|
}));
|
|
33329
33718
|
}
|
|
33330
33719
|
get info() {
|
|
33331
|
-
return this.
|
|
33720
|
+
return this.featureType.then((result) => ({
|
|
33332
33721
|
itemsCount: result.objectCount,
|
|
33722
|
+
hasGeometry: !!result.geometryName,
|
|
33333
33723
|
}));
|
|
33334
33724
|
}
|
|
33335
|
-
|
|
33336
|
-
|
|
33337
|
-
const featureTypes = wfsEndpoint.getFeatureTypes();
|
|
33338
|
-
const featureType = wfsEndpoint.getFeatureTypeSummary(featureTypes.length === 1 && !featureTypeName
|
|
33339
|
-
? featureTypes[0].name
|
|
33340
|
-
: featureTypeName);
|
|
33341
|
-
if (!featureType) {
|
|
33342
|
-
throw new Error('wfs.featuretype.notfound');
|
|
33343
|
-
}
|
|
33344
|
-
if (wfsEndpoint.supportsStartIndex()) {
|
|
33345
|
-
return new WfsReader(wfsUrlEndpoint, wfsEndpoint, featureType.name);
|
|
33346
|
-
}
|
|
33347
|
-
else if (wfsEndpoint.supportsJson(featureType.name)) {
|
|
33348
|
-
return new GeojsonReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33349
|
-
asJson: true,
|
|
33350
|
-
outputCrs: 'EPSG:4326',
|
|
33351
|
-
}));
|
|
33352
|
-
}
|
|
33353
|
-
else {
|
|
33354
|
-
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33355
|
-
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33356
|
-
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33357
|
-
return new GmlReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33358
|
-
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33359
|
-
outputCrs: 'EPSG:4326',
|
|
33360
|
-
}), featureType.name, wfsEndpoint.getVersion());
|
|
33361
|
-
}
|
|
33362
|
-
throw new Error('wfs.geojsongml.notsupported');
|
|
33363
|
-
}
|
|
33725
|
+
load() {
|
|
33726
|
+
// Nothing to load for Wfs
|
|
33364
33727
|
}
|
|
33365
|
-
async
|
|
33366
|
-
|
|
33367
|
-
|
|
33728
|
+
async read() {
|
|
33729
|
+
const endpoint = await this.endpoint;
|
|
33730
|
+
const featureType = await this.featureType;
|
|
33731
|
+
// if we can't use the WFS protocol we fall back to the backup reader
|
|
33732
|
+
if (this.aggregations || this.groupedBy || !endpoint.supportsStartIndex()) {
|
|
33733
|
+
const backupReader = await this.backupReader;
|
|
33734
|
+
backupReader.selectAll();
|
|
33735
|
+
if (this.selected) {
|
|
33736
|
+
backupReader.select(...this.selected);
|
|
33737
|
+
}
|
|
33738
|
+
if (this.aggregations) {
|
|
33739
|
+
backupReader.aggregate(...this.aggregations);
|
|
33740
|
+
}
|
|
33741
|
+
if (this.groupedBy) {
|
|
33742
|
+
backupReader.groupBy(...this.groupedBy);
|
|
33743
|
+
}
|
|
33744
|
+
if (this.sort) {
|
|
33745
|
+
backupReader.orderBy(...this.sort);
|
|
33746
|
+
}
|
|
33747
|
+
if (this.startIndex !== null && this.count !== null) {
|
|
33748
|
+
backupReader.limit(this.startIndex, this.count);
|
|
33749
|
+
}
|
|
33750
|
+
return backupReader.read();
|
|
33368
33751
|
}
|
|
33369
|
-
const asJson =
|
|
33752
|
+
const asJson = endpoint.supportsJson(featureType.name);
|
|
33370
33753
|
const attributes = this.selected ?? undefined;
|
|
33371
|
-
let
|
|
33754
|
+
let sortBy = null;
|
|
33755
|
+
if (this.sort) {
|
|
33756
|
+
const mapSort = (s) => [s[0] === 'desc' ? 'D' : 'A', s[1]];
|
|
33757
|
+
sortBy = Array.isArray(this.sort[0])
|
|
33758
|
+
? this.sort.map(mapSort)
|
|
33759
|
+
: mapSort(this.sort);
|
|
33760
|
+
}
|
|
33761
|
+
const url = endpoint.getFeatureUrl(featureType.name, {
|
|
33372
33762
|
...(this.startIndex !== null && { startIndex: this.startIndex }),
|
|
33373
33763
|
...(this.count !== null && { maxFeatures: this.count }),
|
|
33374
33764
|
asJson,
|
|
33375
33765
|
outputCrs: 'EPSG:4326',
|
|
33376
33766
|
attributes,
|
|
33377
|
-
|
|
33767
|
+
sortBy,
|
|
33378
33768
|
});
|
|
33379
|
-
|
|
33380
|
-
const finalUrl = new URL(url);
|
|
33381
|
-
const sorts = this.sort
|
|
33382
|
-
.map((fieldSort) => `${fieldSort[1]}+${fieldSort[0] === 'asc' ? 'A' : 'D'}`)
|
|
33383
|
-
.join(',');
|
|
33384
|
-
// Direct update on string url to prevent encoding of +A and +D
|
|
33385
|
-
url = `${url}${finalUrl.search ? '&' : ''}SORTBY=${sorts}`;
|
|
33386
|
-
}
|
|
33387
|
-
return fetchDataAsText(url, this.cacheActive).then((text) => asJson
|
|
33769
|
+
return fetchDataAsText(url, this.cacheEnabled).then((text) => asJson
|
|
33388
33770
|
? parseGeojson(text)
|
|
33389
|
-
: parseGml(text,
|
|
33390
|
-
}
|
|
33391
|
-
async getQueryData() {
|
|
33392
|
-
const items = (await this.getData()).items;
|
|
33393
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33394
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33395
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33396
|
-
return result.map(jsonToGeojsonFeature);
|
|
33397
|
-
}
|
|
33398
|
-
load() {
|
|
33399
|
-
// Nothing to load for Wfs
|
|
33400
|
-
}
|
|
33401
|
-
async read() {
|
|
33402
|
-
return (await this.getData(this.aggregations, this.groupedBy)).items;
|
|
33771
|
+
: parseGml(text, featureType.name, endpoint.getVersion()));
|
|
33403
33772
|
}
|
|
33404
33773
|
}
|
|
33405
33774
|
|
|
33406
|
-
async function openDataset(url,
|
|
33407
|
-
const fileType = await inferDatasetType(url, typeHint);
|
|
33775
|
+
async function openDataset(url, options) {
|
|
33776
|
+
const fileType = await inferDatasetType(url, options?.typeHint);
|
|
33408
33777
|
let reader;
|
|
33409
33778
|
try {
|
|
33410
33779
|
switch (fileType) {
|
|
@@ -33421,18 +33790,18 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33421
33790
|
reader = new ExcelReader(url);
|
|
33422
33791
|
break;
|
|
33423
33792
|
case 'gml':
|
|
33424
|
-
reader = new GmlReader(url
|
|
33793
|
+
reader = new GmlReader(url);
|
|
33425
33794
|
break;
|
|
33426
33795
|
case 'wfs':
|
|
33427
|
-
reader =
|
|
33796
|
+
reader = new WfsReader(url, options?.wfsFeatureType);
|
|
33428
33797
|
break;
|
|
33429
33798
|
}
|
|
33430
|
-
reader.
|
|
33799
|
+
reader.enableCache(options?.enableCache ?? true);
|
|
33431
33800
|
reader.load();
|
|
33432
33801
|
return reader;
|
|
33433
33802
|
}
|
|
33434
33803
|
catch (e) {
|
|
33435
|
-
//WfsReader may already raise a FetchError
|
|
33804
|
+
// WfsReader may already raise a FetchError
|
|
33436
33805
|
if (e instanceof FetchError)
|
|
33437
33806
|
throw e;
|
|
33438
33807
|
else
|
|
@@ -33443,13 +33812,13 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33443
33812
|
* This fetches the full dataset at the given URL and parses it according to its mime type.
|
|
33444
33813
|
* All items in the dataset are converted to GeoJSON features, even if they do not bear any spatial geometry.
|
|
33445
33814
|
* File type can be either inferred (from the HTTP headers or the URL), or hinted using the 2nd argument
|
|
33446
|
-
* File type is determined
|
|
33815
|
+
* File type is determined like so:
|
|
33447
33816
|
* 1. if a type hint is given, use it
|
|
33448
33817
|
* 2. otherwise, look for a Content-Type header in the response with a supported mime type
|
|
33449
33818
|
* 3. if no valid mime type was found, look for an explicit file extension in the url (.csv, .geojson etc.)
|
|
33450
33819
|
*/
|
|
33451
|
-
async function readDataset(url,
|
|
33452
|
-
const reader = await openDataset(url,
|
|
33820
|
+
async function readDataset(url, options) {
|
|
33821
|
+
const reader = await openDataset(url, options);
|
|
33453
33822
|
try {
|
|
33454
33823
|
return await reader.read();
|
|
33455
33824
|
}
|
|
@@ -33680,9 +34049,11 @@ class DataService {
|
|
|
33680
34049
|
getDataset(link, cacheActive) {
|
|
33681
34050
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
33682
34051
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString());
|
|
33683
|
-
return from(openDataset(wfsUrlEndpoint,
|
|
34052
|
+
return from(openDataset(wfsUrlEndpoint, {
|
|
34053
|
+
typeHint: 'wfs',
|
|
33684
34054
|
wfsFeatureType: link.name,
|
|
33685
|
-
|
|
34055
|
+
enableCache: cacheActive,
|
|
34056
|
+
}));
|
|
33686
34057
|
}
|
|
33687
34058
|
else if (link.type === 'download') {
|
|
33688
34059
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString());
|
|
@@ -33690,12 +34061,15 @@ class DataService {
|
|
|
33690
34061
|
const supportedType = SupportedTypes.indexOf(format) > -1
|
|
33691
34062
|
? format
|
|
33692
34063
|
: undefined;
|
|
33693
|
-
return from(openDataset(linkProxifiedUrl,
|
|
34064
|
+
return from(openDataset(linkProxifiedUrl, {
|
|
34065
|
+
typeHint: supportedType,
|
|
34066
|
+
enableCache: cacheActive,
|
|
34067
|
+
})).pipe();
|
|
33694
34068
|
}
|
|
33695
34069
|
else if (link.type === 'service' &&
|
|
33696
34070
|
link.accessServiceProtocol === 'esriRest') {
|
|
33697
34071
|
const url = this.getDownloadUrlFromEsriRest(link.url.toString(), 'geojson');
|
|
33698
|
-
return from(openDataset(url, 'geojson',
|
|
34072
|
+
return from(openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })).pipe();
|
|
33699
34073
|
}
|
|
33700
34074
|
else if (link.type === 'service' &&
|
|
33701
34075
|
link.accessServiceProtocol === 'ogcFeatures') {
|
|
@@ -33709,7 +34083,10 @@ class DataService {
|
|
|
33709
34083
|
}
|
|
33710
34084
|
const urlWithoutLimit = new URL(geojsonUrl);
|
|
33711
34085
|
urlWithoutLimit.searchParams.delete('limit');
|
|
33712
|
-
return openDataset(urlWithoutLimit.toString(),
|
|
34086
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
34087
|
+
typeHint: 'geojson',
|
|
34088
|
+
enableCache: cacheActive,
|
|
34089
|
+
});
|
|
33713
34090
|
}));
|
|
33714
34091
|
}
|
|
33715
34092
|
return throwError(() => 'protocol not supported');
|
|
@@ -33961,20 +34338,31 @@ class DataTableComponent {
|
|
|
33961
34338
|
this.eltRef = inject(ElementRef);
|
|
33962
34339
|
this.cdr = inject(ChangeDetectorRef);
|
|
33963
34340
|
this.translateService = inject(TranslateService);
|
|
33964
|
-
this.
|
|
34341
|
+
this.columnsFromFeatureCatalog = null;
|
|
34342
|
+
this.columnsFromDataset = [];
|
|
33965
34343
|
this.selected = new EventEmitter();
|
|
33966
|
-
this.properties$ = new BehaviorSubject(null);
|
|
33967
34344
|
this.loading$ = new BehaviorSubject(false);
|
|
33968
34345
|
this.error = null;
|
|
33969
34346
|
}
|
|
33970
34347
|
set featureAttributes(value) {
|
|
33971
|
-
this.
|
|
33972
|
-
|
|
34348
|
+
this.columnsFromFeatureCatalog = value.map((attrs) => ({
|
|
34349
|
+
name: attrs.value,
|
|
34350
|
+
label: attrs.label,
|
|
34351
|
+
}));
|
|
33973
34352
|
}
|
|
33974
34353
|
set dataset(value) {
|
|
33975
34354
|
this.dataset_ = value;
|
|
33976
34355
|
this.dataset_.load();
|
|
33977
|
-
this.dataset_.info.then((info) =>
|
|
34356
|
+
this.dataset_.info.then((info) => {
|
|
34357
|
+
this.count = info.itemsCount;
|
|
34358
|
+
this.cdr.detectChanges();
|
|
34359
|
+
});
|
|
34360
|
+
}
|
|
34361
|
+
get columns() {
|
|
34362
|
+
return this.columnsFromFeatureCatalog ?? this.columnsFromDataset;
|
|
34363
|
+
}
|
|
34364
|
+
get columnNames() {
|
|
34365
|
+
return this.columns.map((c) => c.name);
|
|
33978
34366
|
}
|
|
33979
34367
|
ngOnInit() {
|
|
33980
34368
|
this.dataSource = new DataTableDataSource();
|
|
@@ -34009,11 +34397,12 @@ class DataTableComponent {
|
|
|
34009
34397
|
}
|
|
34010
34398
|
async readData() {
|
|
34011
34399
|
this.loading$.next(true);
|
|
34012
|
-
// wait for properties to be read
|
|
34013
|
-
const properties = await firstValueFrom(this.properties$.pipe(filter((p) => !!p)));
|
|
34014
|
-
const propsWithoutGeom = properties.filter((p) => !p.toLowerCase().startsWith('geom'));
|
|
34015
|
-
this.dataset_.select(...propsWithoutGeom);
|
|
34016
34400
|
try {
|
|
34401
|
+
// wait for properties to be read
|
|
34402
|
+
if (!this.columnsFromFeatureCatalog) {
|
|
34403
|
+
this.columnsFromDataset = await this.dataset_.properties;
|
|
34404
|
+
}
|
|
34405
|
+
this.dataset_.select(...this.columnNames);
|
|
34017
34406
|
await this.dataSource.showData(this.dataset_.read());
|
|
34018
34407
|
this.error = null;
|
|
34019
34408
|
}
|
|
@@ -34043,7 +34432,7 @@ class DataTableComponent {
|
|
|
34043
34432
|
}
|
|
34044
34433
|
}
|
|
34045
34434
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
34046
|
-
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
|
|
34435
|
+
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 }); }
|
|
34047
34436
|
}
|
|
34048
34437
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, decorators: [{
|
|
34049
34438
|
type: Component,
|
|
@@ -34055,10 +34444,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
34055
34444
|
CommonModule,
|
|
34056
34445
|
LoadingMaskComponent,
|
|
34057
34446
|
PopupAlertComponent,
|
|
34058
|
-
LetDirective,
|
|
34059
34447
|
TranslatePipe,
|
|
34060
34448
|
TranslateDirective,
|
|
34061
|
-
], 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
|
|
34449
|
+
], 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"] }]
|
|
34062
34450
|
}], propDecorators: { featureAttributes: [{
|
|
34063
34451
|
type: Input
|
|
34064
34452
|
}], dataset: [{
|
|
@@ -34161,7 +34549,7 @@ class ChartViewComponent {
|
|
|
34161
34549
|
}), shareReplay$1(1));
|
|
34162
34550
|
this.properties$ = combineLatest([this.dataset$, this.featureCatalog$]).pipe(switchMap$1(([dataset, catalog]) => this.setProperties(dataset, catalog)), shareReplay$1(1));
|
|
34163
34551
|
this.yChoices$ = this.properties$.pipe(map$1((properties) => properties
|
|
34164
|
-
.filter((prop) => prop.type === 'number'
|
|
34552
|
+
.filter((prop) => prop.type === 'number')
|
|
34165
34553
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))), tap$1((choices) => {
|
|
34166
34554
|
if (!choices.find((choice) => choice.value === this.yProperty$.value)) {
|
|
34167
34555
|
const newProp = choices[0]?.value || '';
|
|
@@ -34188,6 +34576,7 @@ class ChartViewComponent {
|
|
|
34188
34576
|
this.aggregation$,
|
|
34189
34577
|
]).pipe(filter$1(([_, x, y]) => !!x || !!y), switchMap$1(([dataset, xProp, yProp, aggregation]) => {
|
|
34190
34578
|
const fieldAgg = aggregation === 'count' ? ['count'] : [aggregation, yProp];
|
|
34579
|
+
this.loading = true;
|
|
34191
34580
|
return dataset
|
|
34192
34581
|
.groupBy(['distinct', xProp])
|
|
34193
34582
|
.aggregate(fieldAgg)
|
|
@@ -34429,6 +34818,7 @@ class GeoTableViewComponent {
|
|
|
34429
34818
|
}));
|
|
34430
34819
|
}
|
|
34431
34820
|
async initMapContext() {
|
|
34821
|
+
this.dataset.load();
|
|
34432
34822
|
this.dataset.selectAll();
|
|
34433
34823
|
return {
|
|
34434
34824
|
layers: [
|
|
@@ -34602,8 +34992,7 @@ class MdViewFacade {
|
|
|
34602
34992
|
}));
|
|
34603
34993
|
this.error$ = this.store.pipe(select(getMetadataError));
|
|
34604
34994
|
this.related$ = this.store.pipe(select(getRelated));
|
|
34605
|
-
this.
|
|
34606
|
-
this.sourceOf$ = this.store.pipe(select(getSourceOf));
|
|
34995
|
+
this.linkedRecords$ = this.store.pipe(select(getLinkedRecords));
|
|
34607
34996
|
this.chartConfig$ = this.store.pipe(select(getChartConfig));
|
|
34608
34997
|
this.allLinks$ = this.metadata$.pipe(map$1((record) => 'onlineResources' in record ? record.onlineResources : []), shareReplay$1(1));
|
|
34609
34998
|
this.resourceDoi$ = this.metadata$.pipe(map$1((record) => {
|
|
@@ -34722,12 +35111,9 @@ class MdViewEffects {
|
|
|
34722
35111
|
this.loadRelatedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSimilarRecords(full)), map$1((related) => {
|
|
34723
35112
|
return setRelated({ related });
|
|
34724
35113
|
}), catchError(() => of(setRelated({ related: null })))));
|
|
34725
|
-
this.
|
|
34726
|
-
return
|
|
34727
|
-
}), catchError(() => of(
|
|
34728
|
-
this.loadSourceOf$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSourceOf(full)), map$1((sourceOf) => {
|
|
34729
|
-
return setSourceOf({ sourceOf });
|
|
34730
|
-
}), catchError(() => of(setSourceOf({ sourceOf: null })))));
|
|
35114
|
+
this.loadLinkedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getLinkedRecords(full)), map$1((linkedRecords) => {
|
|
35115
|
+
return setLinkedRecords({ linkedRecords });
|
|
35116
|
+
}), catchError(() => of(setLinkedRecords({ linkedRecords: null })))));
|
|
34731
35117
|
/*
|
|
34732
35118
|
UserFeedback effects
|
|
34733
35119
|
*/
|
|
@@ -39946,7 +40332,7 @@ class RouterService {
|
|
|
39946
40332
|
return ROUTER_ROUTE_ORGANIZATION;
|
|
39947
40333
|
}
|
|
39948
40334
|
getDefaultSort() {
|
|
39949
|
-
return SortByEnum.
|
|
40335
|
+
return SortByEnum.RESOURCE_DATE;
|
|
39950
40336
|
}
|
|
39951
40337
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: RouterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
39952
40338
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: RouterService, providedIn: 'root' }); }
|
|
@@ -40363,5 +40749,5 @@ const CHART_TYPE_VALUES = [
|
|
|
40363
40749
|
* Generated bundle index. Do not edit.
|
|
40364
40750
|
*/
|
|
40365
40751
|
|
|
40366
|
-
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 };
|
|
40752
|
+
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, SpatialExtentDropdownComponent, SpatialExtentSearchField, 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, isFileExtensionValid, 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, readFileAsText, 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 };
|
|
40367
40753
|
//# sourceMappingURL=geonetwork-ui.mjs.map
|