Getting Started
A lightweight, dependency-free geospatial database for .NET that combines SQLite R*Tree spatial indexing with custom C# geometry algorithms for high-performance spatial queries. No SpatiaLite, no NetTopologySuite — just SQLite and math.
Features
Section titled “Features”- Two-pass query pipeline — R*Tree bounding box filter (SQL, O(log n)) followed by C# geometry refinement for exact results
- Dual coordinate systems — WGS84 (GPS/geographic) with Haversine distance and Cartesian with Euclidean distance
- Full geometry type support — Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection
- WKB serialization — Well-Known Binary encoding/decoding for all geometry types
- Fluent query builder — Chain spatial filters, property filters, sorting, and paging
- Bulk insert — Transaction-wrapped batch inserts, ~20% faster than individual inserts
- Pre-built databases — US states, US cities, Canadian provinces, and Canadian cities included
- AOT-compatible and trimmable — works with ahead-of-time compilation on all .NET platforms
- Zero geospatial dependencies — only depends on
Microsoft.Data.Sqlite
-
Install the NuGet package
Terminal window dotnet add package Shiny.Spatial -
Create a database and spatial table
using Shiny.Spatial.Database;using Shiny.Spatial.Geometry;using var db = new SpatialDatabase("mydata.db");var table = db.CreateTable("cities",CoordinateSystem.Wgs84,new PropertyDefinition("name", PropertyType.Text),new PropertyDefinition("population", PropertyType.Integer)); -
Insert features
var feature = new SpatialFeature(new Point(-104.99, 39.74)){Properties = { ["name"] = "Denver", ["population"] = 715000L }};long id = table.Insert(feature); -
Query spatially
// Find all cities within 150km of Denvervar results = table.FindWithinDistance(new Coordinate(-104.99, 39.74),distanceMeters: 150_000);
Quick Examples
Section titled “Quick Examples”Bulk insert
Section titled “Bulk insert”var features = cities.Select(c => new SpatialFeature(new Point(c.Longitude, c.Latitude)){ Properties = { ["name"] = c.Name, ["population"] = c.Population }}).ToList();
table.BulkInsert(features);Polygon intersection
Section titled “Polygon intersection”var colorado = new Polygon(new[]{ new Coordinate(-109.05, 37.0), new Coordinate(-102.05, 37.0), new Coordinate(-102.05, 41.0), new Coordinate(-109.05, 41.0), new Coordinate(-109.05, 37.0)});
var citiesInColorado = table.FindIntersecting(colorado);Fluent query builder
Section titled “Fluent query builder”var center = new Coordinate(-104.99, 39.74);
var results = table.Query() .WithinDistance(center, 150_000) .WhereProperty("population", ">", 200000L) .OrderByDistance(center) .Limit(10) .ToList();Using a pre-built database
Section titled “Using a pre-built database”using var db = new SpatialDatabase("databases/us-states.db");var states = db.GetTable("states");
var denver = new Point(-104.99, 39.74);var result = states.FindIntersecting(denver);// result[0].Properties["name"] == "Colorado"Architecture
Section titled “Architecture”Shiny.Spatial uses a two-pass query pipeline to deliver both speed and accuracy:
-
Pass 1 — R*Tree bounding box filter (SQL): SQLite’s R*Tree index rapidly eliminates candidates whose bounding boxes don’t overlap the query region. This runs entirely in SQL at O(log n).
-
Pass 2 — C# geometry refinement: Remaining candidates are tested with exact geometry algorithms (point-in-polygon, segment intersection, Haversine distance). This eliminates false positives from the bounding box approximation.
This approach eliminates 99%+ of candidates before expensive geometry checks, delivering sub-millisecond queries on datasets of 100K+ features.
SQLite Schema
Section titled “SQLite Schema”Each spatial table creates an R*Tree virtual table with auxiliary columns:
CREATE VIRTUAL TABLE {name}_rtree USING rtree( id, min_x, max_x, min_y, max_y, +geometry BLOB, -- WKB-encoded geometry +prop_name TEXT, -- user-defined property columns +prop_population INTEGER);Metadata is tracked in two internal tables:
__spatial_meta— table coordinate systems__spatial_columns— property definitions
Supported Platforms
Section titled “Supported Platforms”| Framework | Support |
|---|---|
| .NET 10+ | Full support with AOT/trimming |
| .NET Standard 2.0 | Full support (Xamarin, .NET Framework, etc.) |
| iOS / Android | Full support via Microsoft.Data.Sqlite |
| Windows / macOS / Linux | Full support |
AI Coding Assistant
Section titled “AI Coding Assistant”An AI skill is available for Shiny Spatial to help generate spatial queries, configure databases, and follow best practices directly in your IDE.
Claude Code
claude plugin add github:shinyorg/skillsGitHub Copilot — Copy the shiny-spatial skill file into your repository’s custom instructions.