Geospatial data & queries

This section documents the current Elasticsearch 9.x line (with 8.19 as the final 8.x release) as published at the Elasticsearch documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. Some capabilities (Kibana-only UIs, the ML/NLP model-management workflow, cross-cluster replication, and parts of the paid / serverless-only surface) are linked, not documented in depth.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, as Elasticsearch iterates quickly.

This section’s bibliography lists the reference material consulted while preparing these pages.

Elasticsearch indexes points and shapes on the WGS84 ellipsoid so you can filter, sort and bucket documents by location. This page covers the spatial field types, the coordinate formats they accept (and the lon/lat ordering trap), the four geo queries, and the geo aggregations that drive map visualisations.

Spatial field types

A field must be mapped as a spatial type before any geo query or aggregation can use it — dynamic mapping never infers one. Elasticsearch has two geographic types and two planar (Cartesian) equivalents.

Type What it stores

geo_point

A single lat/lon point on Earth. Distance and bounding-box queries, geo aggregations.

geo_shape

Points, lines, polygons and collections on Earth. Spatial-relation queries.

point

A single planar x/y point — no Earth curvature. For CAD, game maps, virtual worlds.

shape

Arbitrary planar geometry. Same relation queries as geo_shape, in a flat coordinate space.

geo_point and coordinate formats

PUT /venues
{
  "mappings": {
    "properties": {
      "name":     { "type": "keyword" },
      "location": { "type": "geo_point" }
    }
  }
}
// https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html

A geo_point value can be written five ways, and two of them disagree on axis order — the classic Elasticsearch geo bug:

// 1. Object with explicit keys -- order-independent, and the clearest choice.
PUT /venues/_doc/1
{ "name": "Opera", "location": { "lat": 40.4231, "lon": -3.6884 } }

// 2. String "lat,lon" -- latitude FIRST.
PUT /venues/_doc/2
{ "name": "Prado", "location": "40.4138,-3.6921" }

// 3. Array [lon, lat] -- longitude FIRST, the REVERSE of the string form (GeoJSON order).
PUT /venues/_doc/3
{ "name": "Retiro", "location": [-3.6828, 40.4153] }

// 4. WKT POINT -- longitude first, like the array.
PUT /venues/_doc/4
{ "name": "Cibeles", "location": "POINT (-3.6923 40.4193)" }

// 5. Geohash string -- precision follows the hash length.
PUT /venues/_doc/5
{ "name": "Sol", "location": "ezjmgtt8" }
// https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html

The array and WKT forms take [lon, lat]; the object and string forms take latitude first. Pick the object form in application code to avoid the confusion. An array of geo_point values in one field is allowed and makes the document match if any point matches.

geo_shape: GeoJSON and WKT

geo_shape accepts GeoJSON geometry objects and Well-Known Text. GeoJSON coordinates are always [lon, lat]; a polygon’s outer ring should be listed counter-clockwise and be closed (first point repeated last).

PUT /zones
{
  "mappings": {
    "properties": {
      "name":   { "type": "keyword" },
      "region": { "type": "geo_shape" }
    }
  }
}
// https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html

// GeoJSON polygon.
PUT /zones/_doc/1
{
  "name": "downtown",
  "region": {
    "type": "polygon",
    "coordinates": [
      [[-3.71,40.40],[-3.68,40.40],[-3.68,40.43],[-3.71,40.43],[-3.71,40.40]]
    ]
  }
}

// The same kind of shape as WKT.
PUT /zones/_doc/2
{
  "name": "north",
  "region": "POLYGON ((-3.71 40.43, -3.68 40.43, -3.68 40.46, -3.71 40.46, -3.71 40.43))"
}
// point, linestring, multipolygon, envelope and geometrycollection are also accepted.
// https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html

envelope is an Elasticsearch shorthand for an axis-aligned rectangle: [[minLon, maxLat], [maxLon, minLat]] (top-left, bottom-right).

Cartesian point and shape

point and shape use the same geometry syntax but on a flat plane with no notion of poles or the antimeridian — use them for coordinates that are not positions on Earth.

PUT /parts
{
  "mappings": {
    "properties": {
      "pin":     { "type": "point" },
      "outline": { "type": "shape" }
    }
  }
}
// x/y (or WKT), any range, no wrap-around.
PUT /parts/_doc/1
{ "pin": { "x": 132.7, "y": -48.15 } }
// https://www.elastic.co/guide/en/elasticsearch/reference/current/point.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/shape.html

Geo queries

There are four geo queries. Run them inside a bool filter (or the top-level filter) so they skip scoring and get cached. See Geo queries for the set.

geo_bounding_box

Matches points inside an axis-aligned rectangle — the cheapest geo filter, and what a map UI issues as you pan.

GET /venues/_search
{
  "query": {
    "bool": {
      "filter": {
        "geo_bounding_box": {
          "location": {
            "top_left":     { "lat": 40.45, "lon": -3.75 },
            "bottom_right": { "lat": 40.38, "lon": -3.65 }
          }
        }
      }
    }
  }
}
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-bounding-box-query.html

geo_distance

Matches points within a radius of an origin, and pairs with _geo_distance sort to order hits by proximity.

GET /venues/_search
{
  "query": {
    "bool": {
      "filter": {
        "geo_distance": {
          "distance": "2km",
          "distance_type": "arc",
          "location": { "lat": 40.4168, "lon": -3.7038 }
        }
      }
    }
  },
  "sort": [
    { "_geo_distance": { "location": "40.4168,-3.7038", "order": "asc", "unit": "m" } }
  ]
}
// distance_type "arc"   -> great-circle on the ellipsoid, accurate everywhere (default)
// distance_type "plane" -> faster flat-Earth approximation; error grows with distance and latitude
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-distance-query.html

geo_shape (spatial relations)

Tests an indexed geo_shape (or geo_point) field against a query shape using a spatial predicate.

GET /zones/_search
{
  "query": {
    "geo_shape": {
      "region": {
        "shape": {
          "type": "envelope",
          "coordinates": [[-3.72, 40.44], [-3.66, 40.39]]
        },
        "relation": "intersects"
      }
    }
  }
}
// relation:
//   intersects -> field shape shares any point with the query shape (default)
//   within     -> field shape lies entirely inside the query shape
//   contains   -> field shape entirely contains the query shape
//   disjoint   -> field shape shares no point with the query shape
// Reuse a stored shape instead of an inline one with "indexed_shape": { "index", "id", "path" }.
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-shape-query.html

geo_grid

Filters documents that fall inside one named grid cell — a geohash string, a z/x/y map tile, or an H3 hexagon address. Its main use is drilling from a grid-aggregation bucket back to the documents in it.

GET /venues/_search
{
  "query": {
    "geo_grid": {
      "location": { "geohash": "ezjm" }
    }
  }
}
// Use "geotile": "6/32/22" or "geohex": "837542fffffffff" to match the other two grid types.
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-grid-query.html

Geo aggregations

Geo aggregations turn a geo_point field into map layers: distance rings, density grids, and bounding geometry. Set "size": 0 to ask for buckets only. They build on the general mechanics in Aggregations.

geo_distance aggregation

Buckets documents into concentric range rings around an origin.

GET /venues/_search
{
  "size": 0,
  "aggs": {
    "rings": {
      "geo_distance": {
        "field": "location",
        "origin": "40.4168,-3.7038",
        "unit": "km",
        "ranges": [
          { "to": 1 },
          { "from": 1, "to": 5 },
          { "from": 5 }
        ]
      }
    }
  }
}
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geodistance-aggregation.html

Grid aggregations: geohash_grid, geotile_grid, geohex_grid

Each groups points into cells of a fixed grid and returns a doc_count per cell — the data behind a heat map. They differ only in the grid.

GET /venues/_search
{
  "size": 0,
  "aggs": {
    "heat": {
      "geohash_grid": { "field": "location", "precision": 5 },
      "aggs": {
        "centre": { "geo_centroid": { "field": "location" } }
      }
    }
  }
}
// geohash_grid -> base-32 geohash rectangles, precision 1..12
// geotile_grid -> Web-Mercator "z/x/y" tiles, precision 0..29, aligned with slippy-map tiles
// geohex_grid  -> Uber H3 hexagons, precision 0..15, near-equal-area cells
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohashgrid-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geotilegrid-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohexgrid-aggregation.html

Constrain the search with a geo_bounding_box filter to the visible map extent, then raise precision as the user zooms in; drill into a single cell with the geo_grid query above.

geo_bounds and geo_centroid

Two metric aggregations that summarise a set of points — useful at the top level to auto-fit a map, or nested inside a grid bucket to label each cell.

GET /venues/_search
{
  "size": 0,
  "aggs": {
    "box":      { "geo_bounds":   { "field": "location" } },
    "midpoint": { "geo_centroid": { "field": "location" } }
  }
}
// geo_bounds   -> smallest bounding box enclosing every matched point
// geo_centroid -> the weighted centre of mass of every matched point
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-geobounds-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-geocentroid-aggregation.html

Kibana Maps

Kibana Maps is the visual front end for everything on this page. It renders geo_point and geo_shape fields as map layers, issues geo_bounding_box filters automatically as you pan and zoom, and draws the grid aggregations as heat maps or clustered cells — without hand-writing any Query DSL. See Maps.