Elasticsearch geometry library providing core geometric shapes and spatial utility classes for geometric computations and operations.
—
Additional spatial utility functions for envelope calculation, circle conversion, and bit manipulation operations. These utilities provide advanced spatial operations and low-level optimization functions for geometric computations.
Determines the spatial envelope (bounding box) of geometries with support for both Cartesian and geographic coordinate systems.
/**
* Calculates the spatial envelope (bounding box) of a geometry
* @param geometry the geometry to analyze
* @return Optional containing the bounding rectangle, or empty if geometry is empty
*/
public static Optional<Rectangle> visit(Geometry geometry)
/**
* Visitor pattern implementation for envelope calculation
* @param <E> exception type that may be thrown during processing
*/
public class SpatialEnvelopeVisitor<E extends Exception> implements GeometryVisitor<Optional<Rectangle>, E> {
/**
* Creates a cartesian envelope visitor
* @return visitor for cartesian coordinate systems
*/
public static SpatialEnvelopeVisitor<RuntimeException> cartesian()
/**
* Creates a geographic envelope visitor with dateline wrapping
* @return visitor for geographic coordinate systems
*/
public static SpatialEnvelopeVisitor<RuntimeException> geographic()
/**
* Creates a geographic envelope visitor with optional dateline wrapping
* @param wrapLongitude whether to consider longitude wrapping around dateline
* @return visitor for geographic coordinate systems
*/
public static SpatialEnvelopeVisitor<RuntimeException> geographic(boolean wrapLongitude)
}Usage Examples:
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
import java.util.Optional;
// Calculate envelope for any geometry
Polygon complexPolygon = createComplexPolygon();
Optional<Rectangle> envelope = SpatialEnvelopeVisitor.visit(complexPolygon);
if (envelope.isPresent()) {
Rectangle bbox = envelope.get();
double width = bbox.getMaxX() - bbox.getMinX();
double height = bbox.getMaxY() - bbox.getMinY();
System.out.println("Bounding box: " + width + " x " + height);
}
// Use specific coordinate system visitor
SpatialEnvelopeVisitor<RuntimeException> cartesianVisitor = SpatialEnvelopeVisitor.cartesian();
Optional<Rectangle> cartesianEnvelope = complexPolygon.visit(cartesianVisitor);
// Geographic visitor with dateline wrapping consideration
SpatialEnvelopeVisitor<RuntimeException> geoVisitor = SpatialEnvelopeVisitor.geographic(true);
Optional<Rectangle> geoEnvelope = complexPolygon.visit(geoVisitor);Utilities for converting circles to polygon approximations and performing spatial calculations.
/**
* Minimum number of sides for circle-to-polygon conversion
*/
public static final int CIRCLE_TO_POLYGON_MINIMUM_NUMBER_OF_SIDES = 4;
/**
* Maximum number of sides for circle-to-polygon conversion
*/
public static final int CIRCLE_TO_POLYGON_MAXIMUM_NUMBER_OF_SIDES = 1000;
/**
* Converts a circle to a regular polygon approximation
* @param circle the circle to convert
* @param gons number of sides for the polygon (4-1000)
* @return polygon approximating the circle
* @throws IllegalArgumentException if circle contains a pole or gons is invalid
*/
public static Polygon createRegularGeoShapePolygon(Circle circle, int gons)
/**
* Calculates haversine distance between two points (internal utility)
* @param lat1 latitude of first point
* @param lon1 longitude of first point
* @param lat2 latitude of second point
* @param lon2 longitude of second point
* @return distance in meters
*/
private static double slowHaversin(double lat1, double lon1, double lat2, double lon2)Usage Examples:
import org.elasticsearch.geometry.utils.CircleUtils;
// Convert circle to polygon with 64 sides
Circle circle = new Circle(-73.935, 40.730, 1000.0); // 1km radius
Polygon circlePolygon = CircleUtils.createRegularGeoShapePolygon(circle, 64);
// Lower precision for performance (minimum 4 sides)
Polygon simplePolygon = CircleUtils.createRegularGeoShapePolygon(circle, 8);
// High precision approximation (maximum 1000 sides)
Polygon precisePolygon = CircleUtils.createRegularGeoShapePolygon(circle, 1000);
// Error handling for polar circles
try {
Circle polarCircle = new Circle(0, 89, 200000); // Large circle near north pole
Polygon result = CircleUtils.createRegularGeoShapePolygon(polarCircle, 32);
} catch (IllegalArgumentException e) {
System.err.println("Circle contains pole: " + e.getMessage());
}Low-level bit manipulation utilities for spatial indexing and Morton encoding operations.
/**
* Interleaves the first 32 bits of two integer values
* @param even the even-positioned bits (usually x coordinate)
* @param odd the odd-positioned bits (usually y coordinate)
* @return interleaved long value with bits from both inputs
*/
public static long interleave(int even, int odd)
/**
* Deinterleaves a long value to extract even-positioned bits
* @param interleaved the interleaved long value
* @return integer containing the even-positioned bits
*/
public static int deinterleave(long interleaved)
/**
* Deinterleaves a long value to extract odd-positioned bits
* @param interleaved the interleaved long value
* @return integer containing the odd-positioned bits
*/
public static int deinterleaveOdd(long interleaved)
/**
* Flips all bits in an integer value
* @param value the integer to flip
* @return integer with all bits flipped
*/
public static int flipBits(int value)
/**
* Counts the number of set bits in an integer
* @param value the integer to analyze
* @return number of bits set to 1
*/
public static int popCount(int value)Usage Examples:
import org.elasticsearch.geometry.utils.BitUtil;
// Spatial indexing with Morton encoding
int xCoord = 12345;
int yCoord = 67890;
// Interleave coordinates for Z-order curve
long mortonCode = BitUtil.interleave(xCoord, yCoord);
// Extract coordinates back from Morton code
int extractedX = BitUtil.deinterleave(mortonCode);
int extractedY = BitUtil.deinterleaveOdd(mortonCode);
assert extractedX == xCoord;
assert extractedY == yCoord;
// Bit manipulation operations
int value = 0b10110010;
int flipped = BitUtil.flipBits(value); // 0b01001101
int setBits = BitUtil.popCount(value); // 4 (number of 1s)
// Geospatial indexing example
double longitude = -73.935242;
double latitude = 40.730610;
// Convert to integer coordinates (simplified example)
int lonBits = (int) ((longitude + 180.0) * 1000000);
int latBits = (int) ((latitude + 90.0) * 1000000);
// Create spatial index key
long spatialKey = BitUtil.interleave(lonBits, latBits);// Get envelope, convert to circle, then to polygon
MultiPolygon complexGeometry = createComplexMultiPolygon();
// Calculate bounding envelope
Optional<Rectangle> envelope = SpatialEnvelopeVisitor.visit(complexGeometry);
if (envelope.isPresent()) {
Rectangle bbox = envelope.get();
// Create circle that covers the envelope
double centerLon = (bbox.getMinX() + bbox.getMaxX()) / 2;
double centerLat = (bbox.getMinY() + bbox.getMaxY()) / 2;
double width = bbox.getMaxX() - bbox.getMinX();
double height = bbox.getMaxY() - bbox.getMinY();
double radius = Math.sqrt(width * width + height * height) * 111320 / 2; // approx meters
Circle boundingCircle = new Circle(centerLon, centerLat, radius);
// Convert to polygon approximation
Polygon approximation = CircleUtils.createRegularGeoShapePolygon(boundingCircle, 32);
}// Create spatial grid index using Morton encoding
public class SpatialGrid {
private static final double WORLD_WIDTH = 360.0;
private static final double WORLD_HEIGHT = 180.0;
private final int resolution;
public SpatialGrid(int resolution) {
this.resolution = resolution;
}
public long encode(double longitude, double latitude) {
// Normalize coordinates to grid
int x = (int) ((longitude + 180.0) / WORLD_WIDTH * resolution);
int y = (int) ((latitude + 90.0) / WORLD_HEIGHT * resolution);
// Clamp to bounds
x = Math.max(0, Math.min(resolution - 1, x));
y = Math.max(0, Math.min(resolution - 1, y));
return BitUtil.interleave(x, y);
}
public Point decode(long mortonCode) {
int x = BitUtil.deinterleave(mortonCode);
int y = BitUtil.deinterleaveOdd(mortonCode);
double longitude = (double) x / resolution * WORLD_WIDTH - 180.0;
double latitude = (double) y / resolution * WORLD_HEIGHT - 90.0;
return new Point(longitude, latitude);
}
}
// Usage
SpatialGrid grid = new SpatialGrid(1024);
long gridKey = grid.encode(-73.935242, 40.730610);
Point gridCenter = grid.decode(gridKey);// Different envelope calculations for different coordinate systems
Polygon antimeridianPolygon = createAntimeridianSpanningPolygon();
// Cartesian envelope (treats coordinates as planar)
Optional<Rectangle> cartesianEnv = antimeridianPolygon.visit(
SpatialEnvelopeVisitor.cartesian()
);
// Geographic envelope without dateline wrapping
Optional<Rectangle> geoEnvNoWrap = antimeridianPolygon.visit(
SpatialEnvelopeVisitor.geographic(false)
);
// Geographic envelope with dateline wrapping (smaller bbox)
Optional<Rectangle> geoEnvWrap = antimeridianPolygon.visit(
SpatialEnvelopeVisitor.geographic(true)
);
// Compare envelope sizes to choose optimal representation
if (geoEnvWrap.isPresent() && geoEnvNoWrap.isPresent()) {
Rectangle wrapped = geoEnvWrap.get();
Rectangle unwrapped = geoEnvNoWrap.get();
double wrappedArea = (wrapped.getMaxX() - wrapped.getMinX()) *
(wrapped.getMaxY() - wrapped.getMinY());
double unwrappedArea = (unwrapped.getMaxX() - unwrapped.getMinX()) *
(unwrapped.getMaxY() - unwrapped.getMinY());
Rectangle optimal = wrappedArea < unwrappedArea ? wrapped : unwrapped;
}Install with Tessl CLI
npx tessl i tessl/maven-org-elasticsearch--elasticsearch-geo