@loaders.gl/shapefile 3.1.3 → 4.0.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 (99) hide show
  1. package/dist/bundle.js +2 -2
  2. package/dist/bundle.js.map +1 -0
  3. package/dist/dbf-loader.js +20 -29
  4. package/dist/dbf-loader.js.map +1 -0
  5. package/dist/dbf-worker.js +1 -1
  6. package/dist/index.js +4 -11
  7. package/dist/index.js.map +1 -0
  8. package/dist/lib/parsers/parse-dbf.js +264 -300
  9. package/dist/lib/parsers/parse-dbf.js.map +1 -0
  10. package/dist/lib/parsers/parse-shapefile.js +209 -231
  11. package/dist/lib/parsers/parse-shapefile.js.map +1 -0
  12. package/dist/lib/parsers/parse-shp-geometry.js +212 -265
  13. package/dist/lib/parsers/parse-shp-geometry.js.map +1 -0
  14. package/dist/lib/parsers/parse-shp-header.js +27 -38
  15. package/dist/lib/parsers/parse-shp-header.js.map +1 -0
  16. package/dist/lib/parsers/parse-shp.js +136 -152
  17. package/dist/lib/parsers/parse-shp.js.map +1 -0
  18. package/dist/lib/parsers/parse-shx.js +19 -25
  19. package/dist/lib/parsers/parse-shx.js.map +1 -0
  20. package/dist/lib/streaming/binary-chunk-reader.js +128 -152
  21. package/dist/lib/streaming/binary-chunk-reader.js.map +1 -0
  22. package/dist/lib/streaming/binary-reader.js +33 -50
  23. package/dist/lib/streaming/binary-reader.js.map +1 -0
  24. package/dist/lib/streaming/zip-batch-iterators.js +48 -57
  25. package/dist/lib/streaming/zip-batch-iterators.js.map +1 -0
  26. package/dist/shapefile-loader.js +22 -30
  27. package/dist/shapefile-loader.js.map +1 -0
  28. package/dist/shp-loader.js +22 -32
  29. package/dist/shp-loader.js.map +1 -0
  30. package/dist/shp-worker.js +1 -1
  31. package/dist/workers/dbf-worker.js +4 -5
  32. package/dist/workers/dbf-worker.js.map +1 -0
  33. package/dist/workers/shp-worker.js +4 -5
  34. package/dist/workers/shp-worker.js.map +1 -0
  35. package/package.json +9 -9
  36. package/dist/es5/bundle.js +0 -7
  37. package/dist/es5/bundle.js.map +0 -1
  38. package/dist/es5/dbf-loader.js +0 -68
  39. package/dist/es5/dbf-loader.js.map +0 -1
  40. package/dist/es5/index.js +0 -42
  41. package/dist/es5/index.js.map +0 -1
  42. package/dist/es5/lib/parsers/parse-dbf.js +0 -454
  43. package/dist/es5/lib/parsers/parse-dbf.js.map +0 -1
  44. package/dist/es5/lib/parsers/parse-shapefile.js +0 -497
  45. package/dist/es5/lib/parsers/parse-shapefile.js.map +0 -1
  46. package/dist/es5/lib/parsers/parse-shp-geometry.js +0 -288
  47. package/dist/es5/lib/parsers/parse-shp-geometry.js.map +0 -1
  48. package/dist/es5/lib/parsers/parse-shp-header.js +0 -39
  49. package/dist/es5/lib/parsers/parse-shp-header.js.map +0 -1
  50. package/dist/es5/lib/parsers/parse-shp.js +0 -283
  51. package/dist/es5/lib/parsers/parse-shp.js.map +0 -1
  52. package/dist/es5/lib/parsers/parse-shx.js +0 -31
  53. package/dist/es5/lib/parsers/parse-shx.js.map +0 -1
  54. package/dist/es5/lib/streaming/binary-chunk-reader.js +0 -218
  55. package/dist/es5/lib/streaming/binary-chunk-reader.js.map +0 -1
  56. package/dist/es5/lib/streaming/binary-reader.js +0 -56
  57. package/dist/es5/lib/streaming/binary-reader.js.map +0 -1
  58. package/dist/es5/lib/streaming/zip-batch-iterators.js +0 -118
  59. package/dist/es5/lib/streaming/zip-batch-iterators.js.map +0 -1
  60. package/dist/es5/shapefile-loader.js +0 -34
  61. package/dist/es5/shapefile-loader.js.map +0 -1
  62. package/dist/es5/shp-loader.js +0 -71
  63. package/dist/es5/shp-loader.js.map +0 -1
  64. package/dist/es5/workers/dbf-worker.js +0 -8
  65. package/dist/es5/workers/dbf-worker.js.map +0 -1
  66. package/dist/es5/workers/shp-worker.js +0 -8
  67. package/dist/es5/workers/shp-worker.js.map +0 -1
  68. package/dist/esm/bundle.js +0 -5
  69. package/dist/esm/bundle.js.map +0 -1
  70. package/dist/esm/dbf-loader.js +0 -23
  71. package/dist/esm/dbf-loader.js.map +0 -1
  72. package/dist/esm/index.js +0 -4
  73. package/dist/esm/index.js.map +0 -1
  74. package/dist/esm/lib/parsers/parse-dbf.js +0 -299
  75. package/dist/esm/lib/parsers/parse-dbf.js.map +0 -1
  76. package/dist/esm/lib/parsers/parse-shapefile.js +0 -226
  77. package/dist/esm/lib/parsers/parse-shapefile.js.map +0 -1
  78. package/dist/esm/lib/parsers/parse-shp-geometry.js +0 -234
  79. package/dist/esm/lib/parsers/parse-shp-geometry.js.map +0 -1
  80. package/dist/esm/lib/parsers/parse-shp-header.js +0 -32
  81. package/dist/esm/lib/parsers/parse-shp-header.js.map +0 -1
  82. package/dist/esm/lib/parsers/parse-shp.js +0 -154
  83. package/dist/esm/lib/parsers/parse-shp.js.map +0 -1
  84. package/dist/esm/lib/parsers/parse-shx.js +0 -22
  85. package/dist/esm/lib/parsers/parse-shx.js.map +0 -1
  86. package/dist/esm/lib/streaming/binary-chunk-reader.js +0 -137
  87. package/dist/esm/lib/streaming/binary-chunk-reader.js.map +0 -1
  88. package/dist/esm/lib/streaming/binary-reader.js +0 -35
  89. package/dist/esm/lib/streaming/binary-reader.js.map +0 -1
  90. package/dist/esm/lib/streaming/zip-batch-iterators.js +0 -52
  91. package/dist/esm/lib/streaming/zip-batch-iterators.js.map +0 -1
  92. package/dist/esm/shapefile-loader.js +0 -23
  93. package/dist/esm/shapefile-loader.js.map +0 -1
  94. package/dist/esm/shp-loader.js +0 -25
  95. package/dist/esm/shp-loader.js.map +0 -1
  96. package/dist/esm/workers/dbf-worker.js +0 -4
  97. package/dist/esm/workers/dbf-worker.js.map +0 -1
  98. package/dist/esm/workers/shp-worker.js +0 -4
  99. package/dist/esm/workers/shp-worker.js.map +0 -1
