@loaders.gl/geopackage 4.0.0-alpha.4 → 4.0.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.
Files changed (44) hide show
  1. package/dist/bundle.d.ts +2 -0
  2. package/dist/bundle.d.ts.map +1 -0
  3. package/dist/bundle.js +2 -2
  4. package/dist/es5/bundle.js +6 -0
  5. package/dist/es5/bundle.js.map +1 -0
  6. package/dist/es5/geopackage-loader.js +31 -0
  7. package/dist/es5/geopackage-loader.js.map +1 -0
  8. package/dist/es5/index.js +13 -0
  9. package/dist/es5/index.js.map +1 -0
  10. package/dist/es5/lib/parse-geopackage.js +364 -0
  11. package/dist/es5/lib/parse-geopackage.js.map +1 -0
  12. package/dist/es5/lib/types.js +2 -0
  13. package/dist/es5/lib/types.js.map +1 -0
  14. package/dist/esm/bundle.js +4 -0
  15. package/dist/esm/bundle.js.map +1 -0
  16. package/dist/esm/geopackage-loader.js +21 -0
  17. package/dist/esm/geopackage-loader.js.map +1 -0
  18. package/dist/esm/index.js +2 -0
  19. package/dist/esm/index.js.map +1 -0
  20. package/dist/esm/lib/parse-geopackage.js +292 -0
  21. package/dist/esm/lib/parse-geopackage.js.map +1 -0
  22. package/dist/esm/lib/types.js +2 -0
  23. package/dist/esm/lib/types.js.map +1 -0
  24. package/dist/geopackage-loader.d.ts +14 -0
  25. package/dist/geopackage-loader.d.ts.map +1 -0
  26. package/dist/geopackage-loader.js +47 -15
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +5 -2
  30. package/dist/lib/parse-geopackage.d.ts +5 -0
  31. package/dist/lib/parse-geopackage.d.ts.map +1 -0
  32. package/dist/lib/parse-geopackage.js +361 -272
  33. package/dist/lib/types.d.ts +195 -0
  34. package/dist/lib/types.d.ts.map +1 -0
  35. package/dist/lib/types.js +2 -2
  36. package/package.json +9 -9
  37. package/src/geopackage-loader.ts +6 -2
  38. package/src/lib/parse-geopackage.ts +74 -43
  39. package/src/lib/types.ts +17 -2
  40. package/dist/bundle.js.map +0 -1
  41. package/dist/geopackage-loader.js.map +0 -1
  42. package/dist/index.js.map +0 -1
  43. package/dist/lib/parse-geopackage.js.map +0 -1
  44. package/dist/lib/types.js.map +0 -1
@@ -1,303 +1,392 @@
1
- import initSqlJs from 'sql.js';
2
- import { WKBLoader } from '@loaders.gl/wkt';
3
- import { Schema, Field, Bool, Utf8, Float64, Int32, Int8, Int16, Float32, Binary } from '@loaders.gl/schema';
4
- import { binaryToGeometry, transformGeoJsonCoords } from '@loaders.gl/gis';
5
- import { Proj4Projection } from '@math.gl/proj4';
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.DEFAULT_SQLJS_CDN = void 0;
7
+ const sql_js_1 = __importDefault(require("sql.js"));
8
+ const wkt_1 = require("@loaders.gl/wkt");
9
+ const gis_1 = require("@loaders.gl/gis");
10
+ const proj4_1 = require("@math.gl/proj4");
11
+ // We pin to the same version as sql.js that we use.
12
+ // As of March 2022, versions 1.6.0, 1.6.1, and 1.6.2 of sql.js appeared not to work.
13
+ exports.DEFAULT_SQLJS_CDN = 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.5.0/';
14
+ // https://www.geopackage.org/spec121/#flags_layout
6
15
  const ENVELOPE_BYTE_LENGTHS = {
7
- 0: 0,
8
- 1: 32,
9
- 2: 48,
10
- 3: 48,
11
- 4: 64,
12
- 5: 0,
13
- 6: 0,
14
- 7: 0
16
+ 0: 0,
17
+ 1: 32,
18
+ 2: 48,
19
+ 3: 48,
20
+ 4: 64,
21
+ // values 5-7 are invalid and _should_ never show up
22
+ 5: 0,
23
+ 6: 0,
24
+ 7: 0
15
25
  };
