
Mapping the World with EF Core: Working with Spatial Data
- Chris Woodruff
- February 9, 2025
- Entity Framework Core
- .NET, C#, Data, databases, dotnet, Entity Framework Core, programming, spatial
- 6 Comments
Have you ever needed to store coordinates, track locations, or perform distance calculations in your database? Whether you’re building a ride-sharing app, a location-based service, or an interactive map, working with spatial data is essential.
Luckily, EF Core supports spatial data types, allowing you to store, query, and manipulate geographic data seamlessly. No more treating latitude and longitude as simple numbers—let’s bring real GIS (Geographic Information System) power to your EF Core apps!
What is Spatial Data?
Spatial data represents geographic locations and features on the Earth’s surface. Instead of dealing with raw latitude/longitude values, spatial data provides rich functionality for working with points, lines, polygons, and even complex geometries.
Think of it like this:
Points – Represent single locations (e.g., a store location).
Lines – Define paths or routes (e.g., roads, trails).
Polygons – Represent areas (e.g., city boundaries, country borders).
Most modern databases, like SQL Server, PostgreSQL, and MySQL, provide native spatial data support, allowing you to run geospatial queries efficiently.
Setting Up Spatial Data in EF Core
Before we start playing with coordinates, we need to set up EF Core to support spatial data.
1. Install the Required Packages
If you’re using SQL Server, you need the NetTopologySuite package, which enables spatial data support.
dotnet add package Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite
For PostgreSQL, install:
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite
2. Configure DbContext to Use Spatial Support
Modify your DbContext
configuration to enable NetTopologySuite:
public class AppDbContext : DbContext { public DbSet<Location> Locations { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("Your_Connection_String", x => x.UseNetTopologySuite()); // Enable spatial support } }
This tells EF Core to use spatial capabilities when working with SQL Server (or PostgreSQL).
Defining Spatial Data in EF Core
Now that EF Core is ready, let’s create an entity with a geographic location.
3. Create an Entity with Spatial Data
using NetTopologySuite.Geometries; public class Location { public int Id { get; set; } public string Name { get; set; } public Point Coordinates { get; set; } // Stores latitude/longitude }
What’s happening here?
- We import
NetTopologySuite.Geometries
to use spatial types. - The
Point
type is used to store a geographic location (latitude & longitude).
Storing and Querying Spatial Data
Now that we’ve defined our Location
entity, let’s add some data!
4. Inserting Spatial Data
using NetTopologySuite.Geometries; var location = new Location { Name = "Central Park", Coordinates = new Point(-73.9654, 40.7829) { SRID = 4326 } // Longitude, Latitude }; context.Locations.Add(location); await context.SaveChangesAsync();
What is SRID = 4326
?
SRID
(Spatial Reference Identifier) defines the coordinate system.4326
is WGS 84, the standard for latitude/longitude (used by GPS).
5. Querying Nearby Locations
Let’s say you want to find locations within 5 kilometers of a user:
using NetTopologySuite.Geometries; using NetTopologySuite.Geometries.Prepared; var userLocation = new Point(-73.9851, 40.7580) { SRID = 4326 }; // Times Square var nearbyLocations = await context.Locations .Where(l => l.Coordinates.IsWithinDistance(userLocation, 5000)) // 5km radius .ToListAsync(); foreach (var location in nearbyLocations) { Console.WriteLine($"Nearby: {location.Name}"); }
How does this work?
IsWithinDistance()
checks if a location is within 5000 meters (5km) of the user.- This is way more efficient than manually filtering lat/lon values!
Working with Polygons: Defining Regions
Let’s say you need to store city boundaries and check whether a location is inside a region.
6. Create a Polygon Entity
public class CityBoundary { public int Id { get; set; } public string CityName { get; set; } public Polygon Area { get; set; } // Stores city boundary }
7. Query Locations Inside a City
var newYorkBoundary = context.CityBoundaries .FirstOrDefault(c => c.CityName == "New York"); var locationsInNYC = await context.Locations .Where(l => newYorkBoundary.Area.Contains(l.Coordinates)) .ToListAsync(); foreach (var location in locationsInNYC) { Console.WriteLine($"{location.Name} is inside New York!"); }
Why is this cool?
Contains()
lets you check if a location is inside a polygon (city, park, etc.).- This is super useful for geofencing, city-based filtering, and spatial searches.
When Should You Use Spatial Data in EF Core?
Location-Based Apps – Track users, restaurants, stores, and landmarks.
Routing & Navigation – Calculate distances, find nearby places, optimize paths.
Geofencing – Detect when users enter or leave an area (e.g., delivery zones).
Real Estate & Mapping – Store city boundaries, zip codes, and regions.
Wrap-Up: Bringing GIS Power to EF Core
Spatial data in EF Core isn’t just for maps—it’s for anything that involves locations, distances, and geographic relationships. By using NetTopologySuite and EF Core’s spatial capabilities, you can:
Store real-world locations with proper geospatial types
Run optimized queries for nearby locations and distances
Use polygons for geofencing, city boundaries, and more
If you’re building anything with location tracking, maps, or spatial analytics, EF Core has the built-in tools to make it easy!
Are you using spatial data in your projects? Let’s talk in the comments!
Caleb
Isn’t IsWithinDistance going to assume your distance is in degrees, not meters? because its expecting a flat plane, not a round globe? there is no way to specify the units.
Chris Woodruff
No it is in meters for the distance.
https://nettopologysuite.github.io/NetTopologySuite/api/NetTopologySuite.Geometries.Geometry.html#NetTopologySuite_Geometries_Geometry_IsWithinDistance_NetTopologySuite_Geometries_Geometry_System_Double_
Caleb
Isn’t “IsWithinDistance” going to assume the unit is degrees, not meters, because it is expecting a flat plane?
Chris Woodruff
No it is in meters for the distance.
https://nettopologysuite.github.io/NetTopologySuite/api/NetTopologySuite.Geometries.Geometry.html#NetTopologySuite_Geometries_Geometry_IsWithinDistance_NetTopologySuite_Geometries_Geometry_System_Double_
Brian Raak
How would I assign vertices data to newYorkBoundry.Area?
Chris Woodruff
To load data into a NetTopologySuite.Geometries.Polygon, you typically need to create the polygon using a set of coordinates that define its boundary. Here’s how you can do it:
Example 1: Creating a Polygon from Coordinates
using NetTopologySuite.Geometries;
var geometryFactory = new GeometryFactory();
// Define the coordinates for the polygon (must form a closed ring)
var coordinates = new[]
{
new Coordinate(0, 0),
new Coordinate(0, 10),
new Coordinate(10, 10),
new Coordinate(10, 0),
new Coordinate(0, 0) // Closing the ring
};
// Create a LinearRing (required for the outer boundary of a polygon)
var linearRing = geometryFactory.CreateLinearRing(coordinates);
// Create the polygon
var polygon = geometryFactory.CreatePolygon(linearRing);
// Output the polygon's WKT (Well-Known Text) representation
Console.WriteLine(polygon);
Example 2: Loading a Polygon from GeoJSON
If your data is in GeoJSON format, you can use the GeoJsonReader:
using NetTopologySuite.IO;
using NetTopologySuite.Geometries;
var geoJsonReader = new GeoJsonReader();
string geoJson = @"{
'type': 'Polygon',
'coordinates': [[[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]]
}";
// Parse the GeoJSON into a Polygon object(geoJson);
var polygon = (Polygon)geoJsonReader.Read
// Output the polygon's perimeter
Console.WriteLine($"Perimeter: {polygon.Length}");
Key Notes
Closed Rings: The coordinates for a polygon must form a closed ring (i.e., the first and last coordinates must be the same).
Inner Rings: If the polygon has holes, you can define them using additional LinearRing objects for the inner boundaries.
GeometryFactory: Always use a GeometryFactory to create geometries, as it ensures consistency in precision and SRID (Spatial Reference System Identifier).