GeoPackage
editGeoPackage is the SQLite database that GDAL, QGIS and ArcGIS use to store layers: each layer is a table of features, often with an R-tree spatial index. The reader is in the Meziantou.Framework.Geo.GeoPackage package, so that the main package does not depend on SQLite:
dotnet add package Meziantou.Framework.Geo.GeoPackageRead a table
GeoPackage.GetTables lists the feature tables of a file, and GeoPackage.Read reads one. The table name can be omitted when the file has a single one:
var cities = GeoPackage.Read("TestData/places.gpkg", "cities");
var paris = cities[0];
Assert.Equal("1", paris.Id); // The primary key of the table
Assert.Equal(2_102_650, paris.Properties["population"]); // A MEDIUMINT column is an int
Assert.Equal(new GeoPosition(2.3522, 48.8566), ((GeoPoint)paris.Geometry!).Position);
// Write the features in another format, such as GeoJSON
var geoJson = GeoJson.Format(cities);Both methods also take an open SqliteConnection, for a database in memory or a connection your application already has.
Read a large table or a region
GeoPackage.ReadFeaturesAsync returns the features one at a time, as SQLite reads the rows: only the row being read is in memory. Set GeoPackageReaderOptions.Bounds to read only the features whose bounds intersect a box. When the table has an R-tree spatial index, as the tables GDAL writes have, the index finds the rows to read:
// The layers of a GeoPackage file are its feature tables
var tables = GeoPackage.GetTables("TestData/places.gpkg");
Assert.Contains("cities", tables);
// The rows are read one at a time; with a box, the R-tree spatial index of the table finds the rows to read
var europe = new GeoBounds(West: -10, South: 35, East: 30, North: 60);
var names = new List<string?>();
await foreach (var city in GeoPackage.ReadFeaturesAsync("TestData/places.gpkg", "cities", new GeoPackageReaderOptions { Bounds = europe }))
{
names.Add((string?)city.Properties["name"]);
}
Assert.Contains("Paris", names);
Assert.DoesNotContain("Tokyo", names);The features are kept whole: a feature that crosses the edge of the box is not cut. Cut them with Clip, see geometry operations.
What the reader keeps
- Geometries: the standard GeoPackage geometries, read as WellKnownBinary reads them, with their altitudes and measures. An empty or
NULLgeometry gives a feature without geometry. The extended geometries of GeoPackage extensions throw aFormatException. - Identifier: the Id of a feature is the text of the primary key of the table, such as
fid. - Properties: the other columns, with the type their declared type gives: a
boolforBOOLEAN, ansbyte,short,intorlongforTINYINT,SMALLINT,MEDIUMINTandINTEGER, afloatforFLOAT, adoubleforDOUBLEandREAL, astringforTEXT, aDateOnlyforDATE, aDateTimeOffsetforDATETIMEand an array of bytes forBLOB. ANULLvalue is not a property. - Not kept: the coordinate system of the table. The coordinates are expected to be longitudes and latitudes (EPSG:4326), and are not converted otherwise.
The reader opens the file in read-only mode and needs the native SQLite library of Microsoft.Data.Sqlite, with its R-tree module to read a region.