26
+ // Documentation: https://www.geopackage.org/spec130/index.html#table_column_data_types
16
27
  const SQL_TYPE_MAPPING = {
17
- BOOLEAN: Bool,
18
- TINYINT: Int8,
19
- SMALLINT: Int16,
20
- MEDIUMINT: Int32,
21
- INT: Int32,
22
- INTEGER: Int32,
23
- FLOAT: Float32,
24
- DOUBLE: Float64,
25
- REAL: Float64,
26
- TEXT: Utf8,
27
- BLOB: Binary,
28
- DATE: Utf8,
29
- DATETIME: Utf8,
30
- GEOMETRY: Binary
28
+ BOOLEAN: 'bool',
29
+ TINYINT: 'int8',
30
+ SMALLINT: 'int16',
31
+ MEDIUMINT: 'int32',
32
+ INT: 'int32',
33
+ INTEGER: 'int32',
34
+ FLOAT: 'float32',
35
+ DOUBLE: 'float64',
36
+ REAL: 'float64',
37
+ TEXT: 'utf8',
38
+ BLOB: 'binary',
39
+ DATE: 'utf8',
40
+ DATETIME: 'utf8',
41
+ GEOMETRY: 'binary',
42
+ POINT: 'binary',
43
+ LINESTRING: 'binary',
44
+ POLYGON: 'binary',
45
+ MULTIPOINT: 'binary',
46
+ MULTILINESTRING: 'binary',
47
+ MULTIPOLYGON: 'binary',
48
+ GEOMETRYCOLLECTION: 'binary'
31
49
  };
32
- export default async function parseGeoPackage(arrayBuffer, options) {
33
- const {
34
- sqlJsCDN = 'https://sql.js.org/dist/'
35
- } = (options === null || options === void 0 ? void 0 : options.geopackage) || {};
36
- const {
37
- reproject = false,
38
- _targetCrs = 'WGS84'
39
- } = (options === null || options === void 0 ? void 0 : options.gis) || {};
40
- const db = await loadDatabase(arrayBuffer, sqlJsCDN);
41
- const tables = listVectorTables(db);
42
- const projections = getProjections(db);
43
- const result = {};
44
-
45
- for (const table of tables) {
46
- const {
47
- table_name: tableName
48
- } = table;
49
- result[tableName] = getVectorTable(db, tableName, projections, {
50
- reproject,
51
- _targetCrs
52
- });
53
- }
54
-
55
- return result;
50
+ async function parseGeoPackage(arrayBuffer, options) {
51
+ const { sqlJsCDN = exports.DEFAULT_SQLJS_CDN } = options?.geopackage || {};
52
+ const { reproject = false, _targetCrs = 'WGS84', format = 'tables' } = options?.gis || {};
53
+ const db = await loadDatabase(arrayBuffer, sqlJsCDN);
54
+ const tables = listVectorTables(db);
55
+ const projections = getProjections(db);
56
+ // Mapping from tableName to geojson feature collection
57
+ const outputTables = {
58
+ shape: 'tables',
59
+ tables: []
60
+ };
61
+ for (const table of tables) {
62
+ const { table_name: tableName } = table;
63
+ outputTables.tables.push({
64
+ name: tableName,
65
+ table: getVectorTable(db, tableName, projections, {
66
+ reproject,
67
+ _targetCrs
68
+ })
69
+ });
70
+ }
71
+ if (format === 'geojson') {
72
+ return formatTablesAsGeojson(outputTables);
73
+ }
74
+ return outputTables;
56
75
  }
57
-
76
+ exports.default = parseGeoPackage;
77
+ /**
78
+ * Initialize SQL.js and create database
79
+ *
80
+ * @param arrayBuffer input bytes
81
+ * @return SQL.js database object
82
+ */
58
83
  async function loadDatabase(arrayBuffer, sqlJsCDN) {
59
- let SQL;
60
-
61
- if (sqlJsCDN) {
62
- SQL = await initSqlJs({
63
- locateFile: file => "".concat(sqlJsCDN).concat(file)
64
- });
65
- } else {
66
- SQL = await initSqlJs();
67
- }
68
-
69
- return new SQL.Database(new Uint8Array(arrayBuffer));
84
+ // In Node, `locateFile` must not be passed
85
+ let SQL;
86
+ if (sqlJsCDN) {
87
+ SQL = await (0, sql_js_1.default)({
88
+ locateFile: (file) => `${sqlJsCDN}${file}`
89
+ });
90
+ }
91
+ else {
92
+ SQL = await (0, sql_js_1.default)();
93
+ }
94
+ return new SQL.Database(new Uint8Array(arrayBuffer));
70
95
  }
71
-
96
+ /**
97
+ * Find all vector tables in GeoPackage
98
+ * This queries the `gpkg_contents` table to find a list of vector tables
99
+ *
100
+ * @param db GeoPackage to query
101
+ * @return list of table references
102
+ */
72
103
  function listVectorTables(db) {
73
- const stmt = db.prepare("SELECT * FROM gpkg_contents WHERE data_type='features';");
74
- const vectorTablesInfo = [];
75
-
76
- while (stmt.step()) {
77
- const vectorTableInfo = stmt.getAsObject();
78
- vectorTablesInfo.push(vectorTableInfo);
79
- }
80
-
81
- return vectorTablesInfo;
104
+ // The gpkg_contents table can have at least three categorical values for
105
+ // data_type.
106
+ // - 'features' refers to a vector geometry table
107
+ // (https://www.geopackage.org/spec121/#_contents_2)
108
+ // - 'tiles' refers to a raster table
109
+ // (https://www.geopackage.org/spec121/#_contents_3)
110
+ // - 'attributes' refers to a data table with no geometry
111
+ // (https://www.geopackage.org/spec121/#_contents_4).
112
+ // We hard code 'features' because for now we don't support raster data or pure attribute data
113
+ // eslint-disable-next-line quotes
114
+ const stmt = db.prepare("SELECT * FROM gpkg_contents WHERE data_type='features';");
115
+ const vectorTablesInfo = [];
116
+ while (stmt.step()) {
117
+ const vectorTableInfo = stmt.getAsObject();
118
+ vectorTablesInfo.push(vectorTableInfo);
119
+ }
120
+ return vectorTablesInfo;
82
121
  }
83
-
84
- function getVectorTable(db, tableName, projections, {
85
- reproject,
86
- _targetCrs
87
- }) {
88
- const dataColumns = getDataColumns(db, tableName);
89
- const geomColumn = getGeometryColumn(db, tableName);
90
- const featureIdColumn = getFeatureIdName(db, tableName);
91
- const {
92
- columns,
93
- values
94
- } = db.exec("SELECT * FROM `".concat(tableName, "`;"))[0];
95
- let projection;
96
-
97
- if (reproject) {
98
- const geomColumnProjStr = projections[geomColumn.srs_id];
99
- projection = new Proj4Projection({
100
- from: geomColumnProjStr,
101
- to: _targetCrs
102
- });
103
- }
104
-
105
- const geojsonFeatures = [];
106
-
107
- for (const row of values) {
108
- const geojsonFeature = constructGeoJsonFeature(columns, row, geomColumn, dataColumns, featureIdColumn);
109
- geojsonFeatures.push(geojsonFeature);
110
- }
111
-
112
- const schema = getArrowSchema(db, tableName);
113
-
114
- if (projection) {
115
- return {
116
- geojsonFeatures: transformGeoJsonCoords(geojsonFeatures, projection.project),
117
- schema
118
- };
119
- }
120
-
121
- return {
122
- geojsonFeatures,
123
- schema
124
- };
122
+ /**
123
+ * Load geometries from vector table
124
+ *
125
+ * @param db GeoPackage object
126
+ * @param tableName name of vector table to query
127
+ * @param projections keys are srs_id values, values are WKT strings
128
+ * @returns Array of GeoJSON Feature objects
129
+ */
130
+ function getVectorTable(db, tableName, projections, { reproject, _targetCrs }) {
131
+ const dataColumns = getDataColumns(db, tableName);
132
+ const geomColumn = getGeometryColumn(db, tableName);
133
+ const featureIdColumn = getFeatureIdName(db, tableName);
134
+ // Get vector features from table
135
+ // Don't think it's possible to parameterize the table name in SQLite?
136
+ const { columns, values } = db.exec(`SELECT * FROM \`${tableName}\`;`)[0];
137
+ let projection;
138
+ if (reproject) {
139
+ const geomColumnProjStr = projections[geomColumn.srs_id];
140
+ projection = new proj4_1.Proj4Projection({
141
+ from: geomColumnProjStr,
142
+ to: _targetCrs
143
+ });
144
+ }
145
+ const geojsonFeatures = [];
146
+ for (const row of values) {
147
+ const geojsonFeature = constructGeoJsonFeature(columns, row, geomColumn,
148
+ // @ts-ignore
149
+ dataColumns, featureIdColumn);
150
+ geojsonFeatures.push(geojsonFeature);
151
+ }
152
+ const schema = getSchema(db, tableName);
153
+ if (projection) {
154
+ return {
155
+ shape: 'object-row-table',
156
+ data: (0, gis_1.transformGeoJsonCoords)(geojsonFeatures, projection.project),
157
+ schema
158
+ };
159
+ }
160
+ return { data: geojsonFeatures, schema, shape: 'object-row-table' };
125
161
  }
126
-
162
+ /**
163
+ * Find all projections defined in GeoPackage
164
+ * This queries the gpkg_spatial_ref_sys table
165
+ * @param db GeoPackage object
166
+ * @returns mapping from srid to WKT projection string
167
+ */
127
168
  function getProjections(db) {
128
- const stmt = db.prepare('SELECT * FROM gpkg_spatial_ref_sys;');
129
- const projectionMapping = {};
130
-
131
- while (stmt.step()) {
132
- const srsInfo = stmt.getAsObject();
133
- const {
134
- srs_id,
135
- definition
136
- } = srsInfo;
137
- projectionMapping[srs_id] = definition;
138
- }
139
-
140
- return projectionMapping;
169
+ // Query gpkg_spatial_ref_sys to get srid: srtext mappings
170
+ const stmt = db.prepare('SELECT * FROM gpkg_spatial_ref_sys;');
171
+ const projectionMapping = {};
172
+ while (stmt.step()) {
173
+ const srsInfo = stmt.getAsObject();
174
+ const { srs_id, definition } = srsInfo;
175
+ projectionMapping[srs_id] = definition;
176
+ }
177
+ return projectionMapping;
141
178
  }
142
-
179
+ /**
180
+ * Construct single GeoJSON feature given row's data
181
+ * @param columns array of ordered column identifiers
182
+ * @param row array of ordered values representing row's data
183
+ * @param geomColumn geometry column metadata
184
+ * @param dataColumns mapping from table column names to property name
185
+ * @returns GeoJSON Feature object
186
+ */
143
187
  function constructGeoJsonFeature(columns, row, geomColumn, dataColumns, featureIdColumn) {
144
- const idIdx = columns.indexOf(featureIdColumn);
145
- const id = row[idIdx];
146
- const geomColumnIdx = columns.indexOf(geomColumn.column_name);
147
- const geometry = parseGeometry(row[geomColumnIdx].buffer);
148
- const properties = {};
149
-
150
- if (dataColumns) {
151
- for (const [key, value] of Object.entries(dataColumns)) {
152
- const idx = columns.indexOf(key);
153
- properties[value] = row[idx];
188
+ // Find feature id
189
+ const idIdx = columns.indexOf(featureIdColumn);
190
+ const id = row[idIdx];
191
+ // Parse geometry columns to geojson
192
+ const geomColumnIdx = columns.indexOf(geomColumn.column_name);
193
+ const geometry = parseGeometry(row[geomColumnIdx].buffer);
194
+ const properties = {};
195
+ if (dataColumns) {
196
+ for (const [key, value] of Object.entries(dataColumns)) {
197
+ const idx = columns.indexOf(key);
198
+ // @ts-ignore TODO - Check what happens if null?
199
+ properties[value] = row[idx];
200
+ }
154
201
  }
155
- } else {
156
- for (let i = 0; i < columns.length; i++) {
157
- if (i === idIdx || i === geomColumnIdx) {
158
- continue;
159
- }
160
-
161
- const columnName = columns[i];
162
- properties[columnName] = row[i];
202
+ else {
203
+ // Put all columns except for the feature id and geometry in properties
204
+ for (let i = 0; i < columns.length; i++) {
205
+ if (i === idIdx || i === geomColumnIdx) {
206
+ // eslint-disable-next-line no-continue
207
+ continue;
208
+ }
209
+ const columnName = columns[i];
210
+ properties[columnName] = row[i];
211
+ }
163
212
  }
164
- }
165
-
166
- return {
167
- id,
168
- type: 'Feature',
169
- geometry,
170
- properties
171
- };
213
+ return {
214
+ id,
215
+ type: 'Feature',
216
+ geometry,
217
+ properties
218
+ };
172
219
  }
173
-
220
+ /**
221
+ * Get GeoPackage version from database
222
+ * @param db database
223
+ * @returns version string. One of '1.0', '1.1', '1.2'
224
+ */
225
+ // @ts-ignore
226
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
174
227
  function getGeopackageVersion(db) {
175
- const textDecoder = new TextDecoder();
176
- const applicationIdQuery = db.exec('PRAGMA application_id;')[0];
177
- const applicationId = applicationIdQuery.values[0][0];
178
- const buffer = new ArrayBuffer(4);
179
- const view = new DataView(buffer);
180
- view.setInt32(0, Number(applicationId));
181
- const versionString = textDecoder.decode(buffer);
182
-
183
- if (versionString === 'GP10') {
184
- return '1.0';
185
- }
186
-
187
- if (versionString === 'GP11') {
188
- return '1.1';
189
- }
190
-
191
- const userVersionQuery = db.exec('PRAGMA user_version;')[0];
192
- const userVersionInt = userVersionQuery.values[0][0];
193
-
194
- if (userVersionInt && userVersionInt < 10300) {
195
- return '1.2';
196
- }
197
-
198
- return null;
228
+ const textDecoder = new TextDecoder();
229
+ // Read application id from SQLite metadata
230
+ const applicationIdQuery = db.exec('PRAGMA application_id;')[0];
231
+ const applicationId = applicationIdQuery.values[0][0];
232
+ // Convert 4-byte signed int32 application id to text
233
+ const buffer = new ArrayBuffer(4);
234
+ const view = new DataView(buffer);
235
+ view.setInt32(0, Number(applicationId));
236
+ const versionString = textDecoder.decode(buffer);
237
+ if (versionString === 'GP10') {
238
+ return '1.0';
239
+ }
240
+ if (versionString === 'GP11') {
241
+ return '1.1';
242
+ }
243
+ // If versionString is GPKG, then read user_version
244
+ const userVersionQuery = db.exec('PRAGMA user_version;')[0];
245
+ const userVersionInt = userVersionQuery.values[0][0];
246
+ if (userVersionInt && typeof userVersionInt === 'number' && userVersionInt < 10300) {
247
+ return '1.2';
248
+ }
249
+ return null;
199
250
  }
200
-
251
+ /**
252
+ * Find name of feature id column in table
253
+ * The feature ID is the primary key of the table.
254
+ * http://www.geopackage.org/spec121/#feature_user_tables
255
+ *
256
+ * @param db database
257
+ * @param tableName name of table
258
+ * @return name of feature id column
259
+ */
201
260
  function getFeatureIdName(db, tableName) {
202
- const stmt = db.prepare("PRAGMA table_info(`".concat(tableName, "`)"));
203
-
204
- while (stmt.step()) {
205
- const pragmaTableInfo = stmt.getAsObject();
206
- const {
207
- name,
208
- pk
209
- } = pragmaTableInfo;
210
-
211
- if (pk) {
212
- return name;
261
+ // Again, not possible to parameterize table name?
262
+ const stmt = db.prepare(`PRAGMA table_info(\`${tableName}\`)`);
263
+ while (stmt.step()) {
264
+ const pragmaTableInfo = stmt.getAsObject();
265
+ const { name, pk } = pragmaTableInfo;
266
+ if (pk) {
267
+ return name;
268
+ }
213
269
  }
214
- }
215
-
216
- return null;
270
+ // Is it guaranteed for there always to be at least one primary key column in the table?
271
+ return null;
217
272
  }
218
-
273
+ /**
274
+ * Parse geometry buffer
275
+ * GeoPackage vector geometries are slightly extended past the WKB standard
276
+ * See: https://www.geopackage.org/spec121/#gpb_format
277
+ *
278
+ * @param arrayBuffer geometry buffer
279
+ * @return GeoJSON geometry (in original CRS)
280
+ */
219
281
  function parseGeometry(arrayBuffer) {
220
- const view = new DataView(arrayBuffer);
221
- const {
222
- envelopeLength,
223
- emptyGeometry
224
- } = parseGeometryBitFlags(view.getUint8(3));
225
-
226
- if (emptyGeometry) {
227
- return null;
228
- }
229
-
230
- const wkbOffset = 8 + envelopeLength;
231
- const binaryGeometry = WKBLoader.parseSync(arrayBuffer.slice(wkbOffset));
232
- return binaryToGeometry(binaryGeometry);
282
+ const view = new DataView(arrayBuffer);
283
+ const { envelopeLength, emptyGeometry } = parseGeometryBitFlags(view.getUint8(3));
284
+ // A Feature object has a member with the name "geometry". The value of the
285
+ // geometry member SHALL be either a Geometry object as defined above or, in
286
+ // the case that the Feature is unlocated, a JSON null value.
287
+ /** @see https://tools.ietf.org/html/rfc7946#section-3.2 */
288
+ if (emptyGeometry) {
289
+ return null;
290
+ }
291
+ // Do I need to find the srid here? Is it necessarily the same for every
292
+ // geometry in a table?
293
+ // const srid = view.getInt32(4, littleEndian);
294
+ // 2 byte magic, 1 byte version, 1 byte flags, 4 byte int32 srid
295
+ const wkbOffset = 8 + envelopeLength;
296
+ // Loaders should not depend on `core` and the context passed to the main loader doesn't include a
297
+ // `parseSync` option, so instead we call parseSync directly on WKBLoader
298
+ const binaryGeometry = wkt_1.WKBLoader.parseSync(arrayBuffer.slice(wkbOffset));
299
+ return (0, gis_1.binaryToGeometry)(binaryGeometry);
233
300
  }
234
-
301
+ /**
302
+ * Parse geometry header flags
303
+ * https://www.geopackage.org/spec121/#flags_layout
304
+ *
305
+ * @param byte uint8 number representing flags
306
+ * @return object representing information from bit flags
307
+ */
235
308
  function parseGeometryBitFlags(byte) {
236
- const envelopeValue = (byte & 0b00001110) / 2;
237
- const envelopeLength = ENVELOPE_BYTE_LENGTHS[envelopeValue];
238
- return {
239
- littleEndian: Boolean(byte & 0b00000001),
240
- envelopeLength,
241
- emptyGeometry: Boolean(byte & 0b00010000),
242
- extendedGeometryType: Boolean(byte & 0b00100000)
243
- };
309
+ // Are header values little endian?
310
+ const envelopeValue = (byte & 0b00001110) / 2;
311
+ // TODO: Not sure the best way to handle this. Throw an error if envelopeValue outside 0-7?
312
+ const envelopeLength = ENVELOPE_BYTE_LENGTHS[envelopeValue];
313
+ return {
314
+ littleEndian: Boolean(byte & 0b00000001),
315
+ envelopeLength,
316
+ emptyGeometry: Boolean(byte & 0b00010000),
317
+ extendedGeometryType: Boolean(byte & 0b00100000)
318
+ };
244
319
  }
245
-
320
+ /**
321
+ * Find geometry column in given vector table
322
+ *
323
+ * @param db GeoPackage object
324
+ * @param tableName Name of vector table
325
+ * @returns Array of geometry column definitions
326
+ */
246
327
  function getGeometryColumn(db, tableName) {
247
- const stmt = db.prepare('SELECT * FROM gpkg_geometry_columns WHERE table_name=:tableName;');
248
- stmt.bind({
249
- ':tableName': tableName
250
- });
251
- stmt.step();
252
- const geometryColumn = stmt.getAsObject();
253
- return geometryColumn;
328
+ const stmt = db.prepare('SELECT * FROM gpkg_geometry_columns WHERE table_name=:tableName;');
329
+ stmt.bind({ ':tableName': tableName });
330
+ // > Requirement 30
331
+ // > A feature table SHALL have only one geometry column.
332
+ // https://www.geopackage.org/spec121/#feature_user_tables
333
+ // So we should need one and only one step, given that we use the WHERE clause in the SQL query
334
+ // above
335
+ stmt.step();
336
+ const geometryColumn = stmt.getAsObject();
337
+ return geometryColumn;
254
338
  }
255
-
339
+ /**
340
+ * Find property columns in given vector table
341
+ * @param db GeoPackage object
342
+ * @param tableName Name of vector table
343
+ * @returns Mapping from table column names to property name
344
+ */
256
345
  function getDataColumns(db, tableName) {
257
- let stmt;
258
-
259
- try {
260
- stmt = db.prepare('SELECT * FROM gpkg_data_columns WHERE table_name=:tableName;');
261
- } catch (error) {
262
- if (error.message.includes('no such table')) {
263
- return null;
346
+ // gpkg_data_columns is not required to exist
347
+ // https://www.geopackage.org/spec121/#extension_schema
348
+ let stmt;
349
+ try {
350
+ stmt = db.prepare('SELECT * FROM gpkg_data_columns WHERE table_name=:tableName;');
351
+ }
352
+ catch (error) {
353
+ if (error.message.includes('no such table')) {
354
+ return null;
355
+ }
356
+ throw error;
357
+ }
358
+ stmt.bind({ ':tableName': tableName });
359
+ // Convert DataColumnsRow object this to a key-value {column_name: name}
360
+ const result = {};
361
+ while (stmt.step()) {
362
+ const column = stmt.getAsObject();
363
+ const { column_name, name } = column;
364
+ result[column_name] = name || null;
264
365
  }
265
-
266
- throw error;
267
- }
268
-
269
- stmt.bind({
270
- ':tableName': tableName
271
- });
272
- const result = {};
273
-
274
- while (stmt.step()) {
275
- const column = stmt.getAsObject();
276
- const {
277
- column_name,
278
- name
279
- } = column;
280
- result[column_name] = name || null;
281
- }
282
-
283
- return result;
366
+ return result;
284
367
  }
285
-
286
- function getArrowSchema(db, tableName) {
287
- const stmt = db.prepare("PRAGMA table_info(`".concat(tableName, "`)"));
288
- const fields = [];
289
-
290
- while (stmt.step()) {
291
- const pragmaTableInfo = stmt.getAsObject();
292
- const {
293
- name,
294
- type,
295
- notnull
296
- } = pragmaTableInfo;
297
- const field = new Field(name, new SQL_TYPE_MAPPING[type](), !notnull);
298
- fields.push(field);
299
- }
300
-
301
- return new Schema(fields);
368
+ /**
369
+ * Get arrow schema
370
+ * @param db GeoPackage object
371
+ * @param tableName table name
372
+ * @returns Arrow-like Schema
373
+ */
374
+ function getSchema(db, tableName) {
375
+ const stmt = db.prepare(`PRAGMA table_info(\`${tableName}\`)`);
376
+ const fields = [];
377
+ while (stmt.step()) {
378
+ const pragmaTableInfo = stmt.getAsObject();
379
+ const { name, type: sqlType, notnull } = pragmaTableInfo;
380
+ const type = SQL_TYPE_MAPPING[sqlType];
381
+ const field = { name, type, nullable: !notnull };
382
+ fields.push(field);
383
+ }
384
+ return { fields, metadata: {} };
385
+ }
386
+ function formatTablesAsGeojson(tables) {
387
+ const geojsonMap = {};
388
+ for (const table of tables.tables) {
389
+ geojsonMap[table.name] = table.table.data;
390
+ }
391
+ return geojsonMap;
302
392
  }
303
- //# sourceMappingURL=parse-geopackage.js.map