Skip to main content
This guide walks through every step needed to open an existing .gpkg file, inspect its contents, and extract features and tiles.
1

Install the library

npm install @ngageoint/geopackage
2

Open the GeoPackage

GeoPackageManager.open() works in both Node.js (file path) and the browser (byte array).
import {
  GeoPackageManager,
  setCanvasKitWasmLocateFile,
} from '@ngageoint/geopackage';

// Tell the library where to find the CanvasKit WASM module
setCanvasKitWasmLocateFile(
  file => `/path/to/node_modules/@ngageoint/geopackage/dist/canvaskit/${file}`
);

const geoPackage = await GeoPackageManager.open('/path/to/file.gpkg');
In Node.js, pass a file-system path as a string. In the browser, pass a Uint8Array of the file bytes. The library validates the .gpkg extension before opening; passing an invalid path throws a GeoPackageException.
3

List tables

A GeoPackage contains feature tables (vector data) and tile tables (raster data). Retrieve their names before working with them.
// List tile tables
const tileTables: string[] = geoPackage.getTileTables();
console.log('Tile tables:', tileTables);

// List feature tables
const featureTables: string[] = geoPackage.getFeatureTables();
console.log('Feature tables:', featureTables);
4

Inspect table metadata

getInfoForTable() accepts either a TileDao or a FeatureDao and returns a plain object with table name, row count, column definitions, SRS, and—for feature tables—geometry column details.
// Feature table info
const featureDao = geoPackage.getFeatureDao('my_features');
const featureInfo = geoPackage.getInfoForTable(featureDao);
console.log(featureInfo.count);           // number of rows
console.log(featureInfo.geometryColumns); // { geometryColumn, geometryTypeName, ... }
console.log(featureInfo.columns);         // array of column descriptors

// Tile table info
const tileDao = geoPackage.getTileDao('my_tiles');
const tileInfo = geoPackage.getInfoForTable(tileDao);
console.log(tileInfo.minZoom, tileInfo.maxZoom);
5

Iterate feature rows

Use FeatureDao to query rows and access their geometry and attribute values.
const featureDao = geoPackage.getFeatureDao('my_features');

// Query all rows
const resultSet = featureDao.queryForAll();
try {
  for (const row of resultSet) {
    const id = row.getId();
    const geometry = row.getGeometry(); // GeoPackageGeometryData
    const name = row.getValue('name'); // attribute value by column name
    console.log(id, name, geometry);
  }
} finally {
  resultSet.close();
}
Always call resultSet.close() when you are finished iterating, even if an error occurs. Leaving result sets open can exhaust SQLite statement handles.
6

Query features as GeoJSON

queryForGeoJSONFeatures() returns a GeoJSONResultSet that yields standard GeoJSON Feature objects. This is the easiest way to get features into a mapping library.
const geoJSONResultSet = geoPackage.queryForGeoJSONFeatures('my_features');
const features = [];
try {
  for (const feature of geoJSONResultSet) {
    features.push(feature);
  }
} finally {
  geoJSONResultSet.close();
}

const featureCollection = {
  type: 'FeatureCollection',
  features,
};
You can also pass a BoundingBox to limit results to a spatial extent:
import { BoundingBox } from '@ngageoint/geopackage';

const bbox = new BoundingBox(-180, -90, 180, 90); // minLon, minLat, maxLon, maxLat
const filtered = geoPackage.queryForGeoJSONFeatures('my_features', bbox);
7

Read tiles

Use TileDao.queryForTile() to fetch the raw bytes stored in the GeoPackage, or use GeoPackageTileRetriever to retrieve a web-mercator tile reprojected on the fly.
import { GeoPackageTileRetriever, TileUtils, Canvas } from '@ngageoint/geopackage';

const tileDao = geoPackage.getTileDao('my_tiles');

// --- Option A: raw tile bytes (fast, no reprojection) ---
const tileRow = tileDao.queryForTile(/* x */ 0, /* y */ 0, /* zoom */ 0);
if (tileRow) {
  const rawBytes = tileRow.getTileData(); // Buffer / Uint8Array
}

// --- Option B: XYZ web-mercator tile via GeoPackageTileRetriever ---
const retriever = new GeoPackageTileRetriever(tileDao);
const gpTile = await retriever.getTile(0, 0, 0);
if (gpTile) {
  const tileData = gpTile.getData();                        // raw bytes
  const gpImage = await gpTile.getGeoPackageImage();       // decoded image

  const canvas = Canvas.create(
    TileUtils.TILE_PIXELS_DEFAULT,
    TileUtils.TILE_PIXELS_DEFAULT,
  );
  canvas.getContext('2d').drawImage(gpImage.getImage(), 0, 0);
  const dataURL = canvas.toDataURL('image/png');

  // Always dispose in Node.js to prevent memory leaks
  Canvas.disposeImage(gpImage);
  Canvas.disposeCanvas(canvas);
}
8

Close the connection

Call close() when you are finished with the GeoPackage to release the underlying SQLite connection.
geoPackage.close();
Wrap the entire workflow in a try/finally block so close() is always called, even if an error occurs partway through.

Build docs developers (and LLMs) love