Skip to main content
GeoPackage JS provides several strategies for querying feature data, from simple full-table scans to spatially-indexed bounding-box queries with projection support.

Basic query

Use featureDao.queryForAll() to iterate every row in a feature table. This is the simplest approach but loads all rows into memory, so use a more targeted query for large tables.
import { GeoPackageManager } from '@ngageoint/geopackage';

const geoPackage = await GeoPackageManager.open('/path/to/file.gpkg');
const featureDao = geoPackage.getFeatureDao('my_features');

const resultSet = featureDao.queryForAll();
try {
  for (const row of resultSet) {
    const geometry = row.getGeometry();
    const name = row.getValue('name');
    console.log(row.getId(), name);
  }
} finally {
  resultSet.close();
}
Always call resultSet.close() after iterating, even when an error occurs. Use try/finally to guarantee cleanup.

Bounding-box query with FeatureIndexManager

FeatureIndexManager queries features using a spatial index — either the NGA Geometry Index extension or the RTree extension — and falls back to a manual scan when neither is present.
import {
  FeatureIndexManager,
  BoundingBox,
} from '@ngageoint/geopackage';
import { Projections } from '@ngageoint/projections-js';

const featureIndexManager = new FeatureIndexManager(geoPackage, 'my_features');

// Query all indexed features
const allResults = featureIndexManager.query();
try {
  for (const featureRow of allResults) {
    // process featureRow
  }
} finally {
  allResults.close();
}

Spatial query with projection

Pass a BoundingBox and a Projection to queryWithBoundingBoxAndProjection() to filter features spatially. The bbox coordinates should be in the coordinate system of the provided projection.
const bbox = new BoundingBox(
  0,    // minLongitude
  -90,  // minLatitude
  180,  // maxLongitude
  90,   // maxLatitude
);

const resultSet = featureIndexManager.queryWithBoundingBoxAndProjection(
  bbox,
  Projections.getWGS84Projection(),
);
try {
  for (const featureRow of resultSet) {
    console.log(featureRow.getId());
  }
} finally {
  resultSet.close();
  featureIndexManager.close(); // also close the index manager when done
}
FeatureIndexManager checks for an RTree index first, then the NGA GeoPackage extension index. If neither exists, it delegates to ManualFeatureQuery, which scans every geometry in memory — this is slower but always works.

Manual query without spatial index

If the table has no spatial index, use ManualFeatureQuery directly.
import { ManualFeatureQuery } from '@ngageoint/geopackage';

const featureDao = geoPackage.getFeatureDao('my_features');
const manualQuery = new ManualFeatureQuery(featureDao);

const bbox = new BoundingBox(-180, -90, 180, 90);
const projection = Projections.getWGS84Projection();

const resultSet = manualQuery.queryWithBoundingBoxAndProjection(bbox, projection);
try {
  for (const featureRow of resultSet) {
    // process each row
  }
} finally {
  resultSet.close();
}

Paginated results

For very large tables, use FeaturePaginatedResults (returned by paginated query methods on FeatureDao) to avoid loading the full result set at once.
// Paginated iteration — the DAO fetches rows in chunks
const paginated = featureDao.paginate(featureDao.queryForAll());
for (const row of paginated) {
  // Each iteration fetches the next page automatically
  console.log(row.getId());
}

GeoJSON result set

geoPackage.queryForGeoJSONFeatures() wraps FeatureIndexManager internally and yields standard GeoJSON Feature objects ready for use with Leaflet, Mapbox, or any GeoJSON-aware tool.
import { BoundingBox } from '@ngageoint/geopackage';

// All features in the table
const geoJSONResultSet = geoPackage.queryForGeoJSONFeatures('my_features');
const features = [];
try {
  for (const feature of geoJSONResultSet) {
    features.push(feature); // each item is a GeoJSON Feature object
  }
} finally {
  geoJSONResultSet.close();
}

// Features within a bounding box
const bbox = new BoundingBox(-10, 49, 2, 61); // UK roughly
const filteredResultSet = geoPackage.queryForGeoJSONFeatures('my_features', bbox);
try {
  for (const feature of filteredResultSet) {
    console.log(feature.properties);
  }
} finally {
  filteredResultSet.close();
}

Accessing geometry and properties

From a FeatureRow, access the raw geometry or individual attribute values:
const row = featureDao.queryForIdRow(42); // fetch a single FeatureRow by primary key

if (row) {
  // Raw geometry
  const geometryData = row.getGeometry(); // GeoPackageGeometryData
  const geometry = geometryData.getGeometry(); // @ngageoint/simple-features-js Geometry

  // Attribute values
  const name = row.getValue('name');             // any column by name
  const columns = row.getColumnNames();          // array of all column names
}
queryForIdRow() returns null when no row with the given ID exists. Always check the return value before accessing it. Use queryForId() if you need the full FeatureResultSet cursor instead.

Build docs developers (and LLMs) love