---
title: Geospatial Types
---

import { Tabs, TabItem } from '@astrojs/starlight/components';

Chronicle has built-in support for geospatial types from Cratis.Fundamentals: `Point`, `LineString`, and `Polygon`. You can use them directly in events, read models, reducers, and projections without any extra setup.

:::note[Client coverage]
Geospatial types are currently C#-only — Kotlin, Elixir, and TypeScript don't have an equivalent `Point`/`LineString`/`Polygon` type in their client SDKs.
:::

## Geospatial Types

The three geospatial types are records defined in the `Cratis.Geospatial` namespace:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Geospatial;

public static class GeospatialTypeExamples
{
    public static Point CreatePoint() => new(10.456, 42.123);

    public static LineString CreatePath() => new([
        new Point(10.456, 42.123),
        new Point(11.789, 43.456)
    ]);

    public static Polygon CreateBoundary() => new(
        Shell: new LinearRing([
            new Point(0, 0),
            new Point(10, 0),
            new Point(10, 10),
            new Point(0, 10),
            new Point(0, 0)
        ]),
        Holes: []);
}
```

</TabItem>
</Tabs>

- **Point** represents a single geographic location in longitude-then-latitude order (GeoJSON standard).
- **LineString** represents a connected series of points forming a line or path.
- **Polygon** represents an enclosed area with a shell (outer boundary) and optional holes (inner boundaries).
- **LinearRing** is an array of points that form a closed loop (used by Polygon).

## Using Geospatial Types in Events and Read Models

Import the namespace and use the geospatial types as any other property type:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Events;
using Cratis.Geospatial;

[EventType]
public record GeospatialAssetLocationUpdated(Point Location);

[EventType]
public record GeospatialRouteCreated(LineString Path);

[EventType]
public record GeospatialZoneEstablished(Polygon Boundaries);

public record GeospatialAssetReadModel(Guid Id, Point Location);
public record GeospatialRouteReadModel(Guid Id, LineString Path);
public record GeospatialZoneReadModel(Guid Id, Polygon Boundaries);
```

</TabItem>
</Tabs>

Projections and reducers handle geospatial types like any other value type. AutoMap picks them up automatically:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
using Cratis.Geospatial;

[EventType]
public record GeospatialProjAssetLocationUpdated(Point Location);

public record GeospatialProjAssetReadModel(Guid Id, Point Location);

public class GeospatialProjAssetProjection : IProjectionFor<GeospatialProjAssetReadModel>
{
    // AutoMap picks up the Point-typed property automatically — no manual mapping needed
    public void Define(IProjectionBuilderFor<GeospatialProjAssetReadModel> builder) => builder
        .From<GeospatialProjAssetLocationUpdated>();
}
```

</TabItem>
</Tabs>

## JSON Schema

When Chronicle generates the JSON schema, geospatial types are annotated with their GeoJSON-compatible formats:

**Point**: Generates as an object with `longitude` and `latitude` properties, marked with `"format": "point"`

```json
{
  "type": "object",
  "format": "point",
  "properties": {
    "longitude": { "type": "number" },
    "latitude": { "type": "number" }
  }
}
```

**LineString**: Generates as an array of point objects, marked with `"format": "linestring"`

```json
{
  "type": "array",
  "format": "linestring",
  "items": {
    "type": "object",
    "properties": {
      "longitude": { "type": "number" },
      "latitude": { "type": "number" }
    }
  }
}
```

**Polygon**: Generates as an object with `shell` and `holes`, marked with `"format": "polygon"`

```json
{
  "type": "object",
  "format": "polygon",
  "properties": {
    "shell": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "longitude": { "type": "number" },
          "latitude": { "type": "number" }
        }
      }
    },
    "holes": {
      "type": "array",
      "items": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "longitude": { "type": "number" },
            "latitude": { "type": "number" }
          }
        }
      }
    }
  }
}
```

This lets consumers inspect the schema and know the field contains geographic data, and the format annotations make it possible for tools like the Workbench to recognize and properly display geographic values.

## Storage

### MongoDB Sink

Chronicle stores geospatial values in BSON format:

**Point**: Flat document with `longitude` and `latitude`

```json
{ "longitude": 10.456, "latitude": 42.123 }
```

**LineString**: Array of points

```json
[
  { "longitude": 10.456, "latitude": 42.123 },
  { "longitude": 11.789, "latitude": 43.456 }
]
```

**Polygon**: Document with `shell` (outer ring) and `holes` (inner rings)

```json
{
  "shell": [
    { "longitude": 0, "latitude": 0 },
    { "longitude": 10, "latitude": 0 },
    { "longitude": 10, "latitude": 10 },
    { "longitude": 0, "latitude": 10 },
    { "longitude": 0, "latitude": 0 }
  ],
  "holes": []
}
```

BSON serializers are registered automatically — no configuration required.

### SQL Sink

Geospatial types generate as JSON objects in the schema, so the SQL sink stores them as JSON columns. No migration or configuration changes are required.

## Workbench Display

The Chronicle Workbench recognizes geospatial format markers in event schemas and renders them as human-readable strings rather than expanding nested structures:

```text
Point: lat: 42.123, long: 10.456
LineString: [lat: 42.123, long: 10.456], [lat: 43.456, long: 11.789]
Polygon: shell with 5 points, 0 holes
```
