CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-elasticsearch--elasticsearch-geo

Elasticsearch geometry library providing core geometric shapes and spatial utility classes for geometric computations and operations.

Pending
Overview
Eval results
Files

spatial-utilities.mddocs/

Spatial Utilities

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.

Capabilities

SpatialEnvelopeVisitor

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);

CircleUtils

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());
}

BitUtil

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);

Advanced Usage Patterns

Combined Envelope and Circle Conversion

// 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);
}

Spatial Grid Indexing with BitUtil

// 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);

Geographic vs Cartesian Envelope Calculation

// 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

docs

core-geometry.md

format-conversion.md

index.md

multi-geometry.md

simplification.md

spatial-utilities.md

validation.md

visitor-pattern.md

tile.json