@@ -1,335 +1,299 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.parseDBFInBatches = exports.parseDBF = void 0;
7
- const schema_1 = require("@loaders.gl/schema");
8
- const binary_chunk_reader_1 = __importDefault(require("../streaming/binary-chunk-reader"));
1
+ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
2
+ import { Schema, Field, Bool, Utf8, Float64, TimestampMillisecond } from '@loaders.gl/schema';
3
+ import BinaryChunkReader from '../streaming/binary-chunk-reader';
9
4
  const LITTLE_ENDIAN = true;
10
5
  const DBF_HEADER_SIZE = 32;
11
6
  var STATE;
7
+
12
8
  (function (STATE) {
13
- STATE[STATE["START"] = 0] = "START";
14
- STATE[STATE["FIELD_DESCRIPTORS"] = 1] = "FIELD_DESCRIPTORS";
15
- STATE[STATE["FIELD_PROPERTIES"] = 2] = "FIELD_PROPERTIES";
16
- STATE[STATE["END"] = 3] = "END";
17
- STATE[STATE["ERROR"] = 4] = "ERROR";
9
+ STATE[STATE["START"] = 0] = "START";
10
+ STATE[STATE["FIELD_DESCRIPTORS"] = 1] = "FIELD_DESCRIPTORS";
11
+ STATE[STATE["FIELD_PROPERTIES"] = 2] = "FIELD_PROPERTIES";
12
+ STATE[STATE["END"] = 3] = "END";
13
+ STATE[STATE["ERROR"] = 4] = "ERROR";
18
14
  })(STATE || (STATE = {}));
