@loaders.gl/shapefile 4.2.0-alpha.3 → 4.2.0-alpha.5

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.
Files changed (49) hide show
  1. package/dist/dbf-loader.js +25 -20
  2. package/dist/dbf-worker.js +1 -1
  3. package/dist/dist.dev.js +227 -247
  4. package/dist/dist.min.js +12 -0
  5. package/dist/index.cjs +49 -57
  6. package/dist/index.cjs.map +7 -0
  7. package/dist/index.d.ts +6 -6
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/lib/parsers/parse-dbf.d.ts +1 -1
  11. package/dist/lib/parsers/parse-dbf.d.ts.map +1 -1
  12. package/dist/lib/parsers/parse-dbf.js +300 -259
  13. package/dist/lib/parsers/parse-shapefile.d.ts +3 -3
  14. package/dist/lib/parsers/parse-shapefile.d.ts.map +1 -1
  15. package/dist/lib/parsers/parse-shapefile.js +225 -184
  16. package/dist/lib/parsers/parse-shp-geometry.d.ts +1 -1
  17. package/dist/lib/parsers/parse-shp-geometry.d.ts.map +1 -1
  18. package/dist/lib/parsers/parse-shp-geometry.js +260 -168
  19. package/dist/lib/parsers/parse-shp-header.js +33 -23
  20. package/dist/lib/parsers/parse-shp.d.ts +1 -1
  21. package/dist/lib/parsers/parse-shp.d.ts.map +1 -1
  22. package/dist/lib/parsers/parse-shp.js +147 -110
  23. package/dist/lib/parsers/parse-shx.js +19 -15
  24. package/dist/lib/parsers/types.js +0 -1
  25. package/dist/lib/streaming/binary-chunk-reader.js +150 -95
  26. package/dist/lib/streaming/binary-reader.js +49 -23
  27. package/dist/lib/streaming/zip-batch-iterators.js +61 -45
  28. package/dist/shapefile-loader.js +26 -19
  29. package/dist/shp-loader.js +25 -19
  30. package/dist/shp-worker.js +1 -1
  31. package/dist/workers/dbf-worker.js +0 -1
  32. package/dist/workers/shp-worker.js +0 -1
  33. package/package.json +11 -7
  34. package/dist/dbf-loader.js.map +0 -1
  35. package/dist/index.js.map +0 -1
  36. package/dist/lib/parsers/parse-dbf.js.map +0 -1
  37. package/dist/lib/parsers/parse-shapefile.js.map +0 -1
  38. package/dist/lib/parsers/parse-shp-geometry.js.map +0 -1
  39. package/dist/lib/parsers/parse-shp-header.js.map +0 -1
  40. package/dist/lib/parsers/parse-shp.js.map +0 -1
  41. package/dist/lib/parsers/parse-shx.js.map +0 -1
  42. package/dist/lib/parsers/types.js.map +0 -1
  43. package/dist/lib/streaming/binary-chunk-reader.js.map +0 -1
  44. package/dist/lib/streaming/binary-reader.js.map +0 -1
  45. package/dist/lib/streaming/zip-batch-iterators.js.map +0 -1
  46. package/dist/shapefile-loader.js.map +0 -1
  47. package/dist/shp-loader.js.map +0 -1
  48. package/dist/workers/dbf-worker.js.map +0 -1
  49. 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
