Table of Contents

Model coordinates and nearby searches

Trellis.Primitives provides storage-neutral geographic values for validating latitude and longitude, measuring approximate in-memory distance, and building a safe first-pass radius filter.

Use GeoCoordinate for a point and GeoBounds for a center plus radius. A bounds object exposes one or two non-wrapping GeoBoundingBox values, including antimeridian cases.

dotnet add package Trellis.Primitives

Build a nearby-search value

The following Program.cs validates search input, builds conservative bounds, and checks a trusted candidate:

using Trellis;
using Trellis.Primitives;

var searchResult = NearbySearch.TryCreate(
    latitude: 47.6062,
    longitude: -122.3321,
    radiusMeters: 10_000);

if (!searchResult.TryGetValue(out var search, out var error))
{
    Console.Error.WriteLine(error);
    return;
}

var candidate = GeoCoordinate.Create(47.6205, -122.3493);
bool insideBroadBounds = search.Bounds.Contains(candidate);
double exactMeters = search.Center.DistanceMetersTo(candidate);

Console.WriteLine($"candidate={insideBroadBounds}, distance={exactMeters:N0}m");

public sealed record NearbySearch(GeoCoordinate Center, GeoBounds Bounds)
{
    public static Result<NearbySearch> TryCreate(
        double latitude,
        double longitude,
        double radiusMeters) =>
        GeoCoordinate.TryCreate(latitude, longitude, "center")
            .Bind(center =>
                GeoBounds.TryCreate(center, radiusMeters, nameof(radiusMeters))
                    .Map(bounds => new NearbySearch(center, bounds)));
}

GeoCoordinate.TryCreate validates both components and can report both failures. Latitude must be within -90..90, longitude within -180..180, and neither may be NaN or infinity.

Use bounds as a prefilter

GeoBounds.Contains answers whether a point is inside a conservative rectangle. It can include points outside the requested circle, so follow it with an exact distance comparison:

bool isMatch =
    search.Bounds.Contains(candidate) &&
    search.Center.DistanceMetersTo(candidate) <= search.Bounds.RadiusMeters;

DistanceMetersTo uses a spherical haversine approximation. It is appropriate for common nearby-search behavior, not surveying, altitude-aware calculations, or ellipsoidal geodesics.

GeoCoordinateExpressions requires the separate Trellis.EntityFrameworkCore package. Install it, then follow the computed-distance query walkthrough for numeric latitude and longitude columns. The expressions need no DI or interceptor registration. Use a provider-native spatial type instead when you need a spatial index.

Serialization and equality

GeoCoordinate serializes as an object with numeric latitude and longitude fields and deserializes through the validating factory. Equality compares the stored components exactly; it does not apply an epsilon or normalize equivalent longitudes.

Complete generated API: