Skip to main content

๐ŸŒง๏ธ Cropped precision

Each polygon is pre-clipped to the exact circular area you queried โ€” no post-processing needed on your side.

โšก Binary-efficient

Protobuf encoding cuts payload size significantly compared to JSON, making it ideal for mobile and embedded clients.

๐Ÿ—บ๏ธ GeoJSON-ready

Two decode steps are all it takes to go from raw bytes to shapes you can drop straight into geojson.io or Mapbox.

What the endpoint returns

POST /api/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.
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.

Proto schema ๐Ÿ“„

Below are the proto message definitions used by iklim.co for this endpoint.
// 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;
}
Copy the full geojson.proto file into your project before running the decoder steps below. The protobufjs loader needs the file at runtime.

Decoding & converting to GeoJSON ๐Ÿ› ๏ธ

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

Install protobufjs ๐Ÿ“ฆ

npm install protobufjs
# or
yarn add protobufjs
2

Fetch the binary response ๐ŸŒ

Tell fetch to read the response as an ArrayBuffer, then wrap it in a Uint8Array for the proto decoder.
const response = await fetch("/api/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);
3

Decode the protobuf bytes ๐Ÿ”“

Load geojson.proto and decode the bytes into a JavaScript object.
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:
{
  "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
    }
  }]
}
4

Convert to standard GeoJSON ๐Ÿ—บ๏ธ

Transform the decoded object into RFC 7946-compliant GeoJSON.
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));

Field reference ๐Ÿ“š

PrecipitationFeature

FieldTypeDescription
idstringUnique identifier of the radar geometry record.
geometryobjectCropped MultiPolygon geometry (see below).
propertiesobjectTyped precipitation metadata.

geometry (MultiPolygon)

FieldPathDescription
typegeometry.typeAlways MULTI_POLYGON in the proto object; becomes "MultiPolygon" in GeoJSON.
polygonsgeometry.multiPolygon.polygons[]Array of polygons. Each polygon has one or more rings.
ringspolygons[].rings[]First ring is the exterior boundary; additional rings are holes.
coordinatesrings[].coordinates[]Array of { lng, lat } objects โ†’ convert to [lng, lat] arrays for GeoJSON.

properties

FieldTypeDescription
intensitystringPrecipitation intensity label. See the IntensityType catalog below.
radarTimestampnumberUTC epoch milliseconds of the radar scan that detected this event.
forecastbooleantrue = NWP (Numerical Weather Prediction) model forecast polygon; false = live radar observation.

IntensityType catalog

ValueDisplay nameMap color
DRIZZLEDrizzle#7ae1e8
LIGHTLight#00c8d8
MODERATEModerate#2d7bea
HEAVYHeavy#be46eb
VERY_HEAVYVery Heavy#e36546
EXTREMEExtreme#ecd940