@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.
- package/dist/bundle.js +2 -2
- package/dist/bundle.js.map +1 -0
- package/dist/dbf-loader.js +20 -29
- package/dist/dbf-loader.js.map +1 -0
- package/dist/dbf-worker.js +1 -1
- package/dist/index.js +4 -11
- package/dist/index.js.map +1 -0
- package/dist/lib/parsers/parse-dbf.js +264 -300
- package/dist/lib/parsers/parse-dbf.js.map +1 -0
- package/dist/lib/parsers/parse-shapefile.js +209 -231
- package/dist/lib/parsers/parse-shapefile.js.map +1 -0
- package/dist/lib/parsers/parse-shp-geometry.js +212 -265
- package/dist/lib/parsers/parse-shp-geometry.js.map +1 -0
- package/dist/lib/parsers/parse-shp-header.js +27 -38
- package/dist/lib/parsers/parse-shp-header.js.map +1 -0
- package/dist/lib/parsers/parse-shp.js +136 -152
- package/dist/lib/parsers/parse-shp.js.map +1 -0
- package/dist/lib/parsers/parse-shx.js +19 -25
- package/dist/lib/parsers/parse-shx.js.map +1 -0
- package/dist/lib/streaming/binary-chunk-reader.js +128 -152
- package/dist/lib/streaming/binary-chunk-reader.js.map +1 -0
- package/dist/lib/streaming/binary-reader.js +33 -50
- package/dist/lib/streaming/binary-reader.js.map +1 -0
- package/dist/lib/streaming/zip-batch-iterators.js +48 -57
- package/dist/lib/streaming/zip-batch-iterators.js.map +1 -0
- package/dist/shapefile-loader.js +22 -30
- package/dist/shapefile-loader.js.map +1 -0
- package/dist/shp-loader.js +22 -32
- package/dist/shp-loader.js.map +1 -0
- package/dist/shp-worker.js +1 -1
- package/dist/workers/dbf-worker.js +4 -5
- package/dist/workers/dbf-worker.js.map +1 -0
- package/dist/workers/shp-worker.js +4 -5
- package/dist/workers/shp-worker.js.map +1 -0
- package/package.json +9 -9
- package/dist/es5/bundle.js +0 -7
- package/dist/es5/bundle.js.map +0 -1
- package/dist/es5/dbf-loader.js +0 -68
- package/dist/es5/dbf-loader.js.map +0 -1
- package/dist/es5/index.js +0 -42
- package/dist/es5/index.js.map +0 -1
- package/dist/es5/lib/parsers/parse-dbf.js +0 -454
- package/dist/es5/lib/parsers/parse-dbf.js.map +0 -1
- package/dist/es5/lib/parsers/parse-shapefile.js +0 -497
- package/dist/es5/lib/parsers/parse-shapefile.js.map +0 -1
- package/dist/es5/lib/parsers/parse-shp-geometry.js +0 -288
- package/dist/es5/lib/parsers/parse-shp-geometry.js.map +0 -1
- package/dist/es5/lib/parsers/parse-shp-header.js +0 -39
- package/dist/es5/lib/parsers/parse-shp-header.js.map +0 -1
- package/dist/es5/lib/parsers/parse-shp.js +0 -283
- package/dist/es5/lib/parsers/parse-shp.js.map +0 -1
- package/dist/es5/lib/parsers/parse-shx.js +0 -31
- package/dist/es5/lib/parsers/parse-shx.js.map +0 -1
- package/dist/es5/lib/streaming/binary-chunk-reader.js +0 -218
- package/dist/es5/lib/streaming/binary-chunk-reader.js.map +0 -1
- package/dist/es5/lib/streaming/binary-reader.js +0 -56
- package/dist/es5/lib/streaming/binary-reader.js.map +0 -1
- package/dist/es5/lib/streaming/zip-batch-iterators.js +0 -118
- package/dist/es5/lib/streaming/zip-batch-iterators.js.map +0 -1
- package/dist/es5/shapefile-loader.js +0 -34
- package/dist/es5/shapefile-loader.js.map +0 -1
- package/dist/es5/shp-loader.js +0 -71
- package/dist/es5/shp-loader.js.map +0 -1
- package/dist/es5/workers/dbf-worker.js +0 -8
- package/dist/es5/workers/dbf-worker.js.map +0 -1
- package/dist/es5/workers/shp-worker.js +0 -8
- package/dist/es5/workers/shp-worker.js.map +0 -1
- package/dist/esm/bundle.js +0 -5
- package/dist/esm/bundle.js.map +0 -1
- package/dist/esm/dbf-loader.js +0 -23
- package/dist/esm/dbf-loader.js.map +0 -1
- package/dist/esm/index.js +0 -4
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/lib/parsers/parse-dbf.js +0 -299
- package/dist/esm/lib/parsers/parse-dbf.js.map +0 -1
- package/dist/esm/lib/parsers/parse-shapefile.js +0 -226
- package/dist/esm/lib/parsers/parse-shapefile.js.map +0 -1
- package/dist/esm/lib/parsers/parse-shp-geometry.js +0 -234
- package/dist/esm/lib/parsers/parse-shp-geometry.js.map +0 -1
- package/dist/esm/lib/parsers/parse-shp-header.js +0 -32
- package/dist/esm/lib/parsers/parse-shp-header.js.map +0 -1
- package/dist/esm/lib/parsers/parse-shp.js +0 -154
- package/dist/esm/lib/parsers/parse-shp.js.map +0 -1
- package/dist/esm/lib/parsers/parse-shx.js +0 -22
- package/dist/esm/lib/parsers/parse-shx.js.map +0 -1
- package/dist/esm/lib/streaming/binary-chunk-reader.js +0 -137
- package/dist/esm/lib/streaming/binary-chunk-reader.js.map +0 -1
- package/dist/esm/lib/streaming/binary-reader.js +0 -35
- package/dist/esm/lib/streaming/binary-reader.js.map +0 -1
- package/dist/esm/lib/streaming/zip-batch-iterators.js +0 -52
- package/dist/esm/lib/streaming/zip-batch-iterators.js.map +0 -1
- package/dist/esm/shapefile-loader.js +0 -23
- package/dist/esm/shapefile-loader.js.map +0 -1
- package/dist/esm/shp-loader.js +0 -25
- package/dist/esm/shp-loader.js.map +0 -1
- package/dist/esm/workers/dbf-worker.js +0 -4
- package/dist/esm/workers/dbf-worker.js.map +0 -1
- package/dist/esm/workers/shp-worker.js +0 -4
- package/dist/esm/workers/shp-worker.js.map +0 -1
|
@@ -1,335 +1,299 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
end()
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
92
|
+
|
|
93
93
|
if (parser.result.data.length > 0) {
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
299
|
-
|
|
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
|
-
|
|
264
|
+
return text.trim() || null;
|
|
308
265
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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"}
|