> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.iklim.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Precipitation Geometry

> Learn how to decode the protobuf-encoded geometry response from the /v1/precipitations/geometry endpoint and render it as standard GeoJSON.

<CardGroup cols={3}>
  <Card title="🌧️ Cropped precision" icon="crop-simple">
    Each polygon is pre-clipped to the exact circular area you queried — no post-processing needed on your side.
  </Card>

  <Card title="⚡ Binary-efficient" icon="bolt">
    Protobuf encoding cuts payload size significantly compared to JSON, making it ideal for mobile and embedded clients.
  </Card>

  <Card title="🗺️ GeoJSON-ready" icon="map">
    Two decode steps are all it takes to go from raw bytes to shapes you can drop straight into geojson.io or Mapbox.
  </Card>
</CardGroup>

## What the endpoint returns

`POST /v1/precipitations/geometry` responds with a raw binary body (`Content-Type: application/x-protobuf`).
The bytes encode a `PrecipitationFeatureCollection` message — a list of `PrecipitationFeature` items, each carrying a cropped `MultiPolygon` geometry and typed precipitation properties.

<Warning>
  Do **not** try to parse the response body as JSON or text. It is a protobuf binary stream and will look like garbage if treated as a string.
</Warning>

***

## Proto schema 📄

Below are the proto message definitions used by iklim.co for this endpoint.

```protobuf theme={null}
// Shared geometry primitives
message Coordinate {
  double lng = 1;
  double lat = 2;
}

message Ring {
  repeated Coordinate coordinates = 1;
}

message PolygonShape {
  repeated Ring rings = 1;
}

message MultiPolygonShape {
  repeated PolygonShape polygons = 1;
}

message Geometry {
  enum GeometryType {
    POINT              = 0;
    LINE_STRING        = 1;
    POLYGON            = 2;
    MULTI_POINT        = 3;
    MULTI_LINE_STRING  = 4;
    MULTI_POLYGON      = 5;
    GEOMETRY_COLLECTION = 6;
  }

  GeometryType type = 1;

  oneof shape {
    Coordinate           point              = 2;
    LineStringShape      line_string        = 3;
    PolygonShape         polygon            = 4;
    MultiPointShape      multi_point        = 5;
    MultiLineStringShape multi_line_string  = 6;
    MultiPolygonShape    multi_polygon      = 7;
    GeometryCollectionShape geometry_collection = 8;
  }
}

// Precipitation-specific messages
message PrecipitationProperties {
  string intensity        = 1;   // e.g. "DRIZZLE", "LIGHT", "MODERATE"
  int64  radar_timestamp  = 2;   // epoch milliseconds
  bool   forecast         = 3;   // true = NWP (Numerical Weather Prediction) forecast, false = radar observation
}

message PrecipitationFeature {
  string                   id         = 1;
  Geometry                 geometry   = 2;
  PrecipitationProperties  properties = 3;
}

message PrecipitationFeatureCollection {
  repeated PrecipitationFeature features = 1;
}
```

<Tip>
  Copy the full `geojson.proto` file into your project before running the decoder steps below. The `protobufjs` loader needs the file at runtime.
</Tip>

***

## Decoding & converting to GeoJSON 🛠️

<Note>
  **Why two steps?** GeoJSON is a JSON text format (RFC 7946) — it has no official protobuf schema. Its coordinates are bare nested arrays (`[[lng, lat], ...]`), which protobuf cannot represent without a named wrapper message. iklim.co's binary schema uses typed messages (`Coordinate`, `Ring`, `PolygonShape`, …) optimised for compact encoding, so a structural reshaping step is always required after decoding.
</Note>

<Tabs>
  <Tab title="Step-by-step guide 🪜" icon="list-ol">
    <Steps>
      <Step title="Install protobufjs 📦">
        ```bash theme={null}
        npm install protobufjs
        # or
        yarn add protobufjs
        ```
      </Step>

      <Step title="Fetch the binary response 🌐">
        Tell `fetch` to read the response as an `ArrayBuffer`, then wrap it in a `Uint8Array` for the proto decoder.

        ```javascript theme={null}
        const response = await fetch("/v1/precipitations/geometry", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "Authorization": "Bearer <your-token>"
          },
          body: JSON.stringify({
            eventIds: ["69baa6b76e0fe76a1957273f", "4a1c3e9d8f2b07e5c6d4a1b2"],
            center: { lat: 36.80, lng: 32.33 },
            radius: 25000
          })
        });

        const buffer = await response.arrayBuffer();
        const bytes  = new Uint8Array(buffer);
        ```
      </Step>

      <Step title="Decode the protobuf bytes 🔓">
        Load `geojson.proto` and decode the bytes into a JavaScript object.

        ```javascript theme={null}
        import protobuf from "protobufjs";

        const root = await protobuf.load("geojson.proto");
        const PrecipitationFeatureCollection = root.lookupType(
          "PrecipitationFeatureCollection"
        );

        const fc = PrecipitationFeatureCollection.decode(bytes);
        ```

        At this point `fc` looks like:

        ```json theme={null}
        {
          "features": [{
            "id": "69baa6b76e0fe76a1957273f",
            "geometry": {
              "type": "MULTI_POLYGON",
              "multiPolygon": {
                "polygons": [{
                  "rings": [{
                    "coordinates": [
                      { "lng": 32.33, "lat": 36.80 },
                      { "lng": 32.34, "lat": 36.81 }
                    ]
                  }]
                }]
              }
            },
            "properties": {
              "intensity": "DRIZZLE",
              "radarTimestamp": 1773846900000,
              "forecast": true
            }
          }]
        }
        ```
      </Step>

      <Step title="Convert to standard GeoJSON 🗺️">
        Transform the decoded object into RFC 7946-compliant GeoJSON.

        ```javascript theme={null}
        function toGeoJson(fc) {
          return {
            type: "FeatureCollection",
            features: fc.features.map(feature => ({
              type: "Feature",
              id: feature.id,
              geometry: {
                type: "MultiPolygon",
                coordinates: feature.geometry.multiPolygon.polygons.map(polygon =>
                  polygon.rings.map(ring =>
                    ring.coordinates.map(coord => [coord.lng, coord.lat])
                  )
                )
              },
              properties: {
                intensity:      feature.properties.intensity,
                radarTimestamp: feature.properties.radarTimestamp,
                forecast:       feature.properties.forecast
              }
            }))
          };
        }

        const geoJson = toGeoJson(fc);
        console.log(JSON.stringify(geoJson, null, 2));
        ```
      </Step>
    </Steps>
  </Tab>

  <Tab title="All-in-one snippet ⚡" icon="code">
    Drop this single async function into your project — it handles fetch, decode, and GeoJSON conversion end-to-end.

    ```javascript theme={null}
    import protobuf from "protobufjs";

    async function fetchPrecipitationGeoJson({ eventIds, center, radius, token }) {
      // 1 — Fetch binary response
      const response = await fetch("/v1/precipitations/geometry", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${token}`
        },
        body: JSON.stringify({ eventIds, center, radius })
      });

      if (!response.ok) throw new Error(`HTTP ${response.status}`);

      const bytes = new Uint8Array(await response.arrayBuffer());

      // 2 — Decode protobuf
      const root = await protobuf.load("geojson.proto");
      const PrecipitationFeatureCollection = root.lookupType(
        "PrecipitationFeatureCollection"
      );
      const fc = PrecipitationFeatureCollection.decode(bytes);

      // 3 — Convert to GeoJSON
      return {
        type: "FeatureCollection",
        features: fc.features.map(f => ({
          type: "Feature",
          id: f.id,
          geometry: {
            type: "MultiPolygon",
            coordinates: f.geometry.multiPolygon.polygons.map(poly =>
              poly.rings.map(ring =>
                ring.coordinates.map(c => [c.lng, c.lat])
              )
            )
          },
          properties: {
            intensity:      f.properties.intensity,
            radarTimestamp: f.properties.radarTimestamp,
            forecast:       f.properties.forecast
          }
        }))
      };
    }

    // Usage
    const geoJson = await fetchPrecipitationGeoJson({
      eventIds: ["69baa6b76e0fe76a1957273f", "4a1c3e9d8f2b07e5c6d4a1b2"],
      center:   { lat: 36.80, lng: 32.33 },
      radius:   25000,
      token:    "<your-token>"
    });
    ```
  </Tab>

  <Tab title="GeoJSON output 📋" icon="file-code">
    The final GeoJSON object is RFC 7946-compliant and renders correctly in any GeoJSON viewer.

    ```json theme={null}
    {
      "type": "FeatureCollection",
      "features": [
        {
          "type": "Feature",
          "id": "69baa6b76e0fe76a1957273f",
          "geometry": {
            "type": "MultiPolygon",
            "coordinates": [
              [
                [
                  [32.33, 36.80],
                  [32.34, 36.81],
                  [32.35, 36.80],
                  [32.33, 36.80]
                ]
              ]
            ]
          },
          "properties": {
            "intensity": "DRIZZLE",
            "radarTimestamp": 1773846900000,
            "forecast": true
          }
        }
      ]
    }
    ```

    <Tip>
      🧪 Copy the output above and paste it into **[geojson.io](https://geojson.io/)** to visually verify your geometry is correct before integrating with your map renderer.
    </Tip>
  </Tab>
</Tabs>

***

## Field reference 📚

### `PrecipitationFeature`

| Field        | Type   | Description                                     |
| ------------ | ------ | ----------------------------------------------- |
| `id`         | string | Unique identifier of the radar geometry record. |
| `geometry`   | object | Cropped `MultiPolygon` geometry (see below).    |
| `properties` | object | Typed precipitation metadata.                   |

### `geometry` (MultiPolygon)

| Field         | Path                               | Description                                                                      |
| ------------- | ---------------------------------- | -------------------------------------------------------------------------------- |
| `type`        | `geometry.type`                    | Always `MULTI_POLYGON` in the proto object; becomes `"MultiPolygon"` in GeoJSON. |
| `polygons`    | `geometry.multiPolygon.polygons[]` | Array of polygons. Each polygon has one or more `rings`.                         |
| `rings`       | `polygons[].rings[]`               | First ring is the exterior boundary; additional rings are holes.                 |
| `coordinates` | `rings[].coordinates[]`            | Array of `{ lng, lat }` objects → convert to `[lng, lat]` arrays for GeoJSON.    |

### `properties`

| Field            | Type    | Description                                                                                           |
| ---------------- | ------- | ----------------------------------------------------------------------------------------------------- |
| `intensity`      | string  | Precipitation intensity label. See the `IntensityType` catalog below.                                 |
| `radarTimestamp` | number  | UTC epoch milliseconds of the radar scan that detected this event.                                    |
| `forecast`       | boolean | `true` = NWP (Numerical Weather Prediction) model forecast polygon; `false` = live radar observation. |

#### `IntensityType` catalog

| Value        | Display name | Map color |
| ------------ | ------------ | --------- |
| `DRIZZLE`    | Drizzle      | `#7ae1e8` |
| `LIGHT`      | Light        | `#00c8d8` |
| `MODERATE`   | Moderate     | `#2d7bea` |
| `HEAVY`      | Heavy        | `#be46eb` |
| `VERY_HEAVY` | Very Heavy   | `#e36546` |
| `EXTREME`    | Extreme      | `#ecd940` |
