PostGIS/GeoJSON Type Plugin
The Npgsql.GeoJSON plugin makes Npgsql read and write PostGIS spatial types as GeoJSON (RFC7946) types, via the GeoJSON.NET library.
As an alternative, you can use Npgsql.NetTopologySuite, which is a full-fledged .NET spatial library with many features.
Setup
To avoid forcing a dependency on the GeoJSON library for users not using spatial, GeoJSON support is delivered as a separate plugin. To use the plugin, simply add a dependency on Npgsql.GeoJSON and set it up in one of the following ways:
Note
NpgsqlDataSource
was introduced in Npgsql 7.0, and is the recommended way to manage type mapping. If you're using an older version, see the other methods.
var dataSourceBuilder = new NpgsqlDataSourceBuilder(...);
dataSourceBuilder.UseGeoJson();
await using var dataSource = dataSourceBuilder.Build();
Reading and Writing Geometry Values
When reading PostGIS values from the database, Npgsql will automatically return the appropriate GeoJSON types: Point
, LineString
, and so on. Npgsql will also automatically recognize GeoJSON's types in parameters, and will automatically send the corresponding PostGIS type to the database. The following code demonstrates a roundtrip of a GeoJSON Point
to the database:
conn.ExecuteNonQuery("CREATE TEMP TABLE data (geom GEOMETRY)");
await using (var cmd = new NpgsqlCommand("INSERT INTO data (geom) VALUES ($1)", conn))
{
cmd.Parameters.Add(new() { Value = new Point(new Position(51.899523, -2.124156)) });
await cmd.ExecuteNonQueryAsync();
}
await using (var cmd = new NpgsqlCommand("SELECT geom FROM data", conn))
await using (var reader = await cmd.ExecuteReaderAsync())
{
await reader.ReadAsync();
var point2 = reader.GetFieldValue<Point>(0);
}
You may also explicitly specify a parameter's type by setting NpgsqlDbType.Geometry
.
Geography (geodetic) Support
PostGIS has two types: geometry
(for Cartesian coordinates) and geography
(for geodetic or spherical coordinates). You can read about the geometry/geography distinction in the PostGIS docs or in this blog post. In a nutshell, geography
is much more accurate when doing calculations over long distances, but is more expensive computationally and supports only a small subset of the spatial operations supported by geometry
.
Npgsql uses the same GeoJSON types to represent both geometry
and geography
- the Point
type represents a point in either Cartesian or geodetic space. You usually don't need to worry about this distinction because PostgreSQL will usually cast types back and forth as needed. However, it's worth noting that Npgsql sends Cartesian geometry
by default, because that's the usual requirement. You have the option of telling Npgsql to send geography
instead by specifying NpgsqlDbType.Geography
:
using (var cmd = new NpgsqlCommand("INSERT INTO data (geog) VALUES ($1)", conn))
{
cmd.Parameters.Add(new() { Value = point, NpgsqlDbType = NpgsqlDbType.Geography });
await cmd.ExecuteNonQueryAsync();
}
If you prefer to use geography
everywhere by default, you can also specify that when setting up the plugin:
dataSourceBuilder.UseGeoJson(geographyAsDefault: true);