Geotile grid aggregations

The geotile grid aggregation groups documents into grid cells for geographical analysis. Each grid cell corresponds to a map tile and is identified using the {zoom}/{x}/{y} format. You can aggregate documents on geopoint or geoshape fields using a geotile grid aggregation. One notable difference is that a geopoint is only present in one bucket, but a geoshape is counted in all geotile grid cells with which it intersects.

Precision

The precision parameter controls the level of granularity that determines the grid cell size. The lower the precision, the larger the grid cells.

The following example illustrates low-precision and high-precision aggregation requests.

To start, create an index and map the location field as a geo_point:

  1. PUT national_parks
  2. {
  3. "mappings": {
  4. "properties": {
  5. "location": {
  6. "type": "geo_point"
  7. }
  8. }
  9. }
  10. }

copy

Index the following documents into the sample index:

  1. PUT national_parks/_doc/1
  2. {
  3. "name": "Yellowstone National Park",
  4. "location": "44.42, -110.59"
  5. }

copy

  1. PUT national_parks/_doc/2
  2. {
  3. "name": "Yosemite National Park",
  4. "location": "37.87, -119.53"
  5. }

copy

  1. PUT national_parks/_doc/3
  2. {
  3. "name": "Death Valley National Park",
  4. "location": "36.53, -116.93"
  5. }

copy

You can index geopoints in several formats. For a list of all supported formats, see the geopoint documentation.

Low-precision requests

Run a low-precision request that buckets all three documents together:

  1. GET national_parks/_search
  2. {
  3. "aggregations": {
  4. "grouped": {
  5. "geotile_grid": {
  6. "field": "location",
  7. "precision": 1
  8. }
  9. }
  10. }
  11. }

copy

You can use either the GET or POST HTTP method for geotile grid aggregation queries.

The response groups all documents together because they are close enough to be bucketed in one grid cell:

Response

  1. {
  2. "took": 51,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 3,
  13. "relation": "eq"
  14. },
  15. "max_score": 1,
  16. "hits": [
  17. {
  18. "_index": "national_parks",
  19. "_id": "1",
  20. "_score": 1,
  21. "_source": {
  22. "name": "Yellowstone National Park",
  23. "location": "44.42, -110.59"
  24. }
  25. },
  26. {
  27. "_index": "national_parks",
  28. "_id": "2",
  29. "_score": 1,
  30. "_source": {
  31. "name": "Yosemite National Park",
  32. "location": "37.87, -119.53"
  33. }
  34. },
  35. {
  36. "_index": "national_parks",
  37. "_id": "3",
  38. "_score": 1,
  39. "_source": {
  40. "name": "Death Valley National Park",
  41. "location": "36.53, -116.93"
  42. }
  43. }
  44. ]
  45. },
  46. "aggregations": {
  47. "grouped": {
  48. "buckets": [
  49. {
  50. "key": "1/0/0",
  51. "doc_count": 3
  52. }
  53. ]
  54. }
  55. }
  56. }

High-precision requests

Now run a high-precision request:

  1. GET national_parks/_search
  2. {
  3. "aggregations": {
  4. "grouped": {
  5. "geotile_grid": {
  6. "field": "location",
  7. "precision": 6
  8. }
  9. }
  10. }
  11. }

copy

All three documents are bucketed separately because of higher granularity:

Response

  1. {
  2. "took": 15,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 3,
  13. "relation": "eq"
  14. },
  15. "max_score": 1,
  16. "hits": [
  17. {
  18. "_index": "national_parks",
  19. "_id": "1",
  20. "_score": 1,
  21. "_source": {
  22. "name": "Yellowstone National Park",
  23. "location": "44.42, -110.59"
  24. }
  25. },
  26. {
  27. "_index": "national_parks",
  28. "_id": "2",
  29. "_score": 1,
  30. "_source": {
  31. "name": "Yosemite National Park",
  32. "location": "37.87, -119.53"
  33. }
  34. },
  35. {
  36. "_index": "national_parks",
  37. "_id": "3",
  38. "_score": 1,
  39. "_source": {
  40. "name": "Death Valley National Park",
  41. "location": "36.53, -116.93"
  42. }
  43. }
  44. ]
  45. },
  46. "aggregations": {
  47. "grouped": {
  48. "buckets": [
  49. {
  50. "key": "6/12/23",
  51. "doc_count": 1
  52. },
  53. {
  54. "key": "6/11/25",
  55. "doc_count": 1
  56. },
  57. {
  58. "key": "6/10/24",
  59. "doc_count": 1
  60. }
  61. ]
  62. }
  63. }
  64. }

You can also restrict the geographical area by providing the coordinates of the bounding envelope in the bounds parameter. Both bounds and geo_bounding_box coordinates can be specified in any of the geopoint formats. The following query uses the well-known text (WKT) “POINT(longitude latitude)” format for the bounds parameter:

  1. GET national_parks/_search
  2. {
  3. "size": 0,
  4. "aggregations": {
  5. "grouped": {
  6. "geotile_grid": {
  7. "field": "location",
  8. "precision": 6,
  9. "bounds": {
  10. "top_left": "POINT (-120 38)",
  11. "bottom_right": "POINT (-116 36)"
  12. }
  13. }
  14. }
  15. }
  16. }

copy

The response contains only the two results that are within the specified bounds:

Response

  1. {
  2. "took": 48,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 3,
  13. "relation": "eq"
  14. },
  15. "max_score": 1,
  16. "hits": [
  17. {
  18. "_index": "national_parks",
  19. "_id": "1",
  20. "_score": 1,
  21. "_source": {
  22. "name": "Yellowstone National Park",
  23. "location": "44.42, -110.59"
  24. }
  25. },
  26. {
  27. "_index": "national_parks",
  28. "_id": "2",
  29. "_score": 1,
  30. "_source": {
  31. "name": "Yosemite National Park",
  32. "location": "37.87, -119.53"
  33. }
  34. },
  35. {
  36. "_index": "national_parks",
  37. "_id": "3",
  38. "_score": 1,
  39. "_source": {
  40. "name": "Death Valley National Park",
  41. "location": "36.53, -116.93"
  42. }
  43. }
  44. ]
  45. },
  46. "aggregations": {
  47. "grouped": {
  48. "buckets": [
  49. {
  50. "key": "6/11/25",
  51. "doc_count": 1
  52. },
  53. {
  54. "key": "6/10/24",
  55. "doc_count": 1
  56. }
  57. ]
  58. }
  59. }
  60. }

The bounds parameter can be used with or without the geo_bounding_box filter; these two parameters are independent and can have any spatial relationship to each other.

Aggregating geoshapes

To run an aggregation on a geoshape field, first create an index and map the location field as a geo_shape:

  1. PUT national_parks
  2. {
  3. "mappings": {
  4. "properties": {
  5. "location": {
  6. "type": "geo_shape"
  7. }
  8. }
  9. }
  10. }

copy

Next, index some documents into the national_parks index:

  1. PUT national_parks/_doc/1
  2. {
  3. "name": "Yellowstone National Park",
  4. "location":
  5. {"type": "envelope","coordinates": [ [-111.15, 45.12], [-109.83, 44.12] ]}
  6. }

copy

  1. PUT national_parks/_doc/2
  2. {
  3. "name": "Yosemite National Park",
  4. "location":
  5. {"type": "envelope","coordinates": [ [-120.23, 38.16], [-119.05, 37.45] ]}
  6. }

copy

  1. PUT national_parks/_doc/3
  2. {
  3. "name": "Death Valley National Park",
  4. "location":
  5. {"type": "envelope","coordinates": [ [-117.34, 37.01], [-116.38, 36.25] ]}
  6. }

copy

You can run an aggregation on the location field as follows:

  1. GET national_parks/_search
  2. {
  3. "aggregations": {
  4. "grouped": {
  5. "geotile_grid": {
  6. "field": "location",
  7. "precision": 6
  8. }
  9. }
  10. }
  11. }

copy

When aggregating geoshapes, one geoshape can be counted for multiple buckets because it overlaps with multiple grid cells:

Response

  1. {
  2. "took" : 3,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 3,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 1.0,
  16. "hits" : [
  17. {
  18. "_index" : "national_parks",
  19. "_id" : "1",
  20. "_score" : 1.0,
  21. "_source" : {
  22. "name" : "Yellowstone National Park",
  23. "location" : {
  24. "type" : "envelope",
  25. "coordinates" : [
  26. [
  27. -111.15,
  28. 45.12
  29. ],
  30. [
  31. -109.83,
  32. 44.12
  33. ]
  34. ]
  35. }
  36. }
  37. },
  38. {
  39. "_index" : "national_parks",
  40. "_id" : "2",
  41. "_score" : 1.0,
  42. "_source" : {
  43. "name" : "Yosemite National Park",
  44. "location" : {
  45. "type" : "envelope",
  46. "coordinates" : [
  47. [
  48. -120.23,
  49. 38.16
  50. ],
  51. [
  52. -119.05,
  53. 37.45
  54. ]
  55. ]
  56. }
  57. }
  58. },
  59. {
  60. "_index" : "national_parks",
  61. "_id" : "3",
  62. "_score" : 1.0,
  63. "_source" : {
  64. "name" : "Death Valley National Park",
  65. "location" : {
  66. "type" : "envelope",
  67. "coordinates" : [
  68. [
  69. -117.34,
  70. 37.01
  71. ],
  72. [
  73. -116.38,
  74. 36.25
  75. ]
  76. ]
  77. }
  78. }
  79. }
  80. ]
  81. },
  82. "aggregations" : {
  83. "grouped" : {
  84. "buckets" : [
  85. {
  86. "key" : "6/12/23",
  87. "doc_count" : 1
  88. },
  89. {
  90. "key" : "6/12/22",
  91. "doc_count" : 1
  92. },
  93. {
  94. "key" : "6/11/25",
  95. "doc_count" : 1
  96. },
  97. {
  98. "key" : "6/11/24",
  99. "doc_count" : 1
  100. },
  101. {
  102. "key" : "6/10/24",
  103. "doc_count" : 1
  104. }
  105. ]
  106. }
  107. }
  108. }

Currently, OpenSearch supports geoshape aggregation through the API but not in OpenSearch Dashboards visualizations. If you’d like to see geoshape aggregation implemented for visualizations, upvote the related GitHub issue.

Supported parameters

Geotile grid aggregation requests support the following parameters.

ParameterData typeDescription
fieldStringThe field that contains the geopoints. This field must be mapped as a geo_point field. If the field contains an array, all array values are aggregated. Required.
precisionIntegerThe zoom level used to determine grid cells for bucketing results. Valid values are in the [0, 15] range. Optional. Default is 5.
boundsObjectThe bounding box for filtering geopoints. The bounding box is defined by the upper-left and lower-right vertices. The vertices are specified as geopoints in one of the following formats:
- An object with a latitude and longitude
- An array in the [longitude, latitude] format
- A string in the “latitude,longitude” format
- A geohash
- WKT
See the geopoint formats for formatting examples. Optional.
sizeIntegerThe maximum number of buckets to return. When there are more buckets than size, OpenSearch returns buckets with more documents. Optional. Default is 10,000.
shard_sizeIntegerThe maximum number of buckets to return from each shard. Optional. Default is max (10, size · number of shards), which provides a more accurate count of more highly prioritized buckets.