Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lucene/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ Improvements
* GITHUB#15063: Use optimistic-with-checking KNN Query strategy for `DiversifyingChildren*` queries.
(Ben Trent)

* GITHUB#15356: Improve docs for 4 internal classes in geo package (Matt Baranowski)

Optimizations
---------------------
* GITHUB#14932: Switched to GroupVarInt Encoding for HNSW Graph edges, added backwards compatibility (Akira Lonske)
Expand Down
85 changes: 84 additions & 1 deletion lucene/core/src/java/org/apache/lucene/geo/Circle2D.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,90 @@
import org.apache.lucene.index.PointValues.Relation;
import org.apache.lucene.util.SloppyMath;

/** 2D circle implementation containing spatial logic. */
/**
* Internal 2D representation of a circle for spatial query operations.
*
* <p>This class provides spatial logic for circle-based queries, converting high-level {@link
* Circle} or {@link XYCircle} geometries into 2D representations. It is used internally by Lucene's
* spatial search implementation.
*
* <p>Key Features:
*
* <ul>
* <li>Efficient point containment checks
* <li>Line and triangle intersection tests
* <li>Bounding box relationship calculations
* <li>Support for both geographic (lat/lon) and Cartesian coordinate systems
* <li>Handles dateline crossing for geographic circles
* </ul>
*
* <p>Distance Calculation Modes:
*
* <ul>
* <li><strong>Haversin Distance</strong> - For geographic circles (lat/lon coordinates), uses
* spherical Earth model
* <li><strong>Cartesian Distance</strong> - For XY circles (planar coordinates), uses Euclidean
* distance
* </ul>
*
* <p>Usage:
*
* <p>This class is created through the {@link Circle#toComponent2D()} or {@link
* XYCircle#toComponent2D()} methods:
*
* <pre>{@code
* // Geographic circle (lat/lon) - uses Haversin distance
* double eiffelTowerLat = 48.8584;
* double eiffelTowerLon = 2.2945;
* double radiusMeters = 1000; // 1km radius
* Circle geoCircle = new Circle(eiffelTowerLat, eiffelTowerLon, radiusMeters);
* Component2D geoComponent = geoCircle.toComponent2D(); // Creates Circle2D internally
*
* // Cartesian circle (XY) - uses Euclidean distance
* float centerX = 100.0f;
* float centerY = 200.0f;
* float radius = 50.0f;
* XYCircle xyCircle = new XYCircle(centerX, centerY, radius);
* Component2D xyComponent = xyCircle.toComponent2D(); // Creates Circle2D internally
* }</pre>
*
* <p>Spatial Operations:
*
* <p>Circle2D provides several spatial relationship methods:
*
* <ul>
* <li>{@link #contains(double, double)} - Tests if a point is inside the circle
* <li>{@link #relate(double, double, double, double)} - Determines relationship with a bounding
* box
* <li>{@link #intersectsLine} - Tests if a line segment intersects the circle
* <li>{@link #intersectsTriangle} - Tests if a triangle intersects the circle
* <li>{@link #containsLine} - Tests if a line segment is fully contained within the circle
* <li>{@link #containsTriangle} - Tests if a triangle is fully contained within the circle
* </ul>
*
* <p>Performance Considerations:
*
* <ul>
* <li>Uses bounding box checks to eliminate non-intersecting geometries
* <li>Cartesian distance calculations are faster than Haversin (geographic) calculations
* <li>For geographic circles near poles or crossing the dateline, additional logic ensures
* correctness
* </ul>
*
* <p>Implementation Details:
*
* <p>Circle2D uses a strategy pattern with {@link DistanceCalculator} implementations:
*
* <ul>
* <li>{@link CartesianDistance} - For XY coordinate systems
* <li>{@link HaversinDistance} - For geographic coordinate systems (lat/lon)
* </ul>
*
* @lucene.internal
* @see Circle
* @see XYCircle
* @see Component2D
*/
class Circle2D implements Component2D {

private final DistanceCalculator calculator;
Expand Down
137 changes: 136 additions & 1 deletion lucene/core/src/java/org/apache/lucene/geo/Component2D.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,144 @@
import org.apache.lucene.index.PointValues;

/**
* 2D Geometry object that supports spatial relationships with bounding boxes, triangles and points.
* Interface for 2D geometric components that support spatial relationship queries.
*
* <p>Component2D defines the contract for testing spatial relationships between geometric shapes
* and index structures (points, lines, triangles, bounding boxes). This interface is central to
* Lucene's spatial search implementation, enabling filtering of documents during geospatial
* queries.
*
* <p>Key Features:
*
* <ul>
* <li>Point containment testing
* <li>Line and triangle intersection detection
* <li>Line and triangle containment testing
* <li>Bounding box relationship computation
* <li>"Within" relationship evaluation for spatial queries
* </ul>
*
* <p>Implementations:
*
* <p>Component2D is implemented by geometric shapes classes:
*
* <ul>
* <li>{@link Circle2D} - Circular regions (both geographic and Cartesian)
* <li>{@link Polygon2D} - Polygonal regions with hole support
* <li>{@link Rectangle2D} - Rectangular bounding boxes
* <li>{@link Line2D} - Line segments and polylines
* <li>{@link Point2D} - Point geometries
* </ul>
*
* <p>Usage:
*
* <p>Component2D instances are created by calling {@code toComponent2D()} on high-level geometry
* objects:
*
* <pre>{@code
* // Create from a Circle
* double nycLat = 40.7128;
* double nycLon = -74.0060;
* double radiusMeters = 5000; // 5km radius
* Circle circle = new Circle(nycLat, nycLon, radiusMeters);
* Component2D component = circle.toComponent2D();
*
* // Test if a point is contained
* double xCoordinate = -74.0060;
* double yCoordinate = 40.7128;
* boolean contained = component.contains(xCoordinate, yCoordinate);
*
* // Test relationship with a bounding box
* double minX = -74.01;
* double maxX = -74.00;
* double minY = 40.71;
* double maxY = 40.72;
* PointValues.Relation relation = component.relate(minX, maxX, minY, maxY);
* }</pre>
*
* <p>Spatial Relationship Methods:
*
* <p><strong>Containment:</strong>
*
* <ul>
* <li>{@link #contains(double, double)} - Tests if a point is inside the geometry
* <li>{@link #containsLine} - Tests if a line segment is fully contained
* <li>{@link #containsTriangle} - Tests if a triangle is fully contained
* </ul>
*
* <p><strong>Intersection:</strong>
*
* <ul>
* <li>{@link #intersectsLine} - Tests if a line segment intersects the geometry
* <li>{@link #intersectsTriangle} - Tests if a triangle intersects the geometry
* </ul>
*
* <p><strong>Relationship:</strong>
*
* <ul>
* <li>{@link #relate} - Determines spatial relationship with a bounding box (INSIDE, OUTSIDE, or
* CROSSES)
* <li>{@link #withinPoint} - Computes "within" relationship for a point
* <li>{@link #withinLine} - Computes "within" relationship for a line
* <li>{@link #withinTriangle} - Computes "within" relationship for a triangle
* </ul>
*
* <p>Bounding Box:
*
* <p>Every Component2D has an associated bounding box defined by:
*
* <ul>
* <li>{@link #getMinX()}, {@link #getMaxX()} - Horizontal bounds
* <li>{@link #getMinY()}, {@link #getMaxY()} - Vertical bounds
* </ul>
*
* <p><strong>Within Relationship:</strong>
*
* <p>The "within" relationship is used to determine if the query shape is contained within an
* indexed shape. The {@link WithinRelation} enum provides three possible outcomes:
*
* <ul>
* <li>{@link WithinRelation#CANDIDATE} - The shape might be within (requires further validation)
* <li>{@link WithinRelation#NOTWITHIN} - The shape is definitely not within
* <li>{@link WithinRelation#DISJOINT} - The shapes don't intersect at all
* </ul>
*
* <p>Performance Considerations:
*
* <ul>
* <li>Bounding box checks ({@link #getMinX()}, etc.) enable elimination of non-intersecting
* geometries
* <li>Methods accepting pre-computed bounding boxes (minX, maxX, minY, maxY) avoid redundant
* calculations
* <li>Default methods compute bounding boxes automatically for convenience
* <li>Implementations should optimize for the most common query patterns in their domain
* </ul>
*
* <p>Coordinate Systems:
*
* <p>Component2D works with encoded coordinate values:
*
* <ul>
* <li><strong>Geographic</strong> - X represents longitude, Y represents latitude (both encoded)
* <li><strong>Cartesian</strong> - X and Y represent planar coordinates
* </ul>
*
* <p>Utility Methods:
*
* <p>Component2D provides several static utility methods for common spatial operations:
*
* <ul>
* <li>{@link #disjoint} - Tests if two bounding boxes don't overlap
* <li>{@link #within} - Tests if one bounding box is fully contained within another
* <li>{@link #containsPoint} - Tests if a point is inside a rectangle
* <li>{@link #pointInTriangle} - Tests if a point is inside a triangle using winding order
* </ul>
*
* @lucene.internal
* @see Circle2D
* @see Polygon2D
* @see LatLonGeometry
* @see XYGeometry
*/
public interface Component2D {

Expand Down
Loading
Loading