From the Viewport to the Query

Spatialized founder Jozef Sorocin
Jozef Soročin
Updated 07/13/2025

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 http://geojson.io/

Randomly generated points of interest in the NYC area. Base map courtesy of geojson.io

Randomly generated points of interest in the NYC area. Base map courtesy of http://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:

Join 200+ developers who've mastered this! Get Complete Access — €19
Already a member? Sign in here