ol-load-geopackage 2.1.0 → 2.2.0

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/API.md CHANGED
@@ -32,8 +32,7 @@ initSqlJsWasm(sqlJsWasmDir);
32
32
  Begin asynchronous loading of a single OGC GeoPackage, then extract vector data tables into OpenLayers Vector Sources, transforming the data (if necessary) to match the specified display projection. If a "layer_styles" table is found (as generated by QGIS [Package Layers](https://docs.qgis.org/3.16/en/docs/user_manual/processing_algs/qgis/database.html#package-layers) Processing Toolbox command), it will extract the constituent SLD XML styling data associated with each vector data table.
33
33
 
34
34
  Parameters:
35
-
36
- - string `gpkgFile`: OGC GeoPackage file URL or path relative to root
35
+ - string|File|Blob|URL `gpkgFile`: OGC GeoPackage URL string or File/URL object (URL can be path relative to document's base URI)
37
36
  - string `displayProjection`: Map display projection for output sources (e.g. 'EPSG:3857'). Note that projections not built in to OpenLayers must be defined before calling the function. This is most easily done using the Proj4JS library - see Proj4 [Example](README.md#examples-in-github-repository).
38
37
 
39
38
  Returns a Promise which delivers an array of 2 objects:
@@ -58,10 +57,16 @@ Notes:
58
57
  3. In the output GeoPackage from QGIS [Package Layers](https://docs.qgis.org/3.16/en/docs/user_manual/processing_algs/qgis/database.html#package-layers) the "table name" used for each vector data table will be exactly the same as the "layer name" used to index the SLD style strings in the "layer_styles" table.
59
58
 
60
59
  Errors thrown on these events:
61
- - Unable to load WebAssembly binary (sql-wasm.wasm)
62
- - Unable to load requested OGC GeoPackage
63
- - Missing requested display projection
60
+ - SQL WASM loading not previously started, i.e. mmissing a preceding call to initSqlJsWasm()
61
+ - ol/proj is missing the requested display projection (can be added beforehand using ol/proj/proj4.js)
62
+
63
+ Promise rejected on these events:
64
+ - Unable to load SQLite JS WebAssembly binary (sql-wasm.wasm)
65
+ - `gpkgFile` parameter not of type URL string or URL/File/Blob object
66
+ - Unable to load requested GeoPackage
64
67
  - Missing a data projection (from any of Geopackage tables)
68
+ - Failure trying to extract feature tables from GeoPackage
69
+ - Invalid geometry envelope size flag in a GeoPackage table feature
65
70
 
66
71
 
67
72
  ## sql_js_version
package/README.md CHANGED
@@ -121,7 +121,7 @@ npm run-script dev
121
121
  The JavaScript module has 3 exported functions/constants which are described in the separate [API Specification](API.md):
122
122
 
123
123
  - [initSqlJsWasm()](API.md#initsqljswasmsqljswasmdir) - Initialisation: start loading of required sql.js WASM file
124
- - [loadGpkg()](API.md#loadgpkggpkgfile-displayprojection) - start loading and data extraction of GeoPackage
124
+ - [loadGpkg()](API.md#loadgpkggpkgfile-displayprojection) - start asynchronous loading and data extraction of GeoPackage
125
125
  - [sql_js_version](API.md#sql_js_version) - NPM version number of underlying sql.js module
126
126
 
127
127
  ## Migrating from ol-load-geopackage v1.x.x
@@ -30,8 +30,8 @@ export { initSqlJsWasm, loadGpkg, sql_js_version };
30
30
  * then the returned promise can be ignored; loadGpkg() will wait if necessary
31
31
  * and handle any errors loading the WASM file.
32
32
  * @param {string} sqlJsWasmDir - URL of folder containing sql-wasm.wasm file to load for sql.js
33
- * @returns {Promise} Promise which delivers:
34
- * {WebAssembly} sql.js SQLITE database access library
33
+ * @returns {Promise<WebAssembly>} Promise which delivers:
34
+ * sql.js SQLITE database access library WebAssembly
35
35
  */
36
36
  function initSqlJsWasm(sqlJsWasmDir) {
37
37
  // If the WASM file location isn't specified look for it in the root folder
@@ -46,10 +46,12 @@ function initSqlJsWasm(sqlJsWasmDir) {
46
46
  });
47
47
  promiseSqlWasmLoaded
48
48
  .catch(error => {
49
- // Only need report error here - any later calls to loadGpkg() will throw error
50
- console.error(`initSqlJsWasm() unable to load SQLite JS binary (sql-wasm.wasm) from folder:\n` +
51
- `${sqlJsWasmDir}/\n` +
52
- `[sql.js error message]: ${error}`);
49
+ // Only reporting error to console here to simplify error handling;
50
+ // Error will be dealt with on later call(s) to loadGpkg()
51
+ console.error('initSqlJsWasm() unable to load SQLite JS binary ' +
52
+ `(sql-wasm.wasm) from URL folder:\n` +
53
+ ` ${sqlJsWasmDir}/\n` +
54
+ error);
53
55
  });
54
56
 
55
57
  return promiseSqlWasmLoaded;
@@ -57,11 +59,13 @@ function initSqlJsWasm(sqlJsWasmDir) {
57
59
 
58
60
  /**
59
61
  * Wrapper to load a single OGC GeoPackage
60
- * @param {string} gpkgFile - OGC GeoPackage file path
62
+ * @param {(string|File|Blob|URL)} gpkgFile - OGC GeoPackage URL string or File/URL object
61
63
  * @param {string} displayProjection - map display projection (e.g. EPSG:3857)
62
- * @returns {Promise} Promise which delivers array of 2 objects:
64
+ * @returns {Promise<[Object, Object]>} Promise delivering array of 2 objects:
63
65
  * data tables (OpenLayers vector sources, indexed by table name),
64
66
  * styles (SLD layer_styles XML strings, indexed by layer name)
67
+ * @throws {Error} If SQL WASM loading not previously started
68
+ * @throws {Error} If ol/proj is missing the requested display projection
65
69
  */
66
70
  function loadGpkg(gpkgFile, displayProjection) {
67
71
 
@@ -84,14 +88,20 @@ function loadGpkg(gpkgFile, displayProjection) {
84
88
  return Promise.allSettled([promiseSqlWasmLoaded, gpkgReadPromise])
85
89
  .then((results) => {
86
90
  if (results[0].status === 'rejected') {
87
- throw new Error('Unable to load SQLite JS binary (sql-wasm.wasm)');
91
+ // Throw (initSqlJs() failure) will convert to rejected promise
92
+ throw new Error(
93
+ 'Unable to load SQLite JS binary (sql-wasm.wasm).\n' +
94
+ results[0].reason);
88
95
  }
89
96
  if (results[1].status === 'rejected') {
90
- throw new Error(`Unable to read raw GeoPackage data from file: ${gpkgFile}`);
97
+ // Propagate exact Error object from readRawGpkg().
98
+ // Throw will convert to rejected promise
99
+ throw results[1].reason;
91
100
  }
92
101
  const sqlWasm = results[0].value;
93
- const gpkgArrayBuffer = results[1].value;
94
- return processGpkgData(gpkgFile, gpkgArrayBuffer, sqlWasm, displayProjection);
102
+ const gpkgByteArray = results[1].value;
103
+ const gpkgFileName = (gpkgFile instanceof File) ? gpkgFile.name : gpkgFile.toString();
104
+ return processGpkgData(gpkgFileName, gpkgByteArray, sqlWasm, displayProjection);
95
105
  }
96
106
  );
97
107
  }
@@ -100,44 +110,60 @@ function loadGpkg(gpkgFile, displayProjection) {
100
110
 
101
111
  /**
102
112
  * Read raw data from source for a single OGC GeoPackage
103
- * @param {string} gpkgFile - OGC GeoPackage file path
104
- * @returns {Promise} Promise with Gpkg contents in ArrayBuffer format
113
+ * @param {(string|File|Blob|URL)} input - OGC GeoPackage URL string or File/URL object
114
+ * @returns {Promise<Uint8Array>} Promise with Gpkg contents in byte array format
105
115
  */
106
- function readRawGpkg(gpkgFile) {
107
- return new Promise(function(succeed, fail) {
108
- var oReq = new XMLHttpRequest();
109
- oReq.responseType = 'arraybuffer';
110
- oReq.onreadystatechange = function() {
116
+ async function readRawGpkg(input) {
111
117
 
112
- // When request finished and response is ready
113
- if (this.readyState == 4) {
114
- var gpkgArrayBuffer = this.response;
115
- if (this.status === 200 && gpkgArrayBuffer) {
116
- succeed(gpkgArrayBuffer);
117
- } else {
118
- fail(new Error(
119
- 'Requested GPKG file could not be loaded: ' +
120
- gpkgFile));
121
- }
122
- }
123
- };
124
- oReq.open('GET', gpkgFile);
125
- oReq.send();
126
- });
118
+ // Fetch() or File/Blob object (both have the equivalent methods we need)
119
+ let gpkgSource;
120
+
121
+ // File object is a specific type of Blob
122
+ if (input instanceof Blob) {
123
+ gpkgSource = input;
124
+ } else if (typeof input === 'string' || input instanceof URL) {
125
+ const response = await fetch(input);
126
+ if (!response.ok) {
127
+ // Throw will convert to rejected promise (as an async function)
128
+ throw new Error('GPKG file could not be loaded from URL:\n' +
129
+ ` ${input}\n` +
130
+ `Response: (${response.status}) ${response.statusText}`);
131
+ }
132
+ gpkgSource = response;
133
+ } else {
134
+ // Throw will convert to rejected promise (as an async function)
135
+ throw new TypeError('Input must be URL string or URL/File/Blob object');
136
+ }
137
+
138
+ try {
139
+ // Use .bytes() method if available (most browsers since Jan 2025)
140
+ if (typeof gpkgSource.bytes === 'function') {
141
+ return await gpkgSource.bytes();
142
+ }
143
+
144
+ // Fallback approach: Convert ArrayBuffer to Uint8Array
145
+ //console.log('readRawGpkg(): using fallback .arrayBuffer() + Uint8Array() methods');
146
+ const buffer = await gpkgSource.arrayBuffer();
147
+ return new Uint8Array(buffer);
148
+ } catch (error) {
149
+ // Throw will convert to rejected promise (as an async function)
150
+ throw new Error('Requested GPKG file could not be loaded.\n' + error);
151
+ }
127
152
  }
128
153
 
129
154
  /**
130
- * Process OGC GeoPackage (SQLite database) once loaded
131
- * @param {*} loadedGpkgFile - name of GeoPackage file (for diagnostics only)
132
- * @param {ArrayBuffer} gpkgArrayBuffer - ArrayBuffer containing Gpkg data read
155
+ * Process OGC GeoPackage (based on SQLite database) once loaded
156
+ * @param {string} loadedGpkgFile - name of GeoPackage file (for diagnostics only)
157
+ * @param {Uint8Array} gpkgByteArray - Byte Array containing Gpkg data read
133
158
  * @param {WebAssembly} sqlWasm - sql.js SQLITE database access library
134
159
  * @param {string} displayProjection - map display projection (e.g. EPSG:3857)
135
- * @returns {object[]} array of 2 objects: [<data tables>, <slds>]
136
- * <data tables>: OpenLayers vector sources, indexed by table name
137
- * <slds>: SLD XML strings, indexed by layer name
160
+ * @returns {[Object, Object]} Array of 2 objects:
161
+ * data tables (OpenLayers vector sources, indexed by table name),
162
+ * styles (SLD layer_styles XML strings, indexed by layer name)
163
+ * @throws {Error} If unable to extract feature tables from OGC GeoPackage file
164
+ * @throws {Error} If ol/proj is missing required projection for a data table
138
165
  */
139
- function processGpkgData(loadedGpkgFile, gpkgArrayBuffer, sqlWasm,
140
- displayProjection) {
166
+ function processGpkgData(loadedGpkgFile, gpkgByteArray, sqlWasm, displayProjection) {
141
167
  var db;
142
168
 
143
169
  // Data and associated SLD styles loaded both from GPKG
@@ -147,9 +173,6 @@ function processGpkgData(loadedGpkgFile, gpkgArrayBuffer, sqlWasm,
147
173
  // DEBUG: measure GPKG processing time
148
174
  //var startProcessing = Date.now();
149
175
 
150
- // Convert Array Buffer to Byte Array for SQLite
151
- var gpkgByteArray = new Uint8Array(gpkgArrayBuffer);
152
-
153
176
  try {
154
177
  db = new sqlWasm.Database(gpkgByteArray);
155
178
 
@@ -179,8 +202,8 @@ function processGpkgData(loadedGpkgFile, gpkgArrayBuffer, sqlWasm,
179
202
  }
180
203
  catch (err) {
181
204
  throw new Error(
182
- 'Unable to extract feature tables from OGC GeoPackage file "' +
183
- loadedGpkgFile + '":\n' + err);
205
+ 'Unable to extract feature tables from OGC GeoPackage file.\n' +
206
+ err);
184
207
  }
185
208
 
186
209
  // Extract SLD styles for each layer (if styles included in the gpkg)
@@ -212,6 +235,7 @@ function processGpkgData(loadedGpkgFile, gpkgArrayBuffer, sqlWasm,
212
235
  '" - can be added beforehand with ol/proj/proj4');
213
236
  }
214
237
 
238
+ //console.log(`Extracting table "${table_name}" (SRS: ${tableDataProjection})`);
215
239
  stmt = db.prepare("SELECT * FROM '" + table_name + "'");
216
240
  let vectorSource = new ol_source_Vector();
217
241
  let geometry_column_name = table.geometry_column_name;
@@ -254,8 +278,9 @@ function processGpkgData(loadedGpkgFile, gpkgArrayBuffer, sqlWasm,
254
278
  /**
255
279
  * Extract (SRS ID &) WKB from an OGC GeoPackage feature
256
280
  * (i.e. strip off the variable length header)
257
- * @param {object} gpkgBinGeom feature geometry property (includes header)
258
- * @returns feature geometry in WKB (Well Known Binary) format
281
+ * @param {Uint8Array} gpkgBinGeom - feature geometry property (includes header)
282
+ * @returns {Uint8Array} feature geometry in WKB (Well Known Binary) format
283
+ * @throws {Error} If invalid geometry envelope size flag in GeoPackage
259
284
  */
260
285
  function parseGpkgGeom(gpkgBinGeom) {
261
286
  var flags = gpkgBinGeom[3];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ol-load-geopackage",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Loads OGC GeoPackage vector data tables into OpenLayers Vector Sources",
5
5
  "files": [
6
6
  "dist/",