@loaders.gl/flatgeobuf 4.3.0-alpha.2 → 4.3.0-alpha.4
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/dist/dist.dev.js +53 -24
- package/dist/dist.min.js +2 -2
- package/dist/flatgeobuf-loader.d.ts +8 -4
- package/dist/flatgeobuf-loader.d.ts.map +1 -1
- package/dist/flatgeobuf-loader.js +24 -4
- package/dist/flatgeobuf-worker.js +54 -25
- package/dist/floatgeobuf-source.d.ts +51 -0
- package/dist/floatgeobuf-source.d.ts.map +1 -0
- package/dist/floatgeobuf-source.js +72 -0
- package/dist/index.cjs +57 -27
- package/dist/index.cjs.map +2 -2
- package/dist/lib/parse-flatgeobuf.d.ts +11 -3
- package/dist/lib/parse-flatgeobuf.d.ts.map +1 -1
- package/dist/lib/parse-flatgeobuf.js +39 -29
- package/package.json +5 -5
- package/src/flatgeobuf-loader.ts +35 -5
- package/src/floatgeobuf-source.ts +100 -0
- package/src/lib/parse-flatgeobuf.ts +61 -40
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import type { FlatGeobufLoaderOptions } from "../flatgeobuf-loader.js";
|
|
2
1
|
import type { Table } from '@loaders.gl/schema';
|
|
3
2
|
import * as fgb from '../flatgeobuf/3.27.2';
|
|
4
|
-
export
|
|
5
|
-
|
|
3
|
+
export type ParseFlatGeobufOptions = {
|
|
4
|
+
shape?: 'geojson-table' | 'columnar-table' | 'binary';
|
|
5
|
+
/** If supplied, only loads features within the bounding box */
|
|
6
|
+
boundingBox?: [[number, number], [number, number]];
|
|
7
|
+
/** Desired output CRS */
|
|
8
|
+
crs?: string;
|
|
9
|
+
/** Should geometries be reprojected to target CRS */
|
|
10
|
+
reproject?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function parseFlatGeobuf(arrayBuffer: ArrayBuffer, options: ParseFlatGeobufOptions): Table;
|
|
13
|
+
export declare function parseFlatGeobufInBatches(stream: any, options: ParseFlatGeobufOptions): any[] | AsyncGenerator<fgb.IFeature, any, unknown> | AsyncGenerator<any, void, unknown>;
|
|
6
14
|
//# sourceMappingURL=parse-flatgeobuf.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-flatgeobuf.d.ts","sourceRoot":"","sources":["../../src/lib/parse-flatgeobuf.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"parse-flatgeobuf.d.ts","sourceRoot":"","sources":["../../src/lib/parse-flatgeobuf.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAe,KAAK,EAAS,MAAM,oBAAoB,CAAC;AAKpE,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAC;AAS5C,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,CAAC,EAAE,eAAe,GAAG,gBAAgB,GAAG,QAAQ,CAAC;IACtD,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,yBAAyB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAQF,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,KAAK,CAoBhG;AA2DD,wBAAgB,wBAAwB,CAAC,MAAM,KAAA,EAAE,OAAO,EAAE,sBAAsB,2FAU/E"}
|
|
@@ -10,21 +10,6 @@ import * as generic from "../flatgeobuf/3.27.2/generic.js";
|
|
|
10
10
|
import { parseProperties as parsePropertiesBinary } from "../flatgeobuf/3.27.2/generic/feature.js";
|
|
11
11
|
const deserializeGeoJson = geojson.deserialize;
|
|
12
12
|
const deserializeGeneric = generic.deserialize;
|
|
13
|
-
// const parsePropertiesBinary = FlatgeobufFeature.parseProperties;
|
|
14
|
-
// TODO: reproject binary features
|
|
15
|
-
function binaryFromFeature(feature, header) {
|
|
16
|
-
const geometry = feature.geometry();
|
|
17
|
-
// FlatGeobuf files can only hold a single geometry type per file, otherwise
|
|
18
|
-
// GeometryType is GeometryCollection
|
|
19
|
-
// I believe geometry.type() is null (0) except when the geometry type isn't
|
|
20
|
-
// known in the header?
|
|
21
|
-
const geometryType = header.geometryType || geometry?.type();
|
|
22
|
-
const parsedGeometry = fgbToBinaryGeometry(geometry, geometryType);
|
|
23
|
-
// @ts-expect-error this looks wrong
|
|
24
|
-
parsedGeometry.properties = parsePropertiesBinary(feature, header.columns);
|
|
25
|
-
// TODO: wrap binary data either in points, lines, or polygons key
|
|
26
|
-
return parsedGeometry;
|
|
27
|
-
}
|
|
28
13
|
/*
|
|
29
14
|
* Parse FlatGeobuf arrayBuffer and return GeoJSON.
|
|
30
15
|
*
|
|
@@ -32,7 +17,7 @@ function binaryFromFeature(feature, header) {
|
|
|
32
17
|
* @return A GeoJSON geometry object
|
|
33
18
|
*/
|
|
34
19
|
export function parseFlatGeobuf(arrayBuffer, options) {
|
|
35
|
-
const shape = options
|
|
20
|
+
const shape = options.shape;
|
|
36
21
|
switch (shape) {
|
|
37
22
|
case 'geojson-table': {
|
|
38
23
|
return parseFlatGeobufToGeoJSONTable(arrayBuffer, options);
|
|
@@ -55,27 +40,28 @@ function parseFlatGeobufToBinary(arrayBuffer, options = {}) {
|
|
|
55
40
|
// @ts-expect-error
|
|
56
41
|
return deserializeGeneric(array, fgbToBinaryGeometry);
|
|
57
42
|
}
|
|
58
|
-
function parseFlatGeobufToGeoJSONTable(arrayBuffer, options
|
|
43
|
+
function parseFlatGeobufToGeoJSONTable(arrayBuffer, options) {
|
|
59
44
|
if (arrayBuffer.byteLength === 0) {
|
|
60
45
|
return { shape: 'geojson-table', type: 'FeatureCollection', features: [] };
|
|
61
46
|
}
|
|
62
|
-
const { reproject = false,
|
|
47
|
+
const { reproject = false, crs = 'WGS84' } = options;
|
|
63
48
|
const arr = new Uint8Array(arrayBuffer);
|
|
64
49
|
let fgbHeader;
|
|
65
50
|
let schema;
|
|
51
|
+
const rect = options.boundingBox && convertBoundingBox(options.boundingBox);
|
|
66
52
|
// @ts-expect-error this looks wrong
|
|
67
|
-
let { features } = deserializeGeoJson(arr,
|
|
53
|
+
let { features } = deserializeGeoJson(arr, rect, (headerMeta) => {
|
|
68
54
|
fgbHeader = headerMeta;
|
|
69
55
|
schema = getSchemaFromFGBHeader(fgbHeader);
|
|
70
56
|
});
|
|
71
|
-
const
|
|
57
|
+
const fromCRS = fgbHeader?.crs?.wkt;
|
|
72
58
|
let projection;
|
|
73
|
-
if (reproject &&
|
|
59
|
+
if (reproject && fromCRS) {
|
|
74
60
|
// Constructing the projection may fail for some invalid WKT strings
|
|
75
61
|
try {
|
|
76
|
-
projection = new Proj4Projection({ from:
|
|
62
|
+
projection = new Proj4Projection({ from: fromCRS, to: crs });
|
|
77
63
|
}
|
|
78
|
-
catch (
|
|
64
|
+
catch (error) {
|
|
79
65
|
// no op
|
|
80
66
|
}
|
|
81
67
|
}
|
|
@@ -92,7 +78,7 @@ function parseFlatGeobufToGeoJSONTable(arrayBuffer, options = {}) {
|
|
|
92
78
|
*/
|
|
93
79
|
// eslint-disable-next-line complexity
|
|
94
80
|
export function parseFlatGeobufInBatches(stream, options) {
|
|
95
|
-
const shape = options.
|
|
81
|
+
const shape = options.shape;
|
|
96
82
|
switch (shape) {
|
|
97
83
|
case 'binary':
|
|
98
84
|
return parseFlatGeobufInBatchesToBinary(stream, options);
|
|
@@ -105,8 +91,9 @@ export function parseFlatGeobufInBatches(stream, options) {
|
|
|
105
91
|
function parseFlatGeobufInBatchesToBinary(stream, options) {
|
|
106
92
|
// TODO: reproject binary streaming features
|
|
107
93
|
// const {reproject = false, _targetCrs = 'WGS84'} = (options && options.gis) || {};
|
|
94
|
+
const rect = options.boundingBox && convertBoundingBox(options.boundingBox);
|
|
108
95
|
// @ts-expect-error
|
|
109
|
-
const iterator = deserializeGeneric(stream, binaryFromFeature);
|
|
96
|
+
const iterator = deserializeGeneric(stream, binaryFromFeature, rect);
|
|
110
97
|
return iterator;
|
|
111
98
|
}
|
|
112
99
|
/**
|
|
@@ -116,7 +103,7 @@ function parseFlatGeobufInBatchesToBinary(stream, options) {
|
|
|
116
103
|
*/
|
|
117
104
|
// eslint-disable-next-line complexity
|
|
118
105
|
async function* parseFlatGeobufInBatchesToGeoJSON(stream, options) {
|
|
119
|
-
const { reproject = false,
|
|
106
|
+
const { reproject = false, crs = 'WGS84' } = options || {};
|
|
120
107
|
let fgbHeader;
|
|
121
108
|
// let schema: Schema | undefined;
|
|
122
109
|
const iterator = deserializeGeoJson(stream, undefined, (headerMeta) => {
|
|
@@ -128,9 +115,9 @@ async function* parseFlatGeobufInBatchesToGeoJSON(stream, options) {
|
|
|
128
115
|
// @ts-expect-error this looks wrong
|
|
129
116
|
for await (const feature of iterator) {
|
|
130
117
|
if (firstRecord) {
|
|
131
|
-
const
|
|
132
|
-
if (reproject &&
|
|
133
|
-
projection = new Proj4Projection({ from:
|
|
118
|
+
const fromCRS = fgbHeader?.crs?.wkt;
|
|
119
|
+
if (reproject && fromCRS) {
|
|
120
|
+
projection = new Proj4Projection({ from: fromCRS, to: crs });
|
|
134
121
|
}
|
|
135
122
|
firstRecord = false;
|
|
136
123
|
}
|
|
@@ -143,3 +130,26 @@ async function* parseFlatGeobufInBatchesToGeoJSON(stream, options) {
|
|
|
143
130
|
}
|
|
144
131
|
}
|
|
145
132
|
}
|
|
133
|
+
// HELPERS
|
|
134
|
+
function convertBoundingBox(boundingBox) {
|
|
135
|
+
return {
|
|
136
|
+
minX: boundingBox[0][0],
|
|
137
|
+
minY: boundingBox[0][1],
|
|
138
|
+
maxX: boundingBox[1][0],
|
|
139
|
+
maxY: boundingBox[1][1]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
// TODO: reproject binary features
|
|
143
|
+
function binaryFromFeature(feature, header) {
|
|
144
|
+
const geometry = feature.geometry();
|
|
145
|
+
// FlatGeobuf files can only hold a single geometry type per file, otherwise
|
|
146
|
+
// GeometryType is GeometryCollection
|
|
147
|
+
// I believe geometry.type() is null (0) except when the geometry type isn't
|
|
148
|
+
// known in the header?
|
|
149
|
+
const geometryType = header.geometryType || geometry?.type();
|
|
150
|
+
const parsedGeometry = fgbToBinaryGeometry(geometry, geometryType);
|
|
151
|
+
// @ts-expect-error this looks wrong
|
|
152
|
+
parsedGeometry.properties = parsePropertiesBinary(feature, header.columns);
|
|
153
|
+
// TODO: wrap binary data either in points, lines, or polygons key
|
|
154
|
+
return parsedGeometry;
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loaders.gl/flatgeobuf",
|
|
3
3
|
"description": "Loader for FlatGeobuf",
|
|
4
|
-
"version": "4.3.0-alpha.
|
|
4
|
+
"version": "4.3.0-alpha.4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"publishConfig": {
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"build-worker": "esbuild src/workers/flatgeobuf-worker.ts --bundle --outfile=dist/flatgeobuf-worker.js --define:__VERSION__=\\\"$npm_package_version\\\""
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@loaders.gl/gis": "4.3.0-alpha.
|
|
48
|
-
"@loaders.gl/loader-utils": "4.3.0-alpha.
|
|
49
|
-
"@loaders.gl/schema": "4.3.0-alpha.
|
|
47
|
+
"@loaders.gl/gis": "4.3.0-alpha.4",
|
|
48
|
+
"@loaders.gl/loader-utils": "4.3.0-alpha.4",
|
|
49
|
+
"@loaders.gl/schema": "4.3.0-alpha.4",
|
|
50
50
|
"@math.gl/proj4": "^4.0.0",
|
|
51
51
|
"flatgeobuf": "3.27.2"
|
|
52
52
|
},
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@loaders.gl/core": "^4.0.0"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "4900ac4c4de20366c050b80cef04dc5b52d167af"
|
|
60
60
|
}
|
package/src/flatgeobuf-loader.ts
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
+
import type {GeoJSONTable, BinaryFeatureCollection} from '@loaders.gl/schema';
|
|
5
6
|
import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
parseFlatGeobuf,
|
|
9
|
+
parseFlatGeobufInBatches,
|
|
10
|
+
ParseFlatGeobufOptions
|
|
11
|
+
} from './lib/parse-flatgeobuf';
|
|
7
12
|
|
|
8
13
|
// __VERSION__ is injected by babel-plugin-version-inline
|
|
9
14
|
// @ts-ignore TS2304: Cannot find name '__VERSION__'.
|
|
@@ -17,6 +22,7 @@ export type FlatGeobufLoaderOptions = LoaderOptions & {
|
|
|
17
22
|
shape?: 'geojson-table' | 'columnar-table' | 'binary';
|
|
18
23
|
/** Override the URL to the worker bundle (by default loads from unpkg.com) */
|
|
19
24
|
workerUrl?: string;
|
|
25
|
+
boundingBox?: [[number, number], [number, number]];
|
|
20
26
|
};
|
|
21
27
|
gis?: {
|
|
22
28
|
reproject?: boolean;
|
|
@@ -24,6 +30,7 @@ export type FlatGeobufLoaderOptions = LoaderOptions & {
|
|
|
24
30
|
};
|
|
25
31
|
};
|
|
26
32
|
|
|
33
|
+
/** Load flatgeobuf on a worker */
|
|
27
34
|
export const FlatGeobufWorkerLoader = {
|
|
28
35
|
dataType: null as any,
|
|
29
36
|
batchType: null as any,
|
|
@@ -45,13 +52,36 @@ export const FlatGeobufWorkerLoader = {
|
|
|
45
52
|
reproject: false
|
|
46
53
|
}
|
|
47
54
|
}
|
|
48
|
-
} as const satisfies Loader<
|
|
55
|
+
} as const satisfies Loader<GeoJSONTable | BinaryFeatureCollection, any, FlatGeobufLoaderOptions>;
|
|
49
56
|
|
|
50
57
|
export const FlatGeobufLoader = {
|
|
51
58
|
...FlatGeobufWorkerLoader,
|
|
52
|
-
parse: async (arrayBuffer, options
|
|
53
|
-
|
|
59
|
+
parse: async (arrayBuffer: ArrayBuffer, options: FlatGeobufLoaderOptions = {}) =>
|
|
60
|
+
parseSync(arrayBuffer, options),
|
|
61
|
+
parseSync,
|
|
54
62
|
// @ts-expect-error this is a stream parser not an async iterator parser
|
|
55
|
-
parseInBatchesFromStream
|
|
63
|
+
parseInBatchesFromStream,
|
|
56
64
|
binary: true
|
|
57
65
|
} as const satisfies LoaderWithParser<any, any, FlatGeobufLoaderOptions>;
|
|
66
|
+
|
|
67
|
+
function parseSync(arrayBuffer: ArrayBuffer, options: FlatGeobufLoaderOptions = {}) {
|
|
68
|
+
return parseFlatGeobuf(arrayBuffer, getOptions(options));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function parseInBatchesFromStream(stream: any, options: FlatGeobufLoaderOptions) {
|
|
72
|
+
return parseFlatGeobufInBatches(stream, getOptions(options));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getOptions(options: FlatGeobufLoaderOptions): ParseFlatGeobufOptions {
|
|
76
|
+
options = {
|
|
77
|
+
...options,
|
|
78
|
+
flatgeobuf: {...FlatGeobufLoader.options.flatgeobuf, ...options?.flatgeobuf},
|
|
79
|
+
gis: {...FlatGeobufLoader.options.gis, ...options?.gis}
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
shape: options?.flatgeobuf?.shape ?? 'geojson-table',
|
|
83
|
+
boundingBox: options?.flatgeobuf?.boundingBox,
|
|
84
|
+
crs: options?.gis?._targetCrs || 'WGS84',
|
|
85
|
+
reproject: options?.gis?.reproject || false
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Schema, GeoJSONTable} from '@loaders.gl/schema';
|
|
6
|
+
import type {
|
|
7
|
+
VectorSourceProps,
|
|
8
|
+
VectorSourceMetadata,
|
|
9
|
+
GetFeaturesParameters,
|
|
10
|
+
LoaderWithParser
|
|
11
|
+
} from '@loaders.gl/loader-utils';
|
|
12
|
+
import {Source, VectorSource} from '@loaders.gl/loader-utils';
|
|
13
|
+
|
|
14
|
+
import {FlatGeobufLoader} from './flatgeobuf-loader';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @ndeprecated This is a WIP, not fully implemented
|
|
18
|
+
* @see https://developers.arcgis.com/rest/services-reference/enterprise/feature-service.htm
|
|
19
|
+
*/
|
|
20
|
+
export const FlatGeobufSource = {
|
|
21
|
+
name: 'FlatGeobuf',
|
|
22
|
+
id: 'flatgeobuf-server',
|
|
23
|
+
module: 'wms',
|
|
24
|
+
version: '0.0.0',
|
|
25
|
+
extensions: [],
|
|
26
|
+
mimeTypes: [],
|
|
27
|
+
options: {
|
|
28
|
+
url: undefined!,
|
|
29
|
+
'flatgeobuf-server': {
|
|
30
|
+
/** Tabular loaders, normally the GeoJSONLoader */
|
|
31
|
+
loaders: []
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
type: 'flatgeobuf-server',
|
|
36
|
+
fromUrl: true,
|
|
37
|
+
fromBlob: false, // TODO check if supported by library?
|
|
38
|
+
|
|
39
|
+
testURL: (url: string): boolean => url.toLowerCase().includes('FeatureServer'),
|
|
40
|
+
createDataSource: (url, props: FlatGeobufVectorSourceProps): FlatGeobufVectorSource =>
|
|
41
|
+
new FlatGeobufVectorSource(props)
|
|
42
|
+
} as const satisfies Source<FlatGeobufVectorSource, FlatGeobufVectorSourceProps>;
|
|
43
|
+
|
|
44
|
+
export type FlatGeobufVectorSourceProps = VectorSourceProps & {
|
|
45
|
+
url: string;
|
|
46
|
+
'flatgeobuf-server'?: {
|
|
47
|
+
loaders: LoaderWithParser[];
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* ArcGIS ImageServer
|
|
53
|
+
* Note - exports a big API, that could be exposed here if there is a use case
|
|
54
|
+
* @see https://developers.arcgis.com/rest/services-reference/enterprise/feature-service.htm
|
|
55
|
+
*/
|
|
56
|
+
export class FlatGeobufVectorSource extends VectorSource<FlatGeobufVectorSourceProps> {
|
|
57
|
+
data: string;
|
|
58
|
+
url: string;
|
|
59
|
+
protected formatSpecificMetadata: Promise<any> | null = null;
|
|
60
|
+
|
|
61
|
+
constructor(props: FlatGeobufVectorSourceProps) {
|
|
62
|
+
super(props);
|
|
63
|
+
this.data = props.url;
|
|
64
|
+
this.url = props.url;
|
|
65
|
+
// this.formatSpecificMetadata = this._getFormatSpecificMetadata();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** TODO - not yet clear if we can find schema information in the FeatureServer metadata or if we need to request a feature */
|
|
69
|
+
async getSchema(): Promise<Schema> {
|
|
70
|
+
await this.getMetadata({formatSpecificMetadata: true});
|
|
71
|
+
return {metadata: {}, fields: []};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getMetadata(options: {formatSpecificMetadata}): Promise<VectorSourceMetadata> {
|
|
75
|
+
// Wait for raw metadata to load
|
|
76
|
+
if (!this.formatSpecificMetadata) {
|
|
77
|
+
// this.formatSpecificMetadata = await this._getFormatSpecificMetadata();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// const metadata = parseFlatGeobufMetadata(this.formatSpecificMetadata);
|
|
81
|
+
|
|
82
|
+
// Only add the big blob of source metadata if explicitly requested
|
|
83
|
+
if (options.formatSpecificMetadata) {
|
|
84
|
+
// metadata.formatSpecificMetadata = this.formatSpecificMetadata;
|
|
85
|
+
}
|
|
86
|
+
// @ts-expect-error
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async getFeatures(parameters: GetFeaturesParameters): Promise<GeoJSONTable> {
|
|
91
|
+
const response = await this.fetch(this.url);
|
|
92
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
93
|
+
// TODO - hack - done to avoid pulling in selectLoader from core
|
|
94
|
+
|
|
95
|
+
const table = await FlatGeobufLoader.parse(arrayBuffer, {});
|
|
96
|
+
// const loader = this.props['flatgeobuf-server']?.loaders?.[0];
|
|
97
|
+
// const table = loader?.parse(arrayBuffer);
|
|
98
|
+
return table as GeoJSONTable;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import {Proj4Projection} from '@math.gl/proj4';
|
|
6
6
|
import {transformGeoJsonCoords} from '@loaders.gl/gis';
|
|
7
7
|
|
|
8
|
-
import type {FlatGeobufLoaderOptions} from '../flatgeobuf-loader';
|
|
9
8
|
import type {GeoJSONTable, Table, Schema} from '@loaders.gl/schema';
|
|
10
9
|
|
|
11
10
|
import {fgbToBinaryGeometry} from './binary-geometries';
|
|
@@ -20,22 +19,15 @@ const deserializeGeoJson = geojson.deserialize;
|
|
|
20
19
|
const deserializeGeneric = generic.deserialize;
|
|
21
20
|
// const parsePropertiesBinary = FlatgeobufFeature.parseProperties;
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const parsedGeometry = fgbToBinaryGeometry(geometry, geometryType!);
|
|
33
|
-
// @ts-expect-error this looks wrong
|
|
34
|
-
parsedGeometry.properties = parsePropertiesBinary(feature, header.columns);
|
|
35
|
-
|
|
36
|
-
// TODO: wrap binary data either in points, lines, or polygons key
|
|
37
|
-
return parsedGeometry;
|
|
38
|
-
}
|
|
22
|
+
export type ParseFlatGeobufOptions = {
|
|
23
|
+
shape?: 'geojson-table' | 'columnar-table' | 'binary';
|
|
24
|
+
/** If supplied, only loads features within the bounding box */
|
|
25
|
+
boundingBox?: [[number, number], [number, number]];
|
|
26
|
+
/** Desired output CRS */
|
|
27
|
+
crs?: string;
|
|
28
|
+
/** Should geometries be reprojected to target CRS */
|
|
29
|
+
reproject?: boolean;
|
|
30
|
+
};
|
|
39
31
|
|
|
40
32
|
/*
|
|
41
33
|
* Parse FlatGeobuf arrayBuffer and return GeoJSON.
|
|
@@ -43,11 +35,8 @@ function binaryFromFeature(feature: fgb.Feature, header: fgb.HeaderMeta) {
|
|
|
43
35
|
* @param arrayBuffer A FlatGeobuf arrayBuffer
|
|
44
36
|
* @return A GeoJSON geometry object
|
|
45
37
|
*/
|
|
46
|
-
export function parseFlatGeobuf(
|
|
47
|
-
|
|
48
|
-
options?: FlatGeobufLoaderOptions
|
|
49
|
-
): Table {
|
|
50
|
-
const shape = options?.flatgeobuf?.shape;
|
|
38
|
+
export function parseFlatGeobuf(arrayBuffer: ArrayBuffer, options: ParseFlatGeobufOptions): Table {
|
|
39
|
+
const shape = options.shape;
|
|
51
40
|
|
|
52
41
|
switch (shape) {
|
|
53
42
|
case 'geojson-table': {
|
|
@@ -68,7 +57,7 @@ export function parseFlatGeobuf(
|
|
|
68
57
|
}
|
|
69
58
|
}
|
|
70
59
|
|
|
71
|
-
function parseFlatGeobufToBinary(arrayBuffer: ArrayBuffer, options:
|
|
60
|
+
function parseFlatGeobufToBinary(arrayBuffer: ArrayBuffer, options: ParseFlatGeobufOptions = {}) {
|
|
72
61
|
// TODO: reproject binary features
|
|
73
62
|
// const {reproject = false, _targetCrs = 'WGS84'} = (options && options.gis) || {};
|
|
74
63
|
|
|
@@ -79,32 +68,34 @@ function parseFlatGeobufToBinary(arrayBuffer: ArrayBuffer, options: FlatGeobufLo
|
|
|
79
68
|
|
|
80
69
|
function parseFlatGeobufToGeoJSONTable(
|
|
81
70
|
arrayBuffer: ArrayBuffer,
|
|
82
|
-
options:
|
|
71
|
+
options: ParseFlatGeobufOptions
|
|
83
72
|
): GeoJSONTable {
|
|
84
73
|
if (arrayBuffer.byteLength === 0) {
|
|
85
74
|
return {shape: 'geojson-table', type: 'FeatureCollection', features: []};
|
|
86
75
|
}
|
|
87
76
|
|
|
88
|
-
const {reproject = false,
|
|
77
|
+
const {reproject = false, crs = 'WGS84'} = options;
|
|
89
78
|
|
|
90
79
|
const arr = new Uint8Array(arrayBuffer);
|
|
91
80
|
|
|
92
|
-
let fgbHeader;
|
|
81
|
+
let fgbHeader: fgb.HeaderMeta | undefined;
|
|
93
82
|
let schema: Schema | undefined;
|
|
94
83
|
|
|
84
|
+
const rect = options.boundingBox && convertBoundingBox(options.boundingBox);
|
|
85
|
+
|
|
95
86
|
// @ts-expect-error this looks wrong
|
|
96
|
-
let {features} = deserializeGeoJson(arr,
|
|
87
|
+
let {features} = deserializeGeoJson(arr, rect, (headerMeta) => {
|
|
97
88
|
fgbHeader = headerMeta;
|
|
98
89
|
schema = getSchemaFromFGBHeader(fgbHeader);
|
|
99
90
|
});
|
|
100
91
|
|
|
101
|
-
const
|
|
92
|
+
const fromCRS = fgbHeader?.crs?.wkt;
|
|
102
93
|
let projection;
|
|
103
|
-
if (reproject &&
|
|
94
|
+
if (reproject && fromCRS) {
|
|
104
95
|
// Constructing the projection may fail for some invalid WKT strings
|
|
105
96
|
try {
|
|
106
|
-
projection = new Proj4Projection({from:
|
|
107
|
-
} catch (
|
|
97
|
+
projection = new Proj4Projection({from: fromCRS, to: crs});
|
|
98
|
+
} catch (error) {
|
|
108
99
|
// no op
|
|
109
100
|
}
|
|
110
101
|
}
|
|
@@ -123,8 +114,8 @@ function parseFlatGeobufToGeoJSONTable(
|
|
|
123
114
|
* @return A GeoJSON geometry object iterator
|
|
124
115
|
*/
|
|
125
116
|
// eslint-disable-next-line complexity
|
|
126
|
-
export function parseFlatGeobufInBatches(stream, options:
|
|
127
|
-
const shape = options.
|
|
117
|
+
export function parseFlatGeobufInBatches(stream, options: ParseFlatGeobufOptions) {
|
|
118
|
+
const shape = options.shape;
|
|
128
119
|
switch (shape) {
|
|
129
120
|
case 'binary':
|
|
130
121
|
return parseFlatGeobufInBatchesToBinary(stream, options);
|
|
@@ -135,12 +126,14 @@ export function parseFlatGeobufInBatches(stream, options: FlatGeobufLoaderOption
|
|
|
135
126
|
}
|
|
136
127
|
}
|
|
137
128
|
|
|
138
|
-
function parseFlatGeobufInBatchesToBinary(stream, options:
|
|
129
|
+
function parseFlatGeobufInBatchesToBinary(stream, options: ParseFlatGeobufOptions) {
|
|
139
130
|
// TODO: reproject binary streaming features
|
|
140
131
|
// const {reproject = false, _targetCrs = 'WGS84'} = (options && options.gis) || {};
|
|
141
132
|
|
|
133
|
+
const rect = options.boundingBox && convertBoundingBox(options.boundingBox);
|
|
134
|
+
|
|
142
135
|
// @ts-expect-error
|
|
143
|
-
const iterator = deserializeGeneric(stream, binaryFromFeature);
|
|
136
|
+
const iterator = deserializeGeneric(stream, binaryFromFeature, rect);
|
|
144
137
|
return iterator;
|
|
145
138
|
}
|
|
146
139
|
|
|
@@ -150,8 +143,8 @@ function parseFlatGeobufInBatchesToBinary(stream, options: FlatGeobufLoaderOptio
|
|
|
150
143
|
* @param options
|
|
151
144
|
*/
|
|
152
145
|
// eslint-disable-next-line complexity
|
|
153
|
-
async function* parseFlatGeobufInBatchesToGeoJSON(stream, options:
|
|
154
|
-
const {reproject = false,
|
|
146
|
+
async function* parseFlatGeobufInBatchesToGeoJSON(stream, options: ParseFlatGeobufOptions) {
|
|
147
|
+
const {reproject = false, crs = 'WGS84'} = options || {};
|
|
155
148
|
|
|
156
149
|
let fgbHeader;
|
|
157
150
|
// let schema: Schema | undefined;
|
|
@@ -165,9 +158,9 @@ async function* parseFlatGeobufInBatchesToGeoJSON(stream, options: FlatGeobufLoa
|
|
|
165
158
|
// @ts-expect-error this looks wrong
|
|
166
159
|
for await (const feature of iterator) {
|
|
167
160
|
if (firstRecord) {
|
|
168
|
-
const
|
|
169
|
-
if (reproject &&
|
|
170
|
-
projection = new Proj4Projection({from:
|
|
161
|
+
const fromCRS = fgbHeader?.crs?.wkt;
|
|
162
|
+
if (reproject && fromCRS) {
|
|
163
|
+
projection = new Proj4Projection({from: fromCRS, to: crs});
|
|
171
164
|
}
|
|
172
165
|
|
|
173
166
|
firstRecord = false;
|
|
@@ -181,3 +174,31 @@ async function* parseFlatGeobufInBatchesToGeoJSON(stream, options: FlatGeobufLoa
|
|
|
181
174
|
}
|
|
182
175
|
}
|
|
183
176
|
}
|
|
177
|
+
|
|
178
|
+
// HELPERS
|
|
179
|
+
|
|
180
|
+
function convertBoundingBox(boundingBox: [[number, number], [number, number]]): fgb.Rect {
|
|
181
|
+
return {
|
|
182
|
+
minX: boundingBox[0][0],
|
|
183
|
+
minY: boundingBox[0][1],
|
|
184
|
+
maxX: boundingBox[1][0],
|
|
185
|
+
maxY: boundingBox[1][1]
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// TODO: reproject binary features
|
|
190
|
+
function binaryFromFeature(feature: fgb.Feature, header: fgb.HeaderMeta) {
|
|
191
|
+
const geometry = feature.geometry();
|
|
192
|
+
|
|
193
|
+
// FlatGeobuf files can only hold a single geometry type per file, otherwise
|
|
194
|
+
// GeometryType is GeometryCollection
|
|
195
|
+
// I believe geometry.type() is null (0) except when the geometry type isn't
|
|
196
|
+
// known in the header?
|
|
197
|
+
const geometryType = header.geometryType || geometry?.type();
|
|
198
|
+
const parsedGeometry = fgbToBinaryGeometry(geometry, geometryType!);
|
|
199
|
+
// @ts-expect-error this looks wrong
|
|
200
|
+
parsedGeometry.properties = parsePropertiesBinary(feature, header.columns);
|
|
201
|
+
|
|
202
|
+
// TODO: wrap binary data either in points, lines, or polygons key
|
|
203
|
+
return parsedGeometry;
|
|
204
|
+
}
|