@loaders.gl/shapefile 4.2.0-alpha.4 → 4.2.0-alpha.6
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/dbf-loader.js +25 -20
- package/dist/dbf-worker.js +14 -7
- package/dist/dist.dev.js +219 -232
- package/dist/dist.min.js +12 -0
- package/dist/index.cjs +74 -75
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/parsers/parse-dbf.d.ts +1 -1
- package/dist/lib/parsers/parse-dbf.d.ts.map +1 -1
- package/dist/lib/parsers/parse-dbf.js +300 -258
- package/dist/lib/parsers/parse-shapefile.d.ts +3 -3
- package/dist/lib/parsers/parse-shapefile.d.ts.map +1 -1
- package/dist/lib/parsers/parse-shapefile.js +225 -184
- package/dist/lib/parsers/parse-shp-geometry.d.ts +1 -1
- package/dist/lib/parsers/parse-shp-geometry.d.ts.map +1 -1
- package/dist/lib/parsers/parse-shp-geometry.js +260 -168
- package/dist/lib/parsers/parse-shp-header.js +33 -23
- package/dist/lib/parsers/parse-shp.d.ts +1 -1
- package/dist/lib/parsers/parse-shp.d.ts.map +1 -1
- package/dist/lib/parsers/parse-shp.js +146 -109
- package/dist/lib/parsers/parse-shx.js +19 -15
- package/dist/lib/parsers/types.js +0 -1
- package/dist/lib/streaming/binary-chunk-reader.js +154 -95
- package/dist/lib/streaming/binary-reader.js +51 -23
- package/dist/lib/streaming/zip-batch-iterators.js +61 -45
- package/dist/shapefile-loader.js +26 -19
- package/dist/shp-loader.js +25 -19
- package/dist/shp-worker.js +22 -16
- package/dist/workers/dbf-worker.js +0 -1
- package/dist/workers/shp-worker.js +0 -1
- package/package.json +11 -7
- package/dist/dbf-loader.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/parsers/parse-dbf.js.map +0 -1
- package/dist/lib/parsers/parse-shapefile.js.map +0 -1
- package/dist/lib/parsers/parse-shp-geometry.js.map +0 -1
- package/dist/lib/parsers/parse-shp-header.js.map +0 -1
- package/dist/lib/parsers/parse-shp.js.map +0 -1
- package/dist/lib/parsers/parse-shx.js.map +0 -1
- package/dist/lib/parsers/types.js.map +0 -1
- package/dist/lib/streaming/binary-chunk-reader.js.map +0 -1
- package/dist/lib/streaming/binary-reader.js.map +0 -1
- package/dist/lib/streaming/zip-batch-iterators.js.map +0 -1
- package/dist/shapefile-loader.js.map +0 -1
- package/dist/shp-loader.js.map +0 -1
- package/dist/workers/dbf-worker.js.map +0 -1
- package/dist/workers/shp-worker.js.map +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// import type {Feature} from '@loaders.gl/gis';
|
|
1
2
|
import { parseInBatchesFromContext, parseFromContext } from '@loaders.gl/loader-utils';
|
|
2
3
|
import { binaryToGeometry, transformGeoJsonCoords } from '@loaders.gl/gis';
|
|
3
4
|
import { Proj4Projection } from '@math.gl/proj4';
|
|
@@ -5,207 +6,247 @@ import { parseShx } from "./parse-shx.js";
|
|
|
5
6
|
import { zipBatchIterators } from "../streaming/zip-batch-iterators.js";
|
|
6
7
|
import { SHPLoader } from "../../shp-loader.js";
|
|
7
8
|
import { DBFLoader } from "../../dbf-loader.js";
|
|
9
|
+
/**
|
|
10
|
+
* Parsing of file in batches
|
|
11
|
+
*/
|
|
12
|
+
// eslint-disable-next-line max-statements, complexity
|
|
8
13
|
export async function* parseShapefileInBatches(asyncIterator, options, context) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
var _propertyIterable$Sym;
|
|
25
|
-
const propertyIterable = await parseInBatchesFromContext(dbfResponse, DBFLoader, {
|
|
26
|
-
...options,
|
|
27
|
-
dbf: {
|
|
28
|
-
encoding: cpg || 'latin1'
|
|
29
|
-
}
|
|
30
|
-
}, context);
|
|
31
|
-
propertyIterator = ((_propertyIterable$Sym = propertyIterable[Symbol.asyncIterator]) === null || _propertyIterable$Sym === void 0 ? void 0 : _propertyIterable$Sym.call(propertyIterable)) || propertyIterable[Symbol.iterator]();
|
|
32
|
-
}
|
|
33
|
-
let shapeHeader = (await shapeIterator.next()).value;
|
|
34
|
-
if (shapeHeader && shapeHeader.batchType === 'metadata') {
|
|
35
|
-
shapeHeader = (await shapeIterator.next()).value;
|
|
36
|
-
}
|
|
37
|
-
let dbfHeader = {};
|
|
38
|
-
if (propertyIterator) {
|
|
39
|
-
dbfHeader = (await propertyIterator.next()).value;
|
|
40
|
-
if (dbfHeader && dbfHeader.batchType === 'metadata') {
|
|
41
|
-
dbfHeader = (await propertyIterator.next()).value;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const zippedIterator = propertyIterator ? zipBatchIterators(shapeIterator, propertyIterator, 'object-row-table') : shapeIterator;
|
|
45
|
-
const zippedBatchIterable = {
|
|
46
|
-
[Symbol.asyncIterator]() {
|
|
47
|
-
return zippedIterator;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
for await (const batch of zippedBatchIterable) {
|
|
51
|
-
let geometries;
|
|
52
|
-
let properties;
|
|
53
|
-
if (!propertyIterator) {
|
|
54
|
-
geometries = batch;
|
|
55
|
-
} else {
|
|
56
|
-
[geometries, properties] = batch.data;
|
|
14
|
+
const { reproject = false, _targetCrs = 'WGS84' } = options?.gis || {};
|
|
15
|
+
const { shx, cpg, prj } = await loadShapefileSidecarFiles(options, context);
|
|
16
|
+
// parse geometries
|
|
17
|
+
const shapeIterable = await parseInBatchesFromContext(asyncIterator, SHPLoader, options, context);
|
|
18
|
+
const shapeIterator = shapeIterable[Symbol.asyncIterator]?.() || shapeIterable[Symbol.iterator]?.();
|
|
19
|
+
// parse properties
|
|
20
|
+
let propertyIterator = null;
|
|
21
|
+
const dbfResponse = await context?.fetch(replaceExtension(context?.url || '', 'dbf'));
|
|
22
|
+
if (dbfResponse?.ok) {
|
|
23
|
+
const propertyIterable = await parseInBatchesFromContext(dbfResponse, DBFLoader, {
|
|
24
|
+
...options,
|
|
25
|
+
dbf: { encoding: cpg || 'latin1' }
|
|
26
|
+
}, context);
|
|
27
|
+
propertyIterator =
|
|
28
|
+
propertyIterable[Symbol.asyncIterator]?.() || propertyIterable[Symbol.iterator]();
|
|
57
29
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
30
|
+
// When `options.metadata` is `true`, there's an extra initial `metadata`
|
|
31
|
+
// object before the iterator starts. zipBatchIterators expects to receive
|
|
32
|
+
// batches of Array objects, and will fail with non-iterable batches, so it's
|
|
33
|
+
// important to skip over the first batch.
|
|
34
|
+
let shapeHeader = (await shapeIterator.next()).value;
|
|
35
|
+
if (shapeHeader && shapeHeader.batchType === 'metadata') {
|
|
36
|
+
shapeHeader = (await shapeIterator.next()).value;
|
|
37
|
+
}
|
|
38
|
+
let dbfHeader = {};
|
|
39
|
+
if (propertyIterator) {
|
|
40
|
+
dbfHeader = (await propertyIterator.next()).value;
|
|
41
|
+
if (dbfHeader && dbfHeader.batchType === 'metadata') {
|
|
42
|
+
dbfHeader = (await propertyIterator.next()).value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const zippedIterator = propertyIterator
|
|
46
|
+
? zipBatchIterators(shapeIterator, propertyIterator, 'object-row-table')
|
|
47
|
+
: shapeIterator;
|
|
48
|
+
const zippedBatchIterable = {
|
|
49
|
+
[Symbol.asyncIterator]() {
|
|
50
|
+
return zippedIterator;
|
|
51
|
+
}
|
|
69
52
|
};
|
|
70
|
-
|
|
53
|
+
for await (const batch of zippedBatchIterable) {
|
|
54
|
+
let geometries;
|
|
55
|
+
let properties;
|
|
56
|
+
if (!propertyIterator) {
|
|
57
|
+
geometries = batch;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
[geometries, properties] = batch.data;
|
|
61
|
+
}
|
|
62
|
+
const geojsonGeometries = parseGeometries(geometries);
|
|
63
|
+
let features = joinProperties(geojsonGeometries, properties);
|
|
64
|
+
if (reproject) {
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
features = reprojectFeatures(features, prj, _targetCrs);
|
|
67
|
+
}
|
|
68
|
+
yield {
|
|
69
|
+
encoding: cpg,
|
|
70
|
+
prj,
|
|
71
|
+
shx,
|
|
72
|
+
header: shapeHeader,
|
|
73
|
+
data: features
|
|
74
|
+
};
|
|
75
|
+
}
|
|
71
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Parse shapefile
|
|
79
|
+
*
|
|
80
|
+
* @param arrayBuffer
|
|
81
|
+
* @param options
|
|
82
|
+
* @param context
|
|
83
|
+
* @returns output of shapefile
|
|
84
|
+
*/
|
|
72
85
|
export async function parseShapefile(arrayBuffer, options, context) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
fields: []
|
|
111
|
-
},
|
|
112
|
-
prj,
|
|
113
|
-
shx,
|
|
114
|
-
header,
|
|
115
|
-
features
|
|
116
|
-
};
|
|
117
|
-
default:
|
|
118
|
-
return {
|
|
119
|
-
encoding: cpg,
|
|
120
|
-
prj,
|
|
121
|
-
shx,
|
|
122
|
-
header,
|
|
123
|
-
data: features
|
|
124
|
-
};
|
|
125
|
-
}
|
|
86
|
+
const { reproject = false, _targetCrs = 'WGS84' } = options?.gis || {};
|
|
87
|
+
const { shx, cpg, prj } = await loadShapefileSidecarFiles(options, context);
|
|
88
|
+
// parse geometries
|
|
89
|
+
const { header, geometries } = await parseFromContext(arrayBuffer, SHPLoader, options, context); // {shp: shx}
|
|
90
|
+
const geojsonGeometries = parseGeometries(geometries);
|
|
91
|
+
// parse properties
|
|
92
|
+
let propertyTable;
|
|
93
|
+
const dbfResponse = await context?.fetch(replaceExtension(context?.url, 'dbf'));
|
|
94
|
+
if (dbfResponse?.ok) {
|
|
95
|
+
propertyTable = await parseFromContext(dbfResponse, DBFLoader, { dbf: { shape: 'object-row-table', encoding: cpg || 'latin1' } }, context);
|
|
96
|
+
}
|
|
97
|
+
let features = joinProperties(geojsonGeometries, propertyTable?.data || []);
|
|
98
|
+
if (reproject) {
|
|
99
|
+
features = reprojectFeatures(features, prj, _targetCrs);
|
|
100
|
+
}
|
|
101
|
+
switch (options?.shapefile?.shape) {
|
|
102
|
+
case 'geojson-table':
|
|
103
|
+
return {
|
|
104
|
+
// @ts-expect-error
|
|
105
|
+
shape: 'geojson-table',
|
|
106
|
+
type: 'FeatureCollection',
|
|
107
|
+
encoding: cpg,
|
|
108
|
+
schema: propertyTable?.schema || { metadata: {}, fields: [] },
|
|
109
|
+
prj,
|
|
110
|
+
shx,
|
|
111
|
+
header,
|
|
112
|
+
features
|
|
113
|
+
};
|
|
114
|
+
default:
|
|
115
|
+
return {
|
|
116
|
+
encoding: cpg,
|
|
117
|
+
prj,
|
|
118
|
+
shx,
|
|
119
|
+
header,
|
|
120
|
+
data: features
|
|
121
|
+
};
|
|
122
|
+
}
|
|
126
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Parse geometries
|
|
126
|
+
*
|
|
127
|
+
* @param geometries
|
|
128
|
+
* @returns geometries as an array
|
|
129
|
+
*/
|
|
127
130
|
function parseGeometries(geometries) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
const geojsonGeometries = [];
|
|
132
|
+
for (const geom of geometries) {
|
|
133
|
+
geojsonGeometries.push(binaryToGeometry(geom));
|
|
134
|
+
}
|
|
135
|
+
return geojsonGeometries;
|
|
133
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Join properties and geometries into features
|
|
139
|
+
*
|
|
140
|
+
* @param geometries [description]
|
|
141
|
+
* @param properties [description]
|
|
142
|
+
* @return [description]
|
|
143
|
+
*/
|
|
134
144
|
function joinProperties(geometries, properties) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
const features = [];
|
|
146
|
+
for (let i = 0; i < geometries.length; i++) {
|
|
147
|
+
const geometry = geometries[i];
|
|
148
|
+
const feature = {
|
|
149
|
+
type: 'Feature',
|
|
150
|
+
geometry,
|
|
151
|
+
// properties can be undefined if dbfResponse above was empty
|
|
152
|
+
properties: (properties && properties[i]) || {}
|
|
153
|
+
};
|
|
154
|
+
features.push(feature);
|
|
155
|
+
}
|
|
156
|
+
return features;
|
|
146
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Reproject GeoJSON features to output CRS
|
|
160
|
+
*
|
|
161
|
+
* @param features parsed GeoJSON features
|
|
162
|
+
* @param sourceCrs source coordinate reference system
|
|
163
|
+
* @param targetCrs †arget coordinate reference system
|
|
164
|
+
* @return Reprojected Features
|
|
165
|
+
*/
|
|
147
166
|
function reprojectFeatures(features, sourceCrs, targetCrs) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
to: targetCrs || 'WGS84'
|
|
154
|
-
});
|
|
155
|
-
return transformGeoJsonCoords(features, coord => projection.project(coord));
|
|
167
|
+
if (!sourceCrs && !targetCrs) {
|
|
168
|
+
return features;
|
|
169
|
+
}
|
|
170
|
+
const projection = new Proj4Projection({ from: sourceCrs || 'WGS84', to: targetCrs || 'WGS84' });
|
|
171
|
+
return transformGeoJsonCoords(features, (coord) => projection.project(coord));
|
|
156
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
*
|
|
175
|
+
* @param options
|
|
176
|
+
* @param context
|
|
177
|
+
* @returns Promise
|
|
178
|
+
*/
|
|
179
|
+
// eslint-disable-next-line max-statements
|
|
157
180
|
export async function loadShapefileSidecarFiles(options, context) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
fetch
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
};
|
|
181
|
+
// Attempt a parallel load of the small sidecar files
|
|
182
|
+
// @ts-ignore context must be defined
|
|
183
|
+
const { url, fetch } = context;
|
|
184
|
+
const shxPromise = fetch(replaceExtension(url, 'shx'));
|
|
185
|
+
const cpgPromise = fetch(replaceExtension(url, 'cpg'));
|
|
186
|
+
const prjPromise = fetch(replaceExtension(url, 'prj'));
|
|
187
|
+
await Promise.all([shxPromise, cpgPromise, prjPromise]);
|
|
188
|
+
let shx;
|
|
189
|
+
let cpg;
|
|
190
|
+
let prj;
|
|
191
|
+
const shxResponse = await shxPromise;
|
|
192
|
+
if (shxResponse.ok) {
|
|
193
|
+
const arrayBuffer = await shxResponse.arrayBuffer();
|
|
194
|
+
shx = parseShx(arrayBuffer);
|
|
195
|
+
}
|
|
196
|
+
const cpgResponse = await cpgPromise;
|
|
197
|
+
if (cpgResponse.ok) {
|
|
198
|
+
cpg = await cpgResponse.text();
|
|
199
|
+
}
|
|
200
|
+
const prjResponse = await prjPromise;
|
|
201
|
+
if (prjResponse.ok) {
|
|
202
|
+
prj = await prjResponse.text();
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
shx,
|
|
206
|
+
cpg,
|
|
207
|
+
prj
|
|
208
|
+
};
|
|
187
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Replace the extension at the end of a path.
|
|
212
|
+
*
|
|
213
|
+
* Matches the case of new extension with the case of the original file extension,
|
|
214
|
+
* to increase the chance of finding files without firing off a request storm looking for various case combinations
|
|
215
|
+
*
|
|
216
|
+
* NOTE: Extensions can be both lower and uppercase
|
|
217
|
+
* per spec, extensions should be lower case, but that doesn't mean they always are. See:
|
|
218
|
+
* calvinmetcalf/shapefile-js#64, mapserver/mapserver#4712
|
|
219
|
+
* https://trac.osgeo.org/mapserver/ticket/166
|
|
220
|
+
*/
|
|
188
221
|
export function replaceExtension(url, newExtension) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
222
|
+
const baseName = basename(url);
|
|
223
|
+
const extension = extname(url);
|
|
224
|
+
const isUpperCase = extension === extension.toUpperCase();
|
|
225
|
+
if (isUpperCase) {
|
|
226
|
+
newExtension = newExtension.toUpperCase();
|
|
227
|
+
}
|
|
228
|
+
return `${baseName}.${newExtension}`;
|
|
196
229
|
}
|
|
230
|
+
// NOTE - this gives the entire path minus extension (i.e. NOT same as path.basename)
|
|
231
|
+
/**
|
|
232
|
+
* @param url
|
|
233
|
+
* @returns string
|
|
234
|
+
*/
|
|
197
235
|
function basename(url) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
236
|
+
const extIndex = url && url.lastIndexOf('.');
|
|
237
|
+
if (typeof extIndex === 'number') {
|
|
238
|
+
return extIndex >= 0 ? url.substr(0, extIndex) : '';
|
|
239
|
+
}
|
|
240
|
+
return extIndex;
|
|
203
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* @param url
|
|
244
|
+
* @returns string
|
|
245
|
+
*/
|
|
204
246
|
function extname(url) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
247
|
+
const extIndex = url && url.lastIndexOf('.');
|
|
248
|
+
if (typeof extIndex === 'number') {
|
|
249
|
+
return extIndex >= 0 ? url.substr(extIndex + 1) : '';
|
|
250
|
+
}
|
|
251
|
+
return extIndex;
|
|
210
252
|
}
|
|
211
|
-
//# sourceMappingURL=parse-shapefile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-shp-geometry.d.ts","sourceRoot":"","sources":["../../../src/lib/parsers/parse-shp-geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAqB,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAC,gBAAgB,EAAC,
|
|
1
|
+
{"version":3,"file":"parse-shp-geometry.d.ts","sourceRoot":"","sources":["../../../src/lib/parsers/parse-shp-geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAqB,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAC,gBAAgB,EAAC,mBAAgB;AAIzC;;;;;GAKG;AAEH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,cAAc,GAAG,IAAI,CAoD7F"}
|