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
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { DataItem, PropertyInfo } from '../model'
|
|
2
|
-
import {
|
|
3
|
-
fetchDataAsArrayBuffer,
|
|
4
|
-
jsonToGeojsonFeature,
|
|
5
|
-
processItemProperties,
|
|
6
|
-
} from '../utils'
|
|
7
1
|
import { BaseFileReader } from './base-file'
|
|
2
|
+
import { fetchDataAsArrayBuffer } from '../utils'
|
|
8
3
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
4
|
+
export class ExcelReader extends BaseFileReader {
|
|
5
|
+
protected async getLoadQuery(): Promise<string> {
|
|
6
|
+
// we download the file as an array buffer first, in order to be able to check if it's an XLS file
|
|
7
|
+
let buffer = await fetchDataAsArrayBuffer(this.url, this.cacheEnabled)
|
|
8
|
+
const bufferHandle = `B${this.datasetId}`
|
|
9
|
+
|
|
10
|
+
// checking against the magic number at the beginning of XLS files, see https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
11
|
+
const magicNumber = new Uint8Array(buffer, 0, 8) // first 8 bytes
|
|
12
|
+
const isXls =
|
|
13
|
+
Array.from(magicNumber)
|
|
14
|
+
.map((n) => n.toString(16).toUpperCase())
|
|
15
|
+
.join(' ') === 'D0 CF 11 E0 A1 B1 1A E1'
|
|
16
|
+
|
|
17
|
+
// uh oh, this is an XLS file (not supported by duckdb); convert it to CSV using the xlsx package
|
|
18
|
+
if (isXls) {
|
|
19
|
+
buffer = await import('xlsx').then(({ read, utils }) => {
|
|
20
|
+
const workbook = read(buffer)
|
|
21
|
+
const json = utils.sheet_to_json(
|
|
22
|
+
workbook.Sheets[workbook.SheetNames[0]]
|
|
23
|
+
)
|
|
24
|
+
return new TextEncoder().encode(JSON.stringify(json)).buffer
|
|
25
|
+
})
|
|
24
26
|
}
|
|
25
|
-
|
|
26
|
-
})
|
|
27
|
-
}
|
|
27
|
+
const duckDbFn = isXls ? 'read_json' : 'read_xlsx'
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
await this.engine.registerData(bufferHandle, new Uint8Array(buffer))
|
|
30
|
+
return `
|
|
31
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM ${duckDbFn}("${bufferHandle}", ignore_errors = true);`
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -1,29 +1,10 @@
|
|
|
1
|
-
import { DataItem, PropertyInfo } from '../model'
|
|
2
|
-
import { fetchDataAsText, processItemProperties } from '../utils'
|
|
3
1
|
import { BaseFileReader } from './base-file'
|
|
4
2
|
|
|
5
|
-
/**
|
|
6
|
-
* This parser supports both Geojson Feature collections or arrays
|
|
7
|
-
* of Features
|
|
8
|
-
* @param text
|
|
9
|
-
*/
|
|
10
|
-
export function parseGeojson(text: string): {
|
|
11
|
-
items: DataItem[]
|
|
12
|
-
properties: PropertyInfo[]
|
|
13
|
-
} {
|
|
14
|
-
const parsed = JSON.parse(text)
|
|
15
|
-
const features =
|
|
16
|
-
parsed.type === 'FeatureCollection' ? parsed.features : parsed
|
|
17
|
-
if (!Array.isArray(features)) {
|
|
18
|
-
throw new Error(
|
|
19
|
-
'Could not parse GeoJSON, expected a features collection or an array of features at root level'
|
|
20
|
-
)
|
|
21
|
-
}
|
|
22
|
-
return processItemProperties(features)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
3
|
export class GeojsonReader extends BaseFileReader {
|
|
26
|
-
|
|
27
|
-
return
|
|
4
|
+
async getLoadQuery() {
|
|
5
|
+
return `
|
|
6
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
7
|
+
allowed_drivers = ['GeoJSON']
|
|
8
|
+
);`
|
|
28
9
|
}
|
|
29
10
|
}
|
|
@@ -1,54 +1,10 @@
|
|
|
1
1
|
import { BaseFileReader } from './base-file'
|
|
2
|
-
import { DataItem, PropertyInfo } from '../model'
|
|
3
|
-
import { fetchDataAsText, processItemProperties } from '../utils'
|
|
4
|
-
import { GeoJSON, WFS } from 'ol/format.js'
|
|
5
|
-
import { WfsVersion } from '@camptocamp/ogc-client'
|
|
6
|
-
|
|
7
|
-
export function parseGml(
|
|
8
|
-
text: string,
|
|
9
|
-
namespace: string,
|
|
10
|
-
version: string
|
|
11
|
-
): {
|
|
12
|
-
items: DataItem[]
|
|
13
|
-
properties: PropertyInfo[]
|
|
14
|
-
} {
|
|
15
|
-
const splittedNamespace = namespace.split(':')
|
|
16
|
-
const regex = new RegExp(`xmlns:${splittedNamespace[0]}=["']([^'"]*)["']`)
|
|
17
|
-
const match = regex.exec(text)
|
|
18
|
-
|
|
19
|
-
if (match && match.length >= 2) {
|
|
20
|
-
const wf = new WFS({
|
|
21
|
-
featureNS: match[1],
|
|
22
|
-
featureType: splittedNamespace[1],
|
|
23
|
-
version: version,
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
let features
|
|
27
|
-
try {
|
|
28
|
-
features = wf.readFeatures(text)
|
|
29
|
-
} catch (e) {
|
|
30
|
-
throw Error("Couldn't parse WFS with GML features")
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const geojsonItem = new GeoJSON().writeFeaturesObject(features)
|
|
34
|
-
return processItemProperties(geojsonItem.features, true)
|
|
35
|
-
}
|
|
36
|
-
throw Error("Couldn't retrieve namespace url")
|
|
37
|
-
}
|
|
38
2
|
|
|
39
3
|
export class GmlReader extends BaseFileReader {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
) {
|
|
46
|
-
super(url)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
protected getData() {
|
|
50
|
-
return fetchDataAsText(this.url, this.cacheActive).then((text) =>
|
|
51
|
-
parseGml(text, this.namespace, this.version)
|
|
52
|
-
)
|
|
4
|
+
async getLoadQuery() {
|
|
5
|
+
return `
|
|
6
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
7
|
+
allowed_drivers = ['GML']
|
|
8
|
+
);`
|
|
53
9
|
}
|
|
54
10
|
}
|
|
@@ -1,28 +1,10 @@
|
|
|
1
|
-
import { DataItem, PropertyInfo } from '../model'
|
|
2
|
-
import {
|
|
3
|
-
fetchDataAsText,
|
|
4
|
-
jsonToGeojsonFeature,
|
|
5
|
-
processItemProperties,
|
|
6
|
-
} from '../utils'
|
|
7
1
|
import { BaseFileReader } from './base-file'
|
|
8
2
|
|
|
9
|
-
/**
|
|
10
|
-
* This parser only supports arrays of simple flat objects with properties
|
|
11
|
-
* @param text
|
|
12
|
-
*/
|
|
13
|
-
export function parseJson(text: string): {
|
|
14
|
-
items: DataItem[]
|
|
15
|
-
properties: PropertyInfo[]
|
|
16
|
-
} {
|
|
17
|
-
const parsed = JSON.parse(text) as any[]
|
|
18
|
-
if (!Array.isArray(parsed)) {
|
|
19
|
-
throw new Error('Could not parse JSON, expected an array at root level')
|
|
20
|
-
}
|
|
21
|
-
return processItemProperties(parsed.map(jsonToGeojsonFeature))
|
|
22
|
-
}
|
|
23
|
-
|
|
24
3
|
export class JsonReader extends BaseFileReader {
|
|
25
|
-
|
|
26
|
-
return
|
|
4
|
+
async getLoadQuery() {
|
|
5
|
+
return `
|
|
6
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_json("${this.url}",
|
|
7
|
+
maximum_object_size = 536870912 -- 500MB
|
|
8
|
+
);`
|
|
27
9
|
}
|
|
28
10
|
}
|
|
@@ -1,14 +1,58 @@
|
|
|
1
|
-
import { EndpointError, WfsEndpoint, WfsVersion } from '@camptocamp/ogc-client'
|
|
2
|
-
import { DataItem, DatasetInfo, FetchError, PropertyInfo } from '../model'
|
|
3
1
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from '
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
2
|
+
EndpointError,
|
|
3
|
+
WfsEndpoint,
|
|
4
|
+
WfsFeatureTypeFull,
|
|
5
|
+
} from '@camptocamp/ogc-client'
|
|
6
|
+
import { DataItem, DatasetInfo, FetchError, PropertyInfo } from '../model'
|
|
7
|
+
import { fetchDataAsText } from '../utils'
|
|
8
|
+
import WFS from 'ol/format/WFS'
|
|
9
|
+
import GeoJSON from 'ol/format/GeoJSON'
|
|
10
|
+
import Feature from 'ol/Feature'
|
|
11
|
+
import { GeojsonReader } from './geojson'
|
|
12
|
+
import { GmlReader } from './gml'
|
|
13
|
+
import { BaseReader } from './base'
|
|
14
|
+
|
|
15
|
+
const formatGeojson = new GeoJSON()
|
|
16
|
+
|
|
17
|
+
export function parseGeojson(text: string): DataItem[] {
|
|
18
|
+
const parsed = JSON.parse(text)
|
|
19
|
+
const features =
|
|
20
|
+
parsed.type === 'FeatureCollection' ? parsed.features : parsed
|
|
21
|
+
if (!Array.isArray(features)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
'Could not parse GeoJSON, expected a features collection or an array of features at root level'
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
return features
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function parseGml(
|
|
30
|
+
text: string,
|
|
31
|
+
featureType: string,
|
|
32
|
+
version: string
|
|
33
|
+
): DataItem[] {
|
|
34
|
+
const parts = featureType.split(':')
|
|
35
|
+
const regex = new RegExp(`xmlns:${parts[0]}=["']([^'"]*)["']`)
|
|
36
|
+
const match = regex.exec(text)
|
|
37
|
+
if (match && match.length >= 2) {
|
|
38
|
+
const wfs = new WFS({
|
|
39
|
+
featureNS: match[1],
|
|
40
|
+
featureType: parts[1],
|
|
41
|
+
version: version,
|
|
42
|
+
})
|
|
43
|
+
let features: Feature[]
|
|
44
|
+
try {
|
|
45
|
+
features = wfs.readFeatures(text)
|
|
46
|
+
} catch (e: unknown) {
|
|
47
|
+
throw Error(
|
|
48
|
+
`Couldn't parse WFS with GML features: ${(e as Error).message}`
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
const geojsonItem = formatGeojson.writeFeaturesObject(features)
|
|
52
|
+
return geojsonItem.features
|
|
53
|
+
}
|
|
54
|
+
throw Error("Couldn't retrieve namespace url")
|
|
55
|
+
}
|
|
12
56
|
|
|
13
57
|
export async function getWfsEndpoint(wfsUrl: string): Promise<WfsEndpoint> {
|
|
14
58
|
try {
|
|
@@ -36,151 +80,163 @@ export async function getWfsEndpoint(wfsUrl: string): Promise<WfsEndpoint> {
|
|
|
36
80
|
}
|
|
37
81
|
}
|
|
38
82
|
|
|
39
|
-
export class WfsReader extends
|
|
40
|
-
endpoint: WfsEndpoint
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
this.
|
|
83
|
+
export class WfsReader extends BaseReader {
|
|
84
|
+
endpoint: Promise<WfsEndpoint>
|
|
85
|
+
featureType: Promise<WfsFeatureTypeFull>
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* This dataset reader will read the whole data as geojson or gml instead of
|
|
89
|
+
* using the WFS protocol; it is used in the following situations:
|
|
90
|
+
* - the endpoint does not support pagination
|
|
91
|
+
* - aggregations or grouping are set on the dataset (i.e. for chart view)
|
|
92
|
+
*/
|
|
93
|
+
backupReader_: Promise<BaseReader>
|
|
94
|
+
|
|
95
|
+
constructor(url: string, featureTypeName: string) {
|
|
96
|
+
super(url)
|
|
97
|
+
this.endpoint = getWfsEndpoint(url)
|
|
98
|
+
|
|
99
|
+
this.featureType = this.endpoint
|
|
100
|
+
.then((endpoint) => {
|
|
101
|
+
const featureTypes = endpoint.getFeatureTypes()
|
|
102
|
+
return endpoint.getFeatureTypeFull(
|
|
103
|
+
featureTypes.length === 1 && !featureTypeName
|
|
104
|
+
? featureTypes[0].name
|
|
105
|
+
: featureTypeName
|
|
106
|
+
)
|
|
107
|
+
})
|
|
108
|
+
.then((featureType) => {
|
|
109
|
+
if (!featureType) {
|
|
110
|
+
throw new Error('wfs.featuretype.notfound')
|
|
111
|
+
}
|
|
112
|
+
return featureType
|
|
113
|
+
})
|
|
54
114
|
}
|
|
55
115
|
|
|
56
|
-
get
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
116
|
+
get backupReader(): Promise<BaseReader> {
|
|
117
|
+
if (this.backupReader_) {
|
|
118
|
+
return this.backupReader_
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.backupReader_ = Promise.all([this.endpoint, this.featureType]).then(
|
|
122
|
+
([endpoint, featureType]) => {
|
|
123
|
+
let reader: BaseReader
|
|
124
|
+
if (endpoint.supportsJson(featureType.name)) {
|
|
125
|
+
reader = new GeojsonReader(
|
|
126
|
+
endpoint.getFeatureUrl(featureType.name, {
|
|
127
|
+
asJson: true,
|
|
128
|
+
outputCrs: 'EPSG:4326',
|
|
129
|
+
})
|
|
130
|
+
)
|
|
131
|
+
} else {
|
|
132
|
+
if (
|
|
133
|
+
featureType.outputFormats.find((f) =>
|
|
134
|
+
f.toLowerCase().includes('gml')
|
|
135
|
+
) &&
|
|
136
|
+
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
137
|
+
featureType.otherCrs?.includes('EPSG:4326'))
|
|
138
|
+
) {
|
|
139
|
+
reader = new GmlReader(
|
|
140
|
+
endpoint.getFeatureUrl(featureType.name, {
|
|
141
|
+
outputFormat: featureType.outputFormats.find((f) =>
|
|
142
|
+
f.toLowerCase().includes('gml')
|
|
143
|
+
),
|
|
144
|
+
outputCrs: 'EPSG:4326',
|
|
145
|
+
})
|
|
146
|
+
)
|
|
70
147
|
}
|
|
71
|
-
}
|
|
72
|
-
|
|
148
|
+
}
|
|
149
|
+
reader.enableCache(this.cacheEnabled)
|
|
150
|
+
reader.load()
|
|
151
|
+
return reader
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
return this.backupReader_
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
get properties(): Promise<PropertyInfo[]> {
|
|
158
|
+
return this.featureType.then((featureType) =>
|
|
159
|
+
Object.keys(featureType.properties).map((prop) => {
|
|
160
|
+
const originalType = featureType.properties[prop]
|
|
161
|
+
let type: PropertyInfo['type']
|
|
162
|
+
if (originalType === 'float' || originalType === 'integer') {
|
|
163
|
+
type = 'number'
|
|
164
|
+
} else if (originalType === 'boolean') {
|
|
165
|
+
type = 'string' // we don't handle booleans yet in the data fetcher
|
|
166
|
+
} else {
|
|
167
|
+
type = originalType
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
name: prop,
|
|
171
|
+
label: prop,
|
|
172
|
+
type,
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
)
|
|
73
176
|
}
|
|
74
177
|
|
|
75
178
|
get info(): Promise<DatasetInfo> {
|
|
76
|
-
return this.
|
|
179
|
+
return this.featureType.then(
|
|
77
180
|
(result) =>
|
|
78
181
|
({
|
|
79
182
|
itemsCount: result.objectCount,
|
|
183
|
+
hasGeometry: !!result.geometryName,
|
|
80
184
|
}) as DatasetInfo
|
|
81
185
|
)
|
|
82
186
|
}
|
|
83
187
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const featureType = wfsEndpoint.getFeatureTypeSummary(
|
|
88
|
-
featureTypes.length === 1 && !featureTypeName
|
|
89
|
-
? featureTypes[0].name
|
|
90
|
-
: featureTypeName
|
|
91
|
-
)
|
|
92
|
-
if (!featureType) {
|
|
93
|
-
throw new Error('wfs.featuretype.notfound')
|
|
94
|
-
}
|
|
188
|
+
load() {
|
|
189
|
+
// Nothing to load for Wfs
|
|
190
|
+
}
|
|
95
191
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
outputFormat: featureType.outputFormats.find((f) =>
|
|
116
|
-
f.toLowerCase().includes('gml')
|
|
117
|
-
),
|
|
118
|
-
outputCrs: 'EPSG:4326',
|
|
119
|
-
}),
|
|
120
|
-
featureType.name,
|
|
121
|
-
wfsEndpoint.getVersion()
|
|
122
|
-
)
|
|
192
|
+
async read(): Promise<DataItem[]> {
|
|
193
|
+
const endpoint = await this.endpoint
|
|
194
|
+
const featureType = await this.featureType
|
|
195
|
+
|
|
196
|
+
// if we can't use the WFS protocol we fall back to the backup reader
|
|
197
|
+
if (this.aggregations || this.groupedBy || !endpoint.supportsStartIndex()) {
|
|
198
|
+
const backupReader = await this.backupReader
|
|
199
|
+
backupReader.selectAll()
|
|
200
|
+
if (this.selected) {
|
|
201
|
+
backupReader.select(...this.selected)
|
|
202
|
+
}
|
|
203
|
+
if (this.aggregations) {
|
|
204
|
+
backupReader.aggregate(...this.aggregations)
|
|
205
|
+
}
|
|
206
|
+
if (this.groupedBy) {
|
|
207
|
+
backupReader.groupBy(...this.groupedBy)
|
|
208
|
+
}
|
|
209
|
+
if (this.sort) {
|
|
210
|
+
backupReader.orderBy(...this.sort)
|
|
123
211
|
}
|
|
124
|
-
|
|
212
|
+
if (this.startIndex !== null && this.count !== null) {
|
|
213
|
+
backupReader.limit(this.startIndex, this.count)
|
|
214
|
+
}
|
|
215
|
+
return backupReader.read()
|
|
125
216
|
}
|
|
126
|
-
}
|
|
127
217
|
|
|
128
|
-
|
|
129
|
-
if (aggregation || groupedBy) {
|
|
130
|
-
return { items: await this.getQueryData() }
|
|
131
|
-
}
|
|
132
|
-
const asJson = this.endpoint.supportsJson(this.featureTypeName)
|
|
218
|
+
const asJson = endpoint.supportsJson(featureType.name)
|
|
133
219
|
const attributes = this.selected ?? undefined
|
|
134
|
-
let
|
|
220
|
+
let sortBy = null
|
|
221
|
+
if (this.sort) {
|
|
222
|
+
const mapSort = (s) => [s[0] === 'desc' ? 'D' : 'A', s[1]]
|
|
223
|
+
sortBy = Array.isArray(this.sort[0])
|
|
224
|
+
? this.sort.map(mapSort)
|
|
225
|
+
: mapSort(this.sort)
|
|
226
|
+
}
|
|
227
|
+
const url = endpoint.getFeatureUrl(featureType.name, {
|
|
135
228
|
...(this.startIndex !== null && { startIndex: this.startIndex }),
|
|
136
229
|
...(this.count !== null && { maxFeatures: this.count }),
|
|
137
230
|
asJson,
|
|
138
231
|
outputCrs: 'EPSG:4326',
|
|
139
232
|
attributes,
|
|
140
|
-
|
|
233
|
+
sortBy,
|
|
141
234
|
})
|
|
142
235
|
|
|
143
|
-
|
|
144
|
-
const finalUrl = new URL(url)
|
|
145
|
-
const sorts = this.sort
|
|
146
|
-
.map(
|
|
147
|
-
(fieldSort) => `${fieldSort[1]}+${fieldSort[0] === 'asc' ? 'A' : 'D'}`
|
|
148
|
-
)
|
|
149
|
-
.join(',')
|
|
150
|
-
// Direct update on string url to prevent encoding of +A and +D
|
|
151
|
-
url = `${url}${finalUrl.search ? '&' : ''}SORTBY=${sorts}`
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return fetchDataAsText(url, this.cacheActive).then((text) =>
|
|
236
|
+
return fetchDataAsText(url, this.cacheEnabled).then((text) =>
|
|
155
237
|
asJson
|
|
156
238
|
? parseGeojson(text)
|
|
157
|
-
: parseGml(text,
|
|
158
|
-
)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
public async getQueryData() {
|
|
162
|
-
const items = (await this.getData()).items
|
|
163
|
-
const jsonItems = getJsonDataItemsProxy(items)
|
|
164
|
-
const query = generateSqlQuery(
|
|
165
|
-
this.selected,
|
|
166
|
-
this.filter,
|
|
167
|
-
this.sort,
|
|
168
|
-
this.startIndex,
|
|
169
|
-
this.count,
|
|
170
|
-
this.groupedBy,
|
|
171
|
-
this.aggregations
|
|
172
|
-
)
|
|
173
|
-
const result: any[] = await import('alasql').then((module) =>
|
|
174
|
-
module.default(query, [jsonItems])
|
|
239
|
+
: parseGml(text, featureType.name, endpoint.getVersion())
|
|
175
240
|
)
|
|
176
|
-
return result.map(jsonToGeojsonFeature)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
load() {
|
|
180
|
-
// Nothing to load for Wfs
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async read(): Promise<DataItem[]> {
|
|
184
|
-
return (await this.getData(this.aggregations, this.groupedBy)).items
|
|
185
241
|
}
|
|
186
242
|
}
|