15
+
19
16
  class DBFParser {
20
- constructor(options) {
21
- this.binaryReader = new binary_chunk_reader_1.default();
22
- this.state = STATE.START;
23
- this.result = {
24
- data: []
25
- };
26
- this.textDecoder = new TextDecoder(options.encoding);
27
- }
28
- /**
29
- * @param arrayBuffer
30
- */
31
- write(arrayBuffer) {
32
- this.binaryReader.write(arrayBuffer);
33
- this.state = parseState(this.state, this.result, this.binaryReader, this.textDecoder);
34
- // this.result.progress.bytesUsed = this.binaryReader.bytesUsed();
35
- // important events:
36
- // - schema available
37
- // - first rows available
38
- // - all rows available
39
- }
40
- end() {
41
- this.binaryReader.end();
42
- this.state = parseState(this.state, this.result, this.binaryReader, this.textDecoder);
43
- // this.result.progress.bytesUsed = this.binaryReader.bytesUsed();
44
- if (this.state !== STATE.END) {
45
- this.state = STATE.ERROR;
46
- this.result.error = 'DBF incomplete file';
47
- }
17
+ constructor(options) {
18
+ _defineProperty(this, "binaryReader", new BinaryChunkReader());
19
+
20
+ _defineProperty(this, "textDecoder", void 0);
21
+
22
+ _defineProperty(this, "state", STATE.START);
23
+
24
+ _defineProperty(this, "result", {
25
+ data: []
26
+ });
27
+
28
+ this.textDecoder = new TextDecoder(options.encoding);
29
+ }
30
+
31
+ write(arrayBuffer) {
32
+ this.binaryReader.write(arrayBuffer);
33
+ this.state = parseState(this.state, this.result, this.binaryReader, this.textDecoder);
34
+ }
35
+
36
+ end() {
37
+ this.binaryReader.end();
38
+ this.state = parseState(this.state, this.result, this.binaryReader, this.textDecoder);
39
+
40
+ if (this.state !== STATE.END) {
41
+ this.state = STATE.ERROR;
42
+ this.result.error = 'DBF incomplete file';
48
43
  }
44
+ }
45
+
49
46
  }
50
- /**
51
- * @param arrayBuffer
52
- * @param options
53
- * @returns DBFTable or rows
54
- */
55
- function parseDBF(arrayBuffer, options = {}) {
56
- const loaderOptions = options.dbf || {};
57
- const { encoding } = loaderOptions;
58
- const dbfParser = new DBFParser({ encoding });
59
- dbfParser.write(arrayBuffer);
60
- dbfParser.end();
61
- const { data, schema } = dbfParser.result;
62
- switch (options.tables && options.tables.format) {
63
- case 'table':
64
- // TODO - parse columns
65
- return { schema, rows: data };
66
- case 'rows':
67
- default:
68
- return data;
69
- }
47
+
48
+ export function parseDBF(arrayBuffer, options = {}) {
49
+ const loaderOptions = options.dbf || {};
50
+ const {
51
+ encoding
52
+ } = loaderOptions;
53
+ const dbfParser = new DBFParser({
54
+ encoding
55
+ });
56
+ dbfParser.write(arrayBuffer);
57
+ dbfParser.end();
58
+ const {
59
+ data,
60
+ schema
61
+ } = dbfParser.result;
62
+
63
+ switch (options.tables && options.tables.format) {
64
+ case 'table':
65
+ return {
66
+ schema,
67
+ rows: data
68
+ };
69
+
70
+ case 'rows':
71
+ default:
72
+ return data;
73
+ }
70
74
  }
71
- exports.parseDBF = parseDBF;
72
- /**
73
- * @param asyncIterator
74
- * @param options
75
- */
76
- async function* parseDBFInBatches(asyncIterator, options = {}) {
77
- const loaderOptions = options.dbf || {};
78
- const { encoding } = loaderOptions;
79
- const parser = new DBFParser({ encoding });
80
- let headerReturned = false;
81
- for await (const arrayBuffer of asyncIterator) {
82
- parser.write(arrayBuffer);
83
- if (!headerReturned && parser.result.dbfHeader) {
84
- headerReturned = true;
85
- yield parser.result.dbfHeader;
86
- }
87
- if (parser.result.data.length > 0) {
88
- yield parser.result.data;
89
- parser.result.data = [];
90
- }
75
+ export async function* parseDBFInBatches(asyncIterator, options = {}) {
76
+ const loaderOptions = options.dbf || {};
77
+ const {
78
+ encoding
79
+ } = loaderOptions;
80
+ const parser = new DBFParser({
81
+ encoding
82
+ });
83
+ let headerReturned = false;
84
+
85
+ for await (const arrayBuffer of asyncIterator) {
86
+ parser.write(arrayBuffer);
87
+
88
+ if (!headerReturned && parser.result.dbfHeader) {
89
+ headerReturned = true;
90
+ yield parser.result.dbfHeader;
91
91
  }
92
- parser.end();
92
+
93
93
  if (parser.result.data.length > 0) {
94
- yield parser.result.data;
94
+ yield parser.result.data;
95
+ parser.result.data = [];
95
96
  }
97
+ }
98
+
99
+ parser.end();
100
+
101
+ if (parser.result.data.length > 0) {
102
+ yield parser.result.data;
103
+ }
96
104
  }
97
- exports.parseDBFInBatches = parseDBFInBatches;
98
- /**
99
- * https://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm
100
- * @param state
101
- * @param result
102
- * @param binaryReader
103
- * @param textDecoder
104
- * @returns
105
- */
106
- /* eslint-disable complexity, max-depth */
105
+
107
106
  function parseState(state, result, binaryReader, textDecoder) {
108
- // eslint-disable-next-line no-constant-condition
109
- while (true) {
110
- try {
111
- switch (state) {
112
- case STATE.ERROR:
113
- case STATE.END:
114
- return state;
115
- case STATE.START:
116
- // Parse initial file header
117
- const dataView = binaryReader.getDataView(DBF_HEADER_SIZE, 'DBF header');
118
- if (!dataView) {
119
- return state;
120
- }
121
- result.dbfHeader = parseDBFHeader(dataView);
122
- result.progress = {
123
- bytesUsed: 0,
124
- rowsTotal: result.dbfHeader.nRecords,
125
- rows: 0
126
- };
127
- state = STATE.FIELD_DESCRIPTORS;
128
- break;
129
- case STATE.FIELD_DESCRIPTORS:
130
- // Parse DBF field descriptors (schema)
131
- const fieldDescriptorView = binaryReader.getDataView(
132
- // @ts-ignore
133
- result.dbfHeader.headerLength - DBF_HEADER_SIZE, 'DBF field descriptors');
134
- if (!fieldDescriptorView) {
135
- return state;
136
- }
137
- result.dbfFields = parseFieldDescriptors(fieldDescriptorView, textDecoder);
138
- result.schema = new schema_1.Schema(result.dbfFields.map((dbfField) => makeField(dbfField)));
139
- state = STATE.FIELD_PROPERTIES;
140
- // TODO(kyle) Not exactly sure why start offset needs to be headerLength + 1?
141
- // parsedbf uses ((fields.length + 1) << 5) + 2;
142
- binaryReader.skip(1);
143
- break;
144
- case STATE.FIELD_PROPERTIES:
145
- const { recordLength = 0, nRecords = 0 } = result?.dbfHeader || {};
146
- while (result.data.length < nRecords) {
147
- const recordView = binaryReader.getDataView(recordLength - 1);
148
- if (!recordView) {
149
- return state;
150
- }
151
- // Note: Avoid actually reading the last byte, which may not be present
152
- binaryReader.skip(1);
153
- // @ts-ignore
154
- const row = parseRow(recordView, result.dbfFields, textDecoder);
155
- result.data.push(row);
156
- // @ts-ignore
157
- result.progress.rows = result.data.length;
158
- }
159
- state = STATE.END;
160
- break;
161
- default:
162
- state = STATE.ERROR;
163
- result.error = `illegal parser state ${state}`;
164
- return state;
165
- }
166
- }
167
- catch (error) {
168
- state = STATE.ERROR;
169
- result.error = `DBF parsing failed: ${error.message}`;
107
+ while (true) {
108
+ try {
109
+ switch (state) {
110
+ case STATE.ERROR:
111
+ case STATE.END:
112
+ return state;
113
+
114
+ case STATE.START:
115
+ const dataView = binaryReader.getDataView(DBF_HEADER_SIZE, 'DBF header');
116
+
117
+ if (!dataView) {
118
+ return state;
119
+ }
120
+
121
+ result.dbfHeader = parseDBFHeader(dataView);
122
+ result.progress = {
123
+ bytesUsed: 0,
124
+ rowsTotal: result.dbfHeader.nRecords,
125
+ rows: 0
126
+ };
127
+ state = STATE.FIELD_DESCRIPTORS;
128
+ break;
129
+
130
+ case STATE.FIELD_DESCRIPTORS:
131
+ const fieldDescriptorView = binaryReader.getDataView(result.dbfHeader.headerLength - DBF_HEADER_SIZE, 'DBF field descriptors');
132
+
133
+ if (!fieldDescriptorView) {
170
134
  return state;
171
- }
135
+ }
136
+
137
+ result.dbfFields = parseFieldDescriptors(fieldDescriptorView, textDecoder);
138
+ result.schema = new Schema(result.dbfFields.map(dbfField => makeField(dbfField)));
139
+ state = STATE.FIELD_PROPERTIES;
140
+ binaryReader.skip(1);
141
+ break;
142
+
143
+ case STATE.FIELD_PROPERTIES:
144
+ const {
145
+ recordLength = 0,
146
+ nRecords = 0
147
+ } = (result === null || result === void 0 ? void 0 : result.dbfHeader) || {};
148
+
149
+ while (result.data.length < nRecords) {
150
+ const recordView = binaryReader.getDataView(recordLength - 1);
151
+
152
+ if (!recordView) {
153
+ return state;
154
+ }
155
+
156
+ binaryReader.skip(1);
157
+ const row = parseRow(recordView, result.dbfFields, textDecoder);
158
+ result.data.push(row);
159
+ result.progress.rows = result.data.length;
160
+ }
161
+
162
+ state = STATE.END;
163
+ break;
164
+
165
+ default:
166
+ state = STATE.ERROR;
167
+ result.error = "illegal parser state ".concat(state);
168
+ return state;
169
+ }
170
+ } catch (error) {
171
+ state = STATE.ERROR;
172
+ result.error = "DBF parsing failed: ".concat(error.message);
173
+ return state;
172
174
  }
175
+ }
173
176
  }
174
- /**
175
- * @param headerView
176
- */
177
+
177
178
  function parseDBFHeader(headerView) {
178
- return {
179
- // Last updated date
180
- year: headerView.getUint8(1) + 1900,
181
- month: headerView.getUint8(2),
182
- day: headerView.getUint8(3),
183
- // Number of records in data file
184
- nRecords: headerView.getUint32(4, LITTLE_ENDIAN),
185
- // Length of header in bytes
186
- headerLength: headerView.getUint16(8, LITTLE_ENDIAN),
187
- // Length of each record
188
- recordLength: headerView.getUint16(10, LITTLE_ENDIAN),
189
- // Not sure if this is usually set
190
- languageDriver: headerView.getUint8(29)
191
- };
179
+ return {
180
+ year: headerView.getUint8(1) + 1900,
181
+ month: headerView.getUint8(2),
182
+ day: headerView.getUint8(3),
183
+ nRecords: headerView.getUint32(4, LITTLE_ENDIAN),
184
+ headerLength: headerView.getUint16(8, LITTLE_ENDIAN),
185
+ recordLength: headerView.getUint16(10, LITTLE_ENDIAN),
186
+ languageDriver: headerView.getUint8(29)
187
+ };
192
188
  }
193
- /**
194
- * @param view
195
- */
189
+
196
190
  function parseFieldDescriptors(view, textDecoder) {
197
- // NOTE: this might overestimate the number of fields if the "Database
198
- // Container" container exists and is included in the headerLength
199
- const nFields = (view.byteLength - 1) / 32;
200
- const fields = [];
201
- let offset = 0;
202
- for (let i = 0; i < nFields; i++) {
203
- const name = textDecoder
204
- .decode(new Uint8Array(view.buffer, view.byteOffset + offset, 11))
205
- // eslint-disable-next-line no-control-regex
206
- .replace(/\u0000/g, '');
207
- fields.push({
208
- name,
209
- dataType: String.fromCharCode(view.getUint8(offset + 11)),
210
- fieldLength: view.getUint8(offset + 16),
211
- decimal: view.getUint8(offset + 17)
212
- });
213
- offset += 32;
214
- }
215
- return fields;
216
- }
217
- /*
218
- * @param {BinaryChunkReader} binaryReader
219
- function parseRows(binaryReader, fields, nRecords, recordLength, textDecoder) {
220
- const rows = [];
221
- for (let i = 0; i < nRecords; i++) {
222
- const recordView = binaryReader.getDataView(recordLength - 1);
223
- binaryReader.skip(1);
224
- // @ts-ignore
225
- rows.push(parseRow(recordView, fields, textDecoder));
191
+ const nFields = (view.byteLength - 1) / 32;
192
+ const fields = [];
193
+ let offset = 0;
194
+
195
+ for (let i = 0; i < nFields; i++) {
196
+ const name = textDecoder.decode(new Uint8Array(view.buffer, view.byteOffset + offset, 11)).replace(/\u0000/g, '');
197
+ fields.push({
198
+ name,
199
+ dataType: String.fromCharCode(view.getUint8(offset + 11)),
200
+ fieldLength: view.getUint8(offset + 16),
201
+ decimal: view.getUint8(offset + 17)
202
+ });
203
+ offset += 32;
226
204
  }
227
- return rows;
205
+
206
+ return fields;
228
207
  }
229
- */
230
- /**
231
- *
232
- * @param view
233
- * @param fields
234
- * @param textDecoder
235
- * @returns
236
- */
208
+
237
209
  function parseRow(view, fields, textDecoder) {
238
- const out = {};
239
- let offset = 0;
240
- for (const field of fields) {
241
- const text = textDecoder.decode(new Uint8Array(view.buffer, view.byteOffset + offset, field.fieldLength));
242
- out[field.name] = parseField(text, field.dataType);
243
- offset += field.fieldLength;
244
- }
245
- return out;
210
+ const out = {};
211
+ let offset = 0;
212
+
213
+ for (const field of fields) {
214
+ const text = textDecoder.decode(new Uint8Array(view.buffer, view.byteOffset + offset, field.fieldLength));
215
+ out[field.name] = parseField(text, field.dataType);
216
+ offset += field.fieldLength;
217
+ }
218
+
219
+ return out;
246
220
  }
247
- /**
248
- * Should NaN be coerced to null?
249
- * @param text
250
- * @param dataType
251
- * @returns Field depends on a type of the data
252
- */
221
+
253
222
  function parseField(text, dataType) {
254
- switch (dataType) {
255
- case 'B':
256
- return parseNumber(text);
257
- case 'C':
258
- return parseCharacter(text);
259
- case 'F':
260
- return parseNumber(text);
261
- case 'N':
262
- return parseNumber(text);
263
- case 'O':
264
- return parseNumber(text);
265
- case 'D':
266
- return parseDate(text);
267
- case 'L':
268
- return parseBoolean(text);
269
- default:
270
- throw new Error('Unsupported data type');
271
- }
223
+ switch (dataType) {
224
+ case 'B':
225
+ return parseNumber(text);
226
+
227
+ case 'C':
228
+ return parseCharacter(text);
229
+
230
+ case 'F':
231
+ return parseNumber(text);
232
+
233
+ case 'N':
234
+ return parseNumber(text);
235
+
236
+ case 'O':
237
+ return parseNumber(text);
238
+
239
+ case 'D':
240
+ return parseDate(text);
241
+
242
+ case 'L':
243
+ return parseBoolean(text);
244
+
245
+ default:
246
+ throw new Error('Unsupported data type');
247
+ }
272
248
  }
273
- /**
274
- * Parse YYYYMMDD to date in milliseconds
275
- * @param str YYYYMMDD
276
- * @returns new Date as a number
277
- */
249
+
278
250
  function parseDate(str) {
279
- return Date.UTC(str.slice(0, 4), parseInt(str.slice(4, 6), 10) - 1, str.slice(6, 8));
251
+ return Date.UTC(str.slice(0, 4), parseInt(str.slice(4, 6), 10) - 1, str.slice(6, 8));
280
252
  }
281
- /**
282
- * Read boolean value
283
- * any of Y, y, T, t coerce to true
284
- * any of N, n, F, f coerce to false
285
- * otherwise null
286
- * @param value
287
- * @returns boolean | null
288
- */
253
+
289
254
  function parseBoolean(value) {
290
- return /^[nf]$/i.test(value) ? false : /^[yt]$/i.test(value) ? true : null;
255
+ return /^[nf]$/i.test(value) ? false : /^[yt]$/i.test(value) ? true : null;
291
256
  }
292
- /**
293
- * Return null instead of NaN
294
- * @param text
295
- * @returns number | null
296
- */
257
+
297
258
  function parseNumber(text) {
298
- const number = parseFloat(text);
299
- return isNaN(number) ? null : number;
259
+ const number = parseFloat(text);
260
+ return isNaN(number) ? null : number;
300
261
  }
301
- /**
302
- *
303
- * @param text
304
- * @returns string | null
305
- */
262
+
306
263
  function parseCharacter(text) {
307
- return text.trim() || null;
264
+ return text.trim() || null;
308
265
  }
309
- /**
310
- * Create a standard Arrow-style `Field` from field descriptor.
311
- * TODO - use `fieldLength` and `decimal` to generate smaller types?
312
- * @param param0
313
- * @returns Field
314
- */
315
- // eslint-disable
316
- function makeField({ name, dataType, fieldLength, decimal }) {
317
- switch (dataType) {
318
- case 'B':
319
- return new schema_1.Field(name, new schema_1.Float64(), true);
320
- case 'C':
321
- return new schema_1.Field(name, new schema_1.Utf8(), true);
322
- case 'F':
323
- return new schema_1.Field(name, new schema_1.Float64(), true);
324
- case 'N':
325
- return new schema_1.Field(name, new schema_1.Float64(), true);
326
- case 'O':
327
- return new schema_1.Field(name, new schema_1.Float64(), true);
328
- case 'D':
329
- return new schema_1.Field(name, new schema_1.TimestampMillisecond(), true);
330
- case 'L':
331
- return new schema_1.Field(name, new schema_1.Bool(), true);
332
- default:
333
- throw new Error('Unsupported data type');
334
- }
266
+
267
+ function makeField({
268
+ name,
269
+ dataType,
270
+ fieldLength,
271
+ decimal
272
+ }) {
273
+ switch (dataType) {
274
+ case 'B':
275
+ return new Field(name, new Float64(), true);
276
+
277
+ case 'C':
278
+ return new Field(name, new Utf8(), true);
279
+
280
+ case 'F':
281
+ return new Field(name, new Float64(), true);
282
+
283
+ case 'N':
284
+ return new Field(name, new Float64(), true);
285
+
286
+ case 'O':
287
+ return new Field(name, new Float64(), true);
288
+
289
+ case 'D':
290
+ return new Field(name, new TimestampMillisecond(), true);
291
+
292
+ case 'L':
293
+ return new Field(name, new Bool(), true);
294
+
295
+ default:
296
+ throw new Error('Unsupported data type');
297
+ }
335
298
  }
299
+ //# sourceMappingURL=parse-dbf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/parsers/parse-dbf.ts"],"names":["Schema","Field","Bool","Utf8","Float64","TimestampMillisecond","BinaryChunkReader","LITTLE_ENDIAN","DBF_HEADER_SIZE","STATE","DBFParser","constructor","options","START","data","textDecoder","TextDecoder","encoding","write","arrayBuffer","binaryReader","state","parseState","result","end","END","ERROR","error","parseDBF","loaderOptions","dbf","dbfParser","schema","tables","format","rows","parseDBFInBatches","asyncIterator","parser","headerReturned","dbfHeader","length","dataView","getDataView","parseDBFHeader","progress","bytesUsed","rowsTotal","nRecords","FIELD_DESCRIPTORS","fieldDescriptorView","headerLength","dbfFields","parseFieldDescriptors","map","dbfField","makeField","FIELD_PROPERTIES","skip","recordLength","recordView","row","parseRow","push","message","headerView","year","getUint8","month","day","getUint32","getUint16","languageDriver","view","nFields","byteLength","fields","offset","i","name","decode","Uint8Array","buffer","byteOffset","replace","dataType","String","fromCharCode","fieldLength","decimal","out","field","text","parseField","parseNumber","parseCharacter","parseDate","parseBoolean","Error","str","Date","UTC","slice","parseInt","value","test","number","parseFloat","isNaN","trim"],"mappings":"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","sourcesContent":["import {Schema, Field, Bool, Utf8, Float64, TimestampMillisecond} from '@loaders.gl/schema';\nimport BinaryChunkReader from '../streaming/binary-chunk-reader';\n\ntype DBFRowsOutput = object[];\n\ninterface DBFTableOutput {\n schema?: Schema;\n rows: DBFRowsOutput;\n}\n\ntype DBFHeader = {\n // Last updated date\n year: number;\n month: number;\n day: number;\n // Number of records in data file\n nRecords: number;\n // Length of header in bytes\n headerLength: number;\n // Length of each record\n recordLength: number;\n // Not sure if this is usually set\n languageDriver: number;\n};\n\ntype DBFField = {\n name: string;\n dataType: string;\n fieldLength: number;\n decimal: number;\n};\n\ntype DBFResult = {\n data: {[key: string]: any}[];\n schema?: Schema;\n error?: string;\n dbfHeader?: DBFHeader;\n dbfFields?: DBFField[];\n progress?: {\n bytesUsed: number;\n rowsTotal: number;\n rows: number;\n };\n};\n\nconst LITTLE_ENDIAN = true;\nconst DBF_HEADER_SIZE = 32;\n\nenum STATE {\n START = 0, // Expecting header\n FIELD_DESCRIPTORS = 1,\n FIELD_PROPERTIES = 2,\n END = 3,\n ERROR = 4\n}\n\nclass DBFParser {\n binaryReader = new BinaryChunkReader();\n textDecoder: TextDecoder;\n state = STATE.START;\n result: DBFResult = {\n data: []\n };\n\n constructor(options: {encoding: string}) {\n this.textDecoder = new TextDecoder(options.encoding);\n }\n\n /**\n * @param arrayBuffer\n */\n write(arrayBuffer: ArrayBuffer): void {\n this.binaryReader.write(arrayBuffer);\n this.state = parseState(this.state, this.result, this.binaryReader, this.textDecoder);\n // this.result.progress.bytesUsed = this.binaryReader.bytesUsed();\n\n // important events:\n // - schema available\n // - first rows available\n // - all rows available\n }\n\n end(): void {\n this.binaryReader.end();\n this.state = parseState(this.state, this.result, this.binaryReader, this.textDecoder);\n // this.result.progress.bytesUsed = this.binaryReader.bytesUsed();\n if (this.state !== STATE.END) {\n this.state = STATE.ERROR;\n this.result.error = 'DBF incomplete file';\n }\n }\n}\n\n/**\n * @param arrayBuffer\n * @param options\n * @returns DBFTable or rows\n */\nexport function parseDBF(\n arrayBuffer: ArrayBuffer,\n options: any = {}\n): DBFRowsOutput | DBFTableOutput {\n const loaderOptions = options.dbf || {};\n const {encoding} = loaderOptions;\n\n const dbfParser = new DBFParser({encoding});\n dbfParser.write(arrayBuffer);\n dbfParser.end();\n\n const {data, schema} = dbfParser.result;\n switch (options.tables && options.tables.format) {\n case 'table':\n // TODO - parse columns\n return {schema, rows: data};\n\n case 'rows':\n default:\n return data;\n }\n}\n/**\n * @param asyncIterator\n * @param options\n */\nexport async function* parseDBFInBatches(\n asyncIterator: AsyncIterable<ArrayBuffer> | Iterable<ArrayBuffer>,\n options: any = {}\n): AsyncIterable<DBFHeader | DBFRowsOutput | DBFTableOutput> {\n const loaderOptions = options.dbf || {};\n const {encoding} = loaderOptions;\n\n const parser = new DBFParser({encoding});\n let headerReturned = false;\n for await (const arrayBuffer of asyncIterator) {\n parser.write(arrayBuffer);\n if (!headerReturned && parser.result.dbfHeader) {\n headerReturned = true;\n yield parser.result.dbfHeader;\n }\n\n if (parser.result.data.length > 0) {\n yield parser.result.data;\n parser.result.data = [];\n }\n }\n parser.end();\n if (parser.result.data.length > 0) {\n yield parser.result.data;\n }\n}\n/**\n * https://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm\n * @param state\n * @param result\n * @param binaryReader\n * @param textDecoder\n * @returns\n */\n/* eslint-disable complexity, max-depth */\nfunction parseState(\n state: STATE,\n result: DBFResult,\n binaryReader: {[key: string]: any},\n textDecoder: TextDecoder\n): STATE {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n switch (state) {\n case STATE.ERROR:\n case STATE.END:\n return state;\n\n case STATE.START:\n // Parse initial file header\n const dataView = binaryReader.getDataView(DBF_HEADER_SIZE, 'DBF header');\n if (!dataView) {\n return state;\n }\n result.dbfHeader = parseDBFHeader(dataView);\n result.progress = {\n bytesUsed: 0,\n rowsTotal: result.dbfHeader.nRecords,\n rows: 0\n };\n state = STATE.FIELD_DESCRIPTORS;\n break;\n\n case STATE.FIELD_DESCRIPTORS:\n // Parse DBF field descriptors (schema)\n const fieldDescriptorView = binaryReader.getDataView(\n // @ts-ignore\n result.dbfHeader.headerLength - DBF_HEADER_SIZE,\n 'DBF field descriptors'\n );\n if (!fieldDescriptorView) {\n return state;\n }\n\n result.dbfFields = parseFieldDescriptors(fieldDescriptorView, textDecoder);\n result.schema = new Schema(result.dbfFields.map((dbfField) => makeField(dbfField)));\n\n state = STATE.FIELD_PROPERTIES;\n\n // TODO(kyle) Not exactly sure why start offset needs to be headerLength + 1?\n // parsedbf uses ((fields.length + 1) << 5) + 2;\n binaryReader.skip(1);\n break;\n\n case STATE.FIELD_PROPERTIES:\n const {recordLength = 0, nRecords = 0} = result?.dbfHeader || {};\n while (result.data.length < nRecords) {\n const recordView = binaryReader.getDataView(recordLength - 1);\n if (!recordView) {\n return state;\n }\n // Note: Avoid actually reading the last byte, which may not be present\n binaryReader.skip(1);\n\n // @ts-ignore\n const row = parseRow(recordView, result.dbfFields, textDecoder);\n result.data.push(row);\n // @ts-ignore\n result.progress.rows = result.data.length;\n }\n state = STATE.END;\n break;\n\n default:\n state = STATE.ERROR;\n result.error = `illegal parser state ${state}`;\n return state;\n }\n } catch (error) {\n state = STATE.ERROR;\n result.error = `DBF parsing failed: ${(error as Error).message}`;\n return state;\n }\n }\n}\n\n/**\n * @param headerView\n */\nfunction parseDBFHeader(headerView: DataView): DBFHeader {\n return {\n // Last updated date\n year: headerView.getUint8(1) + 1900,\n month: headerView.getUint8(2),\n day: headerView.getUint8(3),\n // Number of records in data file\n nRecords: headerView.getUint32(4, LITTLE_ENDIAN),\n // Length of header in bytes\n headerLength: headerView.getUint16(8, LITTLE_ENDIAN),\n // Length of each record\n recordLength: headerView.getUint16(10, LITTLE_ENDIAN),\n // Not sure if this is usually set\n languageDriver: headerView.getUint8(29)\n };\n}\n\n/**\n * @param view\n */\nfunction parseFieldDescriptors(view: DataView, textDecoder: TextDecoder): DBFField[] {\n // NOTE: this might overestimate the number of fields if the \"Database\n // Container\" container exists and is included in the headerLength\n const nFields = (view.byteLength - 1) / 32;\n const fields: DBFField[] = [];\n let offset = 0;\n for (let i = 0; i < nFields; i++) {\n const name = textDecoder\n .decode(new Uint8Array(view.buffer, view.byteOffset + offset, 11))\n // eslint-disable-next-line no-control-regex\n .replace(/\\u0000/g, '');\n\n fields.push({\n name,\n dataType: String.fromCharCode(view.getUint8(offset + 11)),\n fieldLength: view.getUint8(offset + 16),\n decimal: view.getUint8(offset + 17)\n });\n offset += 32;\n }\n return fields;\n}\n\n/*\n * @param {BinaryChunkReader} binaryReader\nfunction parseRows(binaryReader, fields, nRecords, recordLength, textDecoder) {\n const rows = [];\n for (let i = 0; i < nRecords; i++) {\n const recordView = binaryReader.getDataView(recordLength - 1);\n binaryReader.skip(1);\n // @ts-ignore\n rows.push(parseRow(recordView, fields, textDecoder));\n }\n return rows;\n}\n */\n\n/**\n *\n * @param view\n * @param fields\n * @param textDecoder\n * @returns\n */\nfunction parseRow(\n view: DataView,\n fields: DBFField[],\n textDecoder: TextDecoder\n): {[key: string]: any} {\n const out = {};\n let offset = 0;\n for (const field of fields) {\n const text = textDecoder.decode(\n new Uint8Array(view.buffer, view.byteOffset + offset, field.fieldLength)\n );\n out[field.name] = parseField(text, field.dataType);\n offset += field.fieldLength;\n }\n\n return out;\n}\n\n/**\n * Should NaN be coerced to null?\n * @param text\n * @param dataType\n * @returns Field depends on a type of the data\n */\nfunction parseField(text: string, dataType: string): string | number | boolean | null {\n switch (dataType) {\n case 'B':\n return parseNumber(text);\n case 'C':\n return parseCharacter(text);\n case 'F':\n return parseNumber(text);\n case 'N':\n return parseNumber(text);\n case 'O':\n return parseNumber(text);\n case 'D':\n return parseDate(text);\n case 'L':\n return parseBoolean(text);\n default:\n throw new Error('Unsupported data type');\n }\n}\n\n/**\n * Parse YYYYMMDD to date in milliseconds\n * @param str YYYYMMDD\n * @returns new Date as a number\n */\nfunction parseDate(str: any): number {\n return Date.UTC(str.slice(0, 4), parseInt(str.slice(4, 6), 10) - 1, str.slice(6, 8));\n}\n\n/**\n * Read boolean value\n * any of Y, y, T, t coerce to true\n * any of N, n, F, f coerce to false\n * otherwise null\n * @param value\n * @returns boolean | null\n */\nfunction parseBoolean(value: string): boolean | null {\n return /^[nf]$/i.test(value) ? false : /^[yt]$/i.test(value) ? true : null;\n}\n\n/**\n * Return null instead of NaN\n * @param text\n * @returns number | null\n */\nfunction parseNumber(text: string): number | null {\n const number = parseFloat(text);\n return isNaN(number) ? null : number;\n}\n\n/**\n *\n * @param text\n * @returns string | null\n */\nfunction parseCharacter(text: string): string | null {\n return text.trim() || null;\n}\n\n/**\n * Create a standard Arrow-style `Field` from field descriptor.\n * TODO - use `fieldLength` and `decimal` to generate smaller types?\n * @param param0\n * @returns Field\n */\n// eslint-disable\nfunction makeField({name, dataType, fieldLength, decimal}): Field {\n switch (dataType) {\n case 'B':\n return new Field(name, new Float64(), true);\n case 'C':\n return new Field(name, new Utf8(), true);\n case 'F':\n return new Field(name, new Float64(), true);\n case 'N':\n return new Field(name, new Float64(), true);\n case 'O':\n return new Field(name, new Float64(), true);\n case 'D':\n return new Field(name, new TimestampMillisecond(), true);\n case 'L':\n return new Field(name, new Bool(), true);\n default:\n throw new Error('Unsupported data type');\n }\n}\n"],"file":"parse-dbf.js"}