- var _shapeIterable$Symbol, _shapeIterable$Symbol2;
10
- const {
11
- reproject = false,
12
- _targetCrs = 'WGS84'
13
- } = (options === null || options === void 0 ? void 0 : options.gis) || {};
14
- const {
15
- shx,
16
- cpg,
17
- prj
18
- } = await loadShapefileSidecarFiles(options, context);
19
- const shapeIterable = await parseInBatchesFromContext(asyncIterator, SHPLoader, options, context);
20
- const shapeIterator = ((_shapeIterable$Symbol = shapeIterable[Symbol.asyncIterator]) === null || _shapeIterable$Symbol === void 0 ? void 0 : _shapeIterable$Symbol.call(shapeIterable)) || ((_shapeIterable$Symbol2 = shapeIterable[Symbol.iterator]) === null || _shapeIterable$Symbol2 === void 0 ? void 0 : _shapeIterable$Symbol2.call(shapeIterable));
21
- let propertyIterator = null;
22
- const dbfResponse = await (context === null || context === void 0 ? void 0 : context.fetch(replaceExtension((context === null || context === void 0 ? void 0 : context.url) || '', 'dbf')));
23
- if (dbfResponse !== null && dbfResponse !== void 0 && dbfResponse.ok) {
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
- const geojsonGeometries = parseGeometries(geometries);
59
- let features = joinProperties(geojsonGeometries, properties);
60
- if (reproject) {
61
- features = reprojectFeatures(features, prj, _targetCrs);
62
- }
63
- yield {
64
- encoding: cpg,
65
- prj,
66
- shx,
67
- header: shapeHeader,
68
- data: features
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
- var _propertyTable, _options$shapefile, _propertyTable2;
74
- const {
75
- reproject = false,
76
- _targetCrs = 'WGS84'
77
- } = (options === null || options === void 0 ? void 0 : options.gis) || {};
78
- const {
79
- shx,
80
- cpg,
81
- prj
82
- } = await loadShapefileSidecarFiles(options, context);
83
- const {
84
- header,
85
- geometries
86
- } = await parseFromContext(arrayBuffer, SHPLoader, options, context);
87
- const geojsonGeometries = parseGeometries(geometries);
88
- let propertyTable;
89
- const dbfResponse = await (context === null || context === void 0 ? void 0 : context.fetch(replaceExtension(context === null || context === void 0 ? void 0 : context.url, 'dbf')));
90
- if (dbfResponse !== null && dbfResponse !== void 0 && dbfResponse.ok) {
91
- propertyTable = await parseFromContext(dbfResponse, DBFLoader, {
92
- dbf: {
93
- shape: 'object-row-table',
94
- encoding: cpg || 'latin1'
95
- }
96
- }, context);
97
- }
98
- let features = joinProperties(geojsonGeometries, ((_propertyTable = propertyTable) === null || _propertyTable === void 0 ? void 0 : _propertyTable.data) || []);
99
- if (reproject) {
100
- features = reprojectFeatures(features, prj, _targetCrs);
101
- }
102
- switch (options === null || options === void 0 ? void 0 : (_options$shapefile = options.shapefile) === null || _options$shapefile === void 0 ? void 0 : _options$shapefile.shape) {
103
- case 'geojson-table':
104
- return {
105
- shape: 'geojson-table',
106
- type: 'FeatureCollection',
107
- encoding: cpg,
108
- schema: ((_propertyTable2 = propertyTable) === null || _propertyTable2 === void 0 ? void 0 : _propertyTable2.schema) || {
109
- metadata: {},
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
- const geojsonGeometries = [];
129
- for (const geom of geometries) {
130
- geojsonGeometries.push(binaryToGeometry(geom));
131
- }
132
- return geojsonGeometries;
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
- const features = [];
136
- for (let i = 0; i < geometries.length; i++) {
137
- const geometry = geometries[i];
138
- const feature = {
139
- type: 'Feature',
140
- geometry,
141
- properties: properties && properties[i] || {}
142
- };
143
- features.push(feature);
144
- }
145
- return features;
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
- if (!sourceCrs && !targetCrs) {
149
- return features;
150
- }
151
- const projection = new Proj4Projection({
152
- from: sourceCrs || 'WGS84',
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
- const {
159
- url,
160
- fetch
161
- } = context;
162
- const shxPromise = fetch(replaceExtension(url, 'shx'));
163
- const cpgPromise = fetch(replaceExtension(url, 'cpg'));
164
- const prjPromise = fetch(replaceExtension(url, 'prj'));
165
- await Promise.all([shxPromise, cpgPromise, prjPromise]);
166
- let shx;
167
- let cpg;
168
- let prj;
169
- const shxResponse = await shxPromise;
170
- if (shxResponse.ok) {
171
- const arrayBuffer = await shxResponse.arrayBuffer();
172
- shx = parseShx(arrayBuffer);
173
- }
174
- const cpgResponse = await cpgPromise;
175
- if (cpgResponse.ok) {
176
- cpg = await cpgResponse.text();
177
- }
178
- const prjResponse = await prjPromise;
179
- if (prjResponse.ok) {
180
- prj = await prjResponse.text();
181
- }
182
- return {
183
- shx,
184
- cpg,
185
- prj
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
- const baseName = basename(url);
190
- const extension = extname(url);
191
- const isUpperCase = extension === extension.toUpperCase();
192
- if (isUpperCase) {
193
- newExtension = newExtension.toUpperCase();
194
- }
195
- return `${baseName}.${newExtension}`;
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
- const extIndex = url && url.lastIndexOf('.');
199
- if (typeof extIndex === 'number') {
200
- return extIndex >= 0 ? url.substr(0, extIndex) : '';
201
- }
202
- return extIndex;
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
- const extIndex = url && url.lastIndexOf('.');
206
- if (typeof extIndex === 'number') {
207
- return extIndex >= 0 ? url.substr(extIndex + 1) : '';
208
- }
209
- return extIndex;
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,5 +1,5 @@
1
1
  import { BinaryGeometry } from '@loaders.gl/schema';
2
- import { SHPLoaderOptions } from './types';
2
+ import { SHPLoaderOptions } from "./types.js";
3
3
  /**
4
4
  * Parse individual record
5
5
  *
@@ -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,MAAM,SAAS,CAAC;AAIzC;;;;;GAKG;AAEH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,cAAc,GAAG,IAAI,CAoD7F"}
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"}