Skip to content

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.

  • 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
  1. Install the NuGet package

    Terminal window
    dotnet add package Shiny.Spatial
  2. 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)
    );
  3. Insert features

    var feature = new SpatialFeature(new Point(-104.99, 39.74))
    {
    Properties = { ["name"] = "Denver", ["population"] = 715000L }
    };
    long id = table.Insert(feature);
  4. Query spatially

    // Find all cities within 150km of Denver
    var results = table.FindWithinDistance(
    new Coordinate(-104.99, 39.74),
    distanceMeters: 150_000
    );
var features = cities.Select(c => new SpatialFeature(new Point(c.Longitude, c.Latitude))
{
Properties = { ["name"] = c.Name, ["population"] = c.Population }
}).ToList();
table.BulkInsert(features);
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);
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 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"

Shiny.Spatial uses a two-pass query pipeline to deliver both speed and accuracy:

  1. 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).

  2. 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.

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
FrameworkSupport
.NET 10+Full support with AOT/trimming
.NET Standard 2.0Full support (Xamarin, .NET Framework, etc.)
iOS / AndroidFull support via Microsoft.Data.Sqlite
Windows / macOS / LinuxFull support

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

Terminal window
claude plugin add github:shinyorg/skills

GitHub Copilot — Copy the shiny-spatial skill file into your repository’s custom instructions.