Spatial Queries

What You Will Learn

How to define geospatial entities with @GeoPoint and @GeoField annotations, create indexed caches, and run spatial Ickle queries including within circle, within box, within polygon, distance projections, and spatial ordering.

Prerequisites

  • Java 17+

  • An Infinispan Server running on localhost:11222 (or Docker/Podman available for Testcontainers)

Step 1: Define Geospatial Entities

Use @GeoPoint with @Latitude and @Longitude to define a location on a record:

@Proto
@Indexed
@GeoPoint(fieldName = "location", projectable = true, sortable = true)
public record Restaurant(
      @Keyword(normalizer = "lowercase", projectable = true, sortable = true) String name,
      @Text String description,
      @Text String address,
      @Latitude(fieldName = "location") Double latitude,
      @Longitude(fieldName = "location") Double longitude,
      @Basic Float score
) {
}

For entities with multiple geo points, use multiple @GeoPoint annotations (see TrainRoute). Alternatively, use @GeoField with LatLng for a simpler mapping:

@Proto
@Indexed
public record Hiking(@Keyword String name, @GeoField LatLng start, @GeoField LatLng end) {

}

Step 2: Create an Indexed Cache and Run Spatial Queries

Configure the cache with indexing enabled for all spatial entities. Then run spatial queries — within a circle, within a bounding box, within a polygon, and with distance projections and ordering:

   static QueryResult<Restaurant> withinCircle() {
      Query<Restaurant> query = myCache.query("from tutorial.Restaurant r where r.location " +
            "within circle(:lat, :lon, :distance)");
      query.setParameter("lat", MY_COORDINATES.latitude());
      query.setParameter("lon", MY_COORDINATES.longitude());
      query.setParameter("distance", 100);
      QueryResult<Restaurant> queryResult = query.execute();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(queryResult.list());
      return queryResult;
   }

   static QueryResult<Restaurant> withinBox() {
      Query<Restaurant> query = myCache.query("from tutorial.Restaurant r where r.location " +
            "within box(41.91, 12.45, 41.90, 12.46)");
      QueryResult<Restaurant> queryResult = query.execute();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(queryResult.list());
      return queryResult;
   }

   static QueryResult<Restaurant> withinPolygon() {
      Query<Restaurant> query = myCache.query("from tutorial.Restaurant r where r.location " +
            "within polygon((41.91, 12.45), (41.91, 12.46), (41.90, 12.46), (41.90, 12.46))");
      QueryResult<Restaurant> queryResult = query.execute();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(queryResult.list());
      return queryResult;
   }

   static List<RestaurantDTO> spatialProjection() {
      Query<Object[]> query = myCache.query("select r.name, distance(r.location, 41.90847031512531, 12.455633288333539) " +
            "from tutorial.Restaurant r");
      QueryResult<Object[]> queryResult = query.execute();
      List<RestaurantDTO> valueObjects = queryResult.list().stream()
            .map(r -> new RestaurantDTO((String) r[0], (Double) r[1]))
            .toList();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(valueObjects);
      return valueObjects;
   }

   static QueryResult<Restaurant> spatialOrderBy() {
      Query<Restaurant> query = myCache.query("from tutorial.Restaurant r where r.location " +
            "order by distance(r.location, 41.90847031512531, 12.455633288333539)");
      QueryResult<Restaurant> queryResult = query.execute();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(queryResult.list());
      return queryResult;
   }

   static List<RestaurantDTO> spatialProjection_OrderBy() {
      Query<Object[]> query = myCache.query("select r.name, distance(r.location, 41.90847031512531, 12.455633288333539) " +
            "from tutorial.Restaurant r where r.location " +
            "order by distance(r.location, 41.90847031512531, 12.455633288333539)");
      QueryResult<Object[]> queryResult = query.execute();
      List<RestaurantDTO> valueObjects = queryResult.list().stream()
            .map(r -> new RestaurantDTO((String) r[0], (Double) r[1]))
            .toList();
      // Print the results
      System.out.println("COUNT " + queryResult.count().value());
      System.out.println(valueObjects);
      return valueObjects;
   }

Step 3: Run the Tutorial

mvn package exec:java

What’s Next