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.