ElasticSearch 6 – Spatial Queries with RestHighLevelClient and Java – Part 2: GeoBoundingBoxQuery and GeoPolygonQuery

Introduction

In previous article, we elaborated on the ElasticSearch’s GeoDistanceQuery. This time we’ll demo how to develop a  GeoBoundingBoxQuery  and a GeoPolygonQuery.

The code is still written in Java/Spring but instead of using ‘hellhole Belgium’ (quote unquote) as a location, we use Manhattan as a location and we use the Starbucks coffee shops in our spatial queries.

Following picture clearly visualizes the BoundingBox and the Polygon we will use in our code.

We rely, once more, on Spring and Spring Boot and use REST endpoints. A complete reporting system would include a front-end component (Angular) with some functionality to manage requests/responses but this is not in scope of current  POC.

BoundingBox

Request

In our request we simply define the latitude/longitude of the top left corner and of the bottom right corner:

http://localhost:8090/geoBoundingBoxQuery?top=40.790619&left=-73.977343&bottom=40.762151&right=73.971678

Response

Our response is an array of Starbucks coffee shops with some basic properties, retrieved directly from the Elasticsearch index.

[
{
"address": "Astoria/Ditmars_22-04 31st Street_Astoria, New York 11105_718-626-6004",
"description": "Starbucks - NY - Astoria [W] 05920"
},
{
"address": "76th & Second_1449 Second Avenue_New York, New York 100212904_212-472-0653",
"description": "Starbucks - NY - New York [W] 06182"
},
{
"address": "76th & Columbus_338 Columbus Avenue_New York, New York 10023_646-505-1106",
"description": "Starbucks - NY - New York [W] 06190"
},
{
"address": "92nd & 3rd_1642 Third Avenue_New York, New York 101283618_(212) 360-0425",
"description": "Starbucks - NY - New York [W] 06191"
},
{
"address": "96th & Madison_1378 Madison Avenue_New York, New York 101280712_212-534-7537",
"description": "Starbucks - NY - New York [W] 06198"
}
....

Development

public Set geoBoundingBoxQuery(double[] corners) throws IOException {
        Date startDate = new Date();

        Set locations = new LinkedHashSet<>();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        QueryBuilder query = QueryBuilders.matchAllQuery();

        QueryBuilder geoQueryBuilder = QueryBuilders
                .geoBoundingBoxQuery("location")
                .setCorners(corners[0], corners[1], corners[2], corners[3]);

        QueryBuilder finalQuery = QueryBuilders.boolQuery().must(query).filter(geoQueryBuilder);

        sourceBuilder.query(finalQuery).size(SIZE_ES_QUERY);

        SearchRequest searchRequest = new SearchRequest("retail_locations").source(sourceBuilder);

        SearchResponse searchResponse = restClient.search(searchRequest);

        SearchHits hits = searchResponse.getHits();

        for (SearchHit hit : hits.getHits()) {
            locations.add(GEO_Service.getObjectFromES_Hit(hit, "retailer"));
        }

        return timedReturn(LOGGER, new Object() {}.getClass().getEnclosingMethod().getName(), startDate.getTime(), locations);
    }

Polygon

Request

This time we define a collection of latitudes/longitudes and we append the collection to our query string.

http://localhost:8090/geoPolygonQuery?geoPoints=40.718147,-74.012716_40.715444,-74.005516_40.712646,-73.997834_40.705651,-74.001739_40.699957,-74.017060

Response

[
 {
 "address": "Battery Park Plaza_1 Battery Park Plaza_New York, New York 10004_(212) 482-1180",
 "description": "Starbucks - NY - New York [W] 06202"
 },
 {
 "address": "Broadway & Thames_115 Broadway_New York, New York 100061604_212-732-9268",
 "description": "Starbucks - NY - New York [W] 06206"
 },
 {
 "address": "Chambers and West Broadway_125 Chambers Street_New York, New York 10007_212-791-6368",
 "description": "Starbucks - NY - New York [W] 06213"
 },
 {
 "address": "100 Wall St (New World)_100 Wall Street_New York, New York 10005_212-809-1556",
 "description": "Starbucks - NY - New York [W] 06102"
 },
....

Development

public Set<CommuneRequest.Commune> geoPolygonQuery(List<GeoPoint> geoPoints) throws IOException {
    Date startDate = new Date();

    Set<CommuneRequest.Commune> communes = new LinkedHashSet<>();
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

    QueryBuilder query = QueryBuilders.matchAllQuery();

    QueryBuilder geoQueryBuilder = QueryBuilders
            .geoPolygonQuery("location", geoPoints);

    QueryBuilder finalQuery = QueryBuilders.boolQuery().must(query).filter(geoQueryBuilder);

    sourceBuilder.query(finalQuery).size(SIZE_ES_QUERY);

    SearchRequest searchRequest = new SearchRequest("retail_locations").source(sourceBuilder);

    SearchResponse searchResponse = restClient.search(searchRequest);

    SearchHits hits = searchResponse.getHits();

    for (SearchHit hit : hits.getHits()) {
        communes.add(GEO_Service.getObjectFromES_Hit(hit, "retailer"));
    }

    return timedReturn(LOGGER, new Object() {}.getClass().getEnclosingMethod().getName(), startDate.getTime(), communes);
}

Once again, don’t hesitate to ask questions or to recommend amendments and please stay tuned since next article will cover Elastic Search, Spatial Queries and Aggregations.

Keywords: ElasticSearch, Java, rest-high-level-client

Advertisements

About IctDynamic.Be

IctDynamic designs and develops affordable software applications for the SME We focus on • Java Freelance missions • GIS and geographical solutions • Freelance missions as project manager (certified), analyst, architect, Java software engineer
This entry was posted in GIS, Java and tagged , , . Bookmark the permalink.