diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 14a95c88fc84..9e9f1dcc8f47 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -176,6 +176,8 @@ Improvements * GITHUB#15332: Add PhraseQuery.Builder.setMaxTerms() method to limit the maximum number of terms and excessive memory use (linyunanit) +* GITHUB#15356: Improve docs for 4 internal classes in geo package (Matt Baranowski) + Optimizations --------------------- * GITHUB#15140: Optimize TopScoreDocCollector with TernaryLongHeap for improved performance over Binary-LongHeap. (Ramakrishna Chilaka) diff --git a/lucene/core/src/java/org/apache/lucene/geo/Circle2D.java b/lucene/core/src/java/org/apache/lucene/geo/Circle2D.java index 8fd98adbc55b..6c228bde49c8 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Circle2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Circle2D.java @@ -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. + * + *

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. + * + *

Key Features: + * + *

+ * + *

Distance Calculation Modes: + * + *

+ * + *

Usage: + * + *

This class is created through the {@link Circle#toComponent2D()} or {@link + * XYCircle#toComponent2D()} methods: + * + *

{@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
+ * }
+ * + *

Spatial Operations: + * + *

Circle2D provides several spatial relationship methods: + * + *

+ * + *

Performance Considerations: + * + *

+ * + *

Implementation Details: + * + *

Circle2D uses a strategy pattern with {@link DistanceCalculator} implementations: + * + *

+ * + * @lucene.internal + * @see Circle + * @see XYCircle + * @see Component2D + */ class Circle2D implements Component2D { private final DistanceCalculator calculator; diff --git a/lucene/core/src/java/org/apache/lucene/geo/Component2D.java b/lucene/core/src/java/org/apache/lucene/geo/Component2D.java index 247f0ce09eb0..9bde00b84d17 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Component2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Component2D.java @@ -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. + * + *

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. + * + *

Key Features: + * + *

+ * + *

Implementations: + * + *

Component2D is implemented by geometric shapes classes: + * + *

+ * + *

Usage: + * + *

Component2D instances are created by calling {@code toComponent2D()} on high-level geometry + * objects: + * + *

{@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);
+ * }
+ * + *

Spatial Relationship Methods: + * + *

Containment: + * + *

+ * + *

Intersection: + * + *

+ * + *

Relationship: + * + *

+ * + *

Bounding Box: + * + *

Every Component2D has an associated bounding box defined by: + * + *

+ * + *

Within Relationship: + * + *

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: + * + *

+ * + *

Performance Considerations: + * + *

+ * + *

Coordinate Systems: + * + *

Component2D works with encoded coordinate values: + * + *

+ * + *

Utility Methods: + * + *

Component2D provides several static utility methods for common spatial operations: + * + *

* * @lucene.internal + * @see Circle2D + * @see Polygon2D + * @see LatLonGeometry + * @see XYGeometry */ public interface Component2D { diff --git a/lucene/core/src/java/org/apache/lucene/geo/ComponentTree.java b/lucene/core/src/java/org/apache/lucene/geo/ComponentTree.java index 073e3baf17d0..26009beaebc9 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/ComponentTree.java +++ b/lucene/core/src/java/org/apache/lucene/geo/ComponentTree.java @@ -21,9 +21,169 @@ import org.apache.lucene.util.ArrayUtil; /** - * 2D multi-component geometry implementation represented as an interval tree of components. + * Internal spatial index structure for querying multi-component geometries. * - *

Construction takes {@code O(n log n)} time for sorting and tree construction. + *

ComponentTree organizes multiple {@link Component2D} objects into a binary interval tree, + * enabling spatial queries across complex geometries with many parts. This structure is used + * internally when a geometry contains multiple components (e.g., a polygon with multiple holes, or + * a multi-polygon). + * + *

Key Features: + * + *

+ * + *

Tree Structure: + * + *

The tree is constructed by recursively splitting components along alternating dimensions: + * + *

+ * + *

This alternating split strategy ensures balanced spatial partitioning and efficient query + * performance across different geometry distributions. + * + *

Construction: + * + *

ComponentTree is created from an array of Component2D objects using the static {@link + * #create(Component2D[])} method: + * + *

{@code
+ * // Create multiple circle components
+ * double lat1 = 40.7128;
+ * double lon1 = -74.0060;
+ * double radius1 = 1000;
+ * Circle circle1 = new Circle(lat1, lon1, radius1);
+ *
+ * double lat2 = 40.7589;
+ * double lon2 = -73.9851;
+ * double radius2 = 1000;
+ * Circle circle2 = new Circle(lat2, lon2, radius2);
+ *
+ * double lat3 = 40.7484;
+ * double lon3 = -73.9857;
+ * double radius3 = 1000;
+ * Circle circle3 = new Circle(lat3, lon3, radius3);
+ *
+ * Component2D[] components = new Component2D[] {
+ *   circle1.toComponent2D(),
+ *   circle2.toComponent2D(),
+ *   circle3.toComponent2D()
+ * };
+ *
+ * // Create tree from components
+ * Component2D tree = ComponentTree.create(components);
+ *
+ * // Query the tree
+ * double testLon = -74.0060;
+ * double testLat = 40.7128;
+ * boolean contained = tree.contains(testLon, testLat);
+ * }
+ * + *

Performance Characteristics: + * + *

+ * + *

Query Operations: + * + *

ComponentTree supports all standard Component2D operations: + * + *

+ * + *

Queries traverse the tree recursively, using bounding box checks to prune branches that cannot + * contribute to the result. + * + *

Limitations: + * + *

+ * + *

Implementation Details: + * + *

Each node in the tree contains: + * + *

+ * + *

The tree construction algorithm: + * + *

    + *
  1. Sorts components along the split dimension at each level + *
  2. Selects median component as the split point + *
  3. Recursively builds left subtree with components below median + *
  4. Recursively builds right subtree with components above median + *
  5. Propagates maximum bounding box values up the tree + *
  6. Adjusts root node to contain consistent minimum bounds + *
+ * + *

Use Cases: + * + *

+ * + *

Example with Multiple Polygon Components: + * + *

{@code
+ * // Create multiple separate polygons (e.g., for a multi-polygon geometry)
+ * double[] lats1 = {40.7128, 40.7589, 40.7484, 40.7128};
+ * double[] lons1 = {-74.0060, -73.9851, -73.9857, -74.0060};
+ * Polygon polygon1 = new Polygon(lats1, lons1);
+ *
+ * double[] lats2 = {40.7300, 40.7400, 40.7350, 40.7300};
+ * double[] lons2 = {-73.9900, -73.9950, -73.9975, -73.9900};
+ * Polygon polygon2 = new Polygon(lats2, lons2);
+ *
+ * // Create a tree from multiple polygon components
+ * Component2D[] components = new Component2D[] {
+ *   polygon1.toComponent2D(),
+ *   polygon2.toComponent2D()
+ * };
+ *
+ * Component2D tree = ComponentTree.create(components);
+ *
+ * // Note: For a single polygon with holes, pass holes to the Polygon constructor:
+ * // Polygon hole = new Polygon(holeLats, holeLons);
+ * // Polygon polygonWithHole = new Polygon(outerLats, outerLons, hole);
+ * // Component2D component = polygonWithHole.toComponent2D();
+ * }
+ * + * @lucene.internal + * @see Component2D + * @see Circle2D + * @see Polygon2D */ final class ComponentTree implements Component2D { diff --git a/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java b/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java index ccd306ee25e9..3e2f8cd6c852 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java @@ -28,7 +28,141 @@ import java.util.Objects; import org.apache.lucene.index.PointValues; -/** 2D rectangle implementation containing cartesian spatial logic. */ +/** + * Internal 2D representation of a rectangle for spatial query operations. + * + *

This class provides spatial logic for rectangle-based queries, converting high-level {@link + * Rectangle} or {@link XYRectangle} geometries into 2D representations. It is used internally by + * Lucene's spatial search implementation for bounding box operations. + * + *

Key Features: + * + *

+ * + *

Coordinate Systems: + * + *

+ * + *

Usage: + * + *

Rectangle2D instances are created through the static {@link #create(Rectangle)} or {@link + * #create(XYRectangle)} factory methods: + * + *

{@code
+ * // Geographic rectangle (lat/lon)
+ * double minLat = 40.7;
+ * double maxLat = 40.8;
+ * double minLon = -74.0;
+ * double maxLon = -73.9;
+ * Rectangle geoRect = new Rectangle(minLat, maxLat, minLon, maxLon);
+ * Component2D geoComponent = Rectangle2D.create(geoRect);
+ *
+ * // Query operations
+ * double testLon = -73.95;
+ * double testLat = 40.75;
+ * boolean contained = geoComponent.contains(testLon, testLat);
+ *
+ * // Cartesian rectangle (XY)
+ * float minX = 100.0f;
+ * float maxX = 200.0f;
+ * float minY = 150.0f;
+ * float maxY = 250.0f;
+ * XYRectangle xyRect = new XYRectangle(minX, maxX, minY, maxY);
+ * Component2D xyComponent = Rectangle2D.create(xyRect);
+ *
+ * }
+ * + *

Dateline Crossing: + * + *

For geographic rectangles that cross the International Date Line (where minLon > maxLon), + * Rectangle2D automatically creates a {@link ComponentTree} containing two separate Rectangle2D + * components to handle the split geometry correctly: + * + *

{@code
+ * // Rectangle crossing the dateline (170°E to -170°E)
+ * double minLat = 20.0;
+ * double maxLat = 30.0;
+ * double minLon = 170.0;  // East of dateline
+ * double maxLon = -170.0; // West of dateline
+ * Rectangle datelineRect = new Rectangle(minLat, maxLat, minLon, maxLon);
+ *
+ * // Creates ComponentTree with two rectangles internally:
+ * // 1. From 170°E to 180°E
+ * // 2. From -180°E to -170°E
+ * Component2D component = Rectangle2D.create(datelineRect);
+ * }
+ * + *

Performance Characteristics: + * + *

+ * + *

Spatial Operations: + * + *

Rectangle2D implements all {@link Component2D} operations: + * + *

+ * + *

Edge Intersection Algorithm: + * + *

Rectangle2D uses the {@link #edgesIntersect(double, double, double, double)} method to check + * if a line segment intersects any of the rectangle's four edges. This method: + * + *

    + *
  1. First performs a quick bounding box check to eliminate obvious non-intersections + *
  2. Tests intersection against each of the four rectangle edges (top, right, bottom, left) + *
  3. Uses {@link GeoUtils#lineCrossesLineWithBoundary} for precise intersection detection + *
+ * + *

Coordinate Encoding: + * + *

For geographic rectangles, latitude and longitude values are quantized to match Lucene's point + * encoding: + * + *

+ * + *

Use Cases: + * + *

+ * + * @lucene.internal + * @see Rectangle + * @see XYRectangle + * @see Component2D + * @see ComponentTree + */ final class Rectangle2D implements Component2D { private final double minX;