For AI agents: this page is also available as Markdown at /docs/geo-shapes.md. Every docs page is: add .md to its URL. llms.txt lists them all.

Geo shapes

A geo_point field holds locations. A geo_shape field holds geometry: delivery zones, service areas, neighborhoods, parcels, routes, or any mix of polygons, lines and points. Queries then ask how each document's shape relates to a query shape: does this zone cover the customer's address, does this route cross the closure, which parcels lie entirely inside the district.

The examples add a delivery area to some of the places in the Geo search quickstart.

Define a geo_shape field

curl -X PATCH https://api.warpsearch.io/v1/collections/places/schema \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fields": {"delivery_area": "geo_shape"}}'

With settings, the definition is an object with params:

{"fields": {"delivery_area": {"type": "geo_shape", "params": {"max_cells": 64, "err_pct": 0.01}}}}
curl -X PUT https://api.warpsearch.io/compat/es/places/_mapping \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"properties": {"delivery_area": {"type": "geo_shape"}}}'

The mapping accepts orientation, ignore_malformed and distance_error_pct (the err_pct setting below). index, doc_values and ignore_z_value are accepted when true, and coerce when false. The legacy prefix-tree settings (tree, strategy, precision, tree_levels, points_only), coerce: true, copy_to and multi-fields are rejected with a message naming the setting.

Setting Range Default Effect
max_cells 1 to 1024 8 to 64, growing with the shape's vertex count The most grid cells used to index one shape.
max_level 2 to 32 24 The finest grid level used to index a shape. Odd values round down.
err_pct 0 to 0.5 0.025 Stop refining a shape's cells once they are this fraction of its bounding box.
orientation right (or ccw, counterclockwise), left (or cw, clockwise) right The winding of polygon outer rings in WKT values, used to decide whether a wide polygon crosses the date line.
pyramid true, false true Also index each cell's ancestors, which speeds up queries with large query shapes at the cost of more index terms.

Beside type and params, a geo_shape definition takes ignore_malformed and parent. Other options, such as index, are rejected. The settings decide how shapes are indexed, so changing them on an existing field is refused with 409 incompatible_schema; add a new field instead. See Accuracy and index size for how to choose them.

Send shapes

A shape value is a GeoJSON object, a WKT string (or GeoJSON text in a string), or a point in the [lon, lat] or {"lat": ..., "lon": ...} form. Positions are longitude first.

curl -X POST "https://api.warpsearch.io/v1/collections/places/documents?commit=true" \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {"id": "p1", "name": "Harbor Coffee", "description": "Espresso, pour-over and pastries",
     "category": "cafe", "city": "Manhattan", "rating": 4.6, "location": [-73.9851, 40.7589],
     "delivery_area": {"type": "Polygon", "coordinates": [[[-74.00, 40.745], [-73.97, 40.745],
       [-73.97, 40.775], [-74.00, 40.775], [-74.00, 40.745]]]}},
    {"id": "p3", "name": "Midtown Market", "description": "Groceries, deli and a coffee bar",
     "category": "grocery", "city": "Manhattan", "rating": 4.1, "location": [-73.9772, 40.7527],
     "delivery_area": "POLYGON ((-73.99 40.74, -73.96 40.74, -73.96 40.765, -73.99 40.765, -73.99 40.74))"},
    {"id": "p5", "name": "Hudson Espresso", "description": "Espresso bar by the ferry",
     "category": "cafe", "city": "Hoboken", "rating": 4.5, "location": [-74.0324, 40.7440],
     "delivery_area": "POLYGON ((-74.05 40.73, -74.01 40.73, -74.01 40.76, -74.05 40.76, -74.05 40.73))"}
  ]'

A write replaces the whole document, so the example sends every field again.

Shape WKT GeoJSON type
Point, several points POINT, MULTIPOINT Point, MultiPoint
Line, several lines LINESTRING, MULTILINESTRING LineString, MultiLineString
Polygon with holes, several polygons POLYGON, MULTIPOLYGON Polygon, MultiPolygon
Box BBOX (minLon, maxLon, maxLat, minLat) envelope, "coordinates": [[minLon, maxLat], [maxLon, minLat]]
Mixed GEOMETRYCOLLECTION GeometryCollection

Name the field in fields (or _source) to get the shape back as GeoJSON. The coordinates are the stored grid positions, so they can differ from the input in about the eighth decimal place.

Query shape fields

geo_shape relates each document's shape to a query shape. Which places deliver to an address near Bryant Park (40.7520, -73.9850)?

{
  "language": "es",
  "query": {"geo_shape": {"delivery_area": {"shape": "POINT (-73.9850 40.7520)", "relation": "intersects"}}},
  "fields": ["name"]
}
{
  "query": {"bool": {"filter": [{"geo_shape": {"delivery_area": {
    "shape": {"type": "Point", "coordinates": [-73.9850, 40.7520]},
    "relation": "intersects"
  }}}]}},
  "_source": ["name"]
}

This matches Harbor Coffee (p1) and Midtown Market (p3): the address is inside both of their delivery areas, and outside Hudson Espresso's.

Relation Matches documents whose shape
intersects (default) Shares at least one point with the query shape.
disjoint Shares no point with it. Documents without the field do not match.
within Lies inside the query shape: each part of the document's shape inside one part of the query shape.
contains Contains the query shape, read the same way: each part of the query shape inside one part of the document's shape.

The point queries work on shape fields too, as intersects queries: geo_distance matches shapes that come within the radius, geo_bounding_box shapes that meet the box, and geo_polygon shapes that meet the polygon. Areas within 1 km of Times Square:

{
  "language": "es",
  "query": {"geo_distance": {"distance": "1km", "delivery_area": "40.7580,-73.9855"}}
}

A geo_grid query with a geohash or geotile key matches the shapes that meet the cell. Cells include their edges, so a shape that touches a cell edge matches both cells, the same documents the shape grid aggregation counts in that bucket. geohex cells are rejected on shape fields with 400.

Not supported on shape fields: sorting by distance, geo_nearest and distance_feature. For those, keep a representative point, such as a zone's center, in a geo_point field beside the shape.

Aggregate shape fields

The grid aggregations geohash_grid and geotile_grid and the geo_bounds and geo_centroid metrics work on shape fields, in the same request forms as on points (see Geo aggregations):

{
  "language": "lucene",
  "query": "*:*",
  "limit": 0,
  "facets": {"tiles": {"type": "geotile_grid", "field": "delivery_area", "precision": 14}},
  "metrics": {"coverage": {"func": "geo_bounds", "field": "delivery_area"}}
}
{
  "size": 0,
  "aggs": {
    "tiles": {"geotile_grid": {"field": "delivery_area", "precision": 14}},
    "coverage": {"geo_bounds": {"field": "delivery_area"}}
  }
}

Accuracy and index size

A shape is indexed as a set of grid cells that cover it, plus its exact geometry. A query first uses the cells to find candidates, accepts the documents whose cells settle the answer, and checks the rest against the exact geometry. Results are the same whatever the settings; the settings trade index size against how many candidates need that check.

To Change Cost
Make queries check fewer candidates Raise max_cells, lower err_pct, raise max_level More index terms per shape: a larger index and slower writes
Shrink the index for many small or simple shapes Lower max_cells, raise err_pct More candidates to check at query time
Speed up queries with large query shapes (a state, a country) Keep pyramid: true (the default) More terms per shape
Save index space when query shapes are small pyramid: false Large query shapes read more terms

The defaults suit most data. Change them when a profile of real queries shows many candidates being checked, or when index size matters more than query time. Very detailed shapes, such as coastlines with tens of thousands of vertices, cost more to check: simplify them before indexing when exact borders are not needed.

View this page as Markdown

Cookie settings

Optional tracking stays off until you choose it. Your choice applies to this browser for 180 days. Read the details.

Essential storage keeps you signed in, protects forms, and remembers this choice. It is always available.