From the Viewport to the Query

I want to retrieve locations
- that are within the NYC area, defined by a rectangular viewport
- and boost those that are in Brooklyn, described by a GeoJSON polygon:

Randomly generated points of interest in the NYC area. Base map courtesy of geojson.io
PUT locations
{
"mappings": {
"properties": {
"geometry": {
"type": "object",
"properties": {
"coordinates": {
"type": "geo_point"
}
}
}
}
}
}
Notice how the geometry.coordinates
field is explicitly declared as a geo_point
. It's tempting to use the geo_shape
field type on the whole geometry
field because a Point
is a valid GeoJSON type (which a geo_shape
would accept) but the geo_bounding_box
query we'll be using only allows filtering on explicitly declared geo_point
fields!
POST _bulk
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-73.78964,40.70269]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.14181,40.76044]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.18276,40.85367]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.12306,40.62101]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.0113,40.83436]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.27228,40.72379]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-73.7888,40.59183]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-73.87138,40.86417]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-73.89522,40.71144]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.12918,40.72388]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.04345,40.86526]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.27225,40.84098]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.19762,40.69674]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-73.78374,40.89914]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.23609,40.86037]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-73.94215,40.67004]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.12929,40.60387]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.08588,40.68269]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-74.27447,40.89801]}}
{"index":{"_index":"locations"}}
{"geometry":{"type":"Point","coordinates":[-77.17435,38.98741]}, "meta": "not in nyc"}
In order to describe an area of a map (that uses the Mercator projection ) with minimum points, we'll need a rectangle's opposing corners — typically the south west and the north east:
- South West → Bottom Left → [ min(x) , min(y) ]
- North East → Top Right → [ max(x) , max(y) ]
Many frontend mapping libraries implement a getBounds()
method which returns the viewport's NE & SW corners — most notably Mapbox , Google Maps and Leaflet .
As soon as we've obtained the two points, we can plug them into the geo_bounding_box
query:
POST locations/_search
{
"query": {
"geo_bounding_box": {
"geometry.coordinates": {
"bottom_left": [-74.32457, 40.51119],
"top_right": [-73.72444, 40.93012]
}
}
}
}
Alternatively, the opposing points' coordinates can be deconstructed into a bounding box:
[ min(x), min(y), max(x), max(y) ] or
[ sw_lng, sw_lat, ne_lng, ne_lat ] or
[ west, south, east, north ] or
[ left, bottom, right, top ]
Consequently, the NYC BBOX can be written as [ -74.32457 , 40.51119 , -73.72444 , 40.93012 ] and used in the same geo_bounding_box
query through its vertices :
POST locations/_search
{
"query": {
"geo_bounding_box": {
"geometry.coordinates": {
"left": -74.32457,
"bottom" :40.51119,
"right": -73.72444,
"top": 40.93012
}
}
}
}
What you're far more likely to encounter, though, is BBOXs in the form of rectangular polygons. See, the "simplest" polygon in Euclidian/flat geometry is a triangle (3 points); then comes the rectangle (4 points) and so on — so each rectangle is technically also a polygon.
In the GeoJSON standard , a polygon is said to have 4 or more positions, the first and last of which are identical → so in the case of rectangles, we'll have 5 coordinate pairs in total! Furthermore, it's required that the coordinate pairs follow the right-hand rule, i.e. are written counterclockwise. To keep things consistent, let's start in the bottom left corner and move CCW: