From 18e1b40b1c4a15379da865fef05f9a58a2dbdcbf Mon Sep 17 00:00:00 2001 From: Karl Wright Date: Mon, 7 Aug 2017 07:01:19 -0400 Subject: [PATCH] LUCENE-7918: Revamp the API for composites so that it's generic and useful for many kinds of shapes. Committed (as was LUCENE-7906) on behalf of Ignacio Vera. --- .../geom/GeoBaseCompositeAreaShape.java | 135 ++++++++++++++++++ .../geom/GeoBaseCompositeMembershipShape.java | 53 +++++++ .../spatial3d/geom/GeoBaseCompositeShape.java | 121 ++++++++++++++++ .../spatial3d/geom/GeoCompositeAreaShape.java | 119 +-------------- .../geom/GeoCompositeMembershipShape.java | 93 +----------- .../spatial3d/geom/GeoCompositePolygon.java | 11 +- .../lucene/spatial3d/geom/GeoPolygonTest.java | 8 +- 7 files changed, 330 insertions(+), 210 deletions(-) create mode 100644 lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeAreaShape.java create mode 100644 lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeMembershipShape.java create mode 100644 lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeShape.java diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeAreaShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeAreaShape.java new file mode 100644 index 00000000000..fea4eab501e --- /dev/null +++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeAreaShape.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.lucene.spatial3d.geom; + +/** + * Base class to create a composite of GeoAreaShapes + * + * @param is the type of GeoAreaShapes of the composite. + * @lucene.internal + */ +abstract class GeoBaseCompositeAreaShape extends GeoBaseCompositeMembershipShape implements GeoAreaShape { + + /** All edgepoints inside shape */ + protected final static int ALL_INSIDE = 0; + /** Some edgepoints inside shape */ + protected final static int SOME_INSIDE = 1; + /** No edgepoints inside shape */ + protected final static int NONE_INSIDE = 2; + + /** + * Constructor. + */ + public GeoBaseCompositeAreaShape() { + } + + @Override + public boolean intersects(GeoShape geoShape){ + for(GeoAreaShape geoAreaShape : shapes){ + if (geoAreaShape.intersects(geoShape)){ + return true; + } + } + return false; + } + + @Override + public int getRelationship(GeoShape geoShape) { + final int insideGeoAreaShape = isShapeInsideGeoAreaShape(geoShape); + if (insideGeoAreaShape == SOME_INSIDE) { + return GeoArea.OVERLAPS; + } + + final int insideShape = isGeoAreaShapeInsideShape(geoShape); + if (insideShape == SOME_INSIDE) { + return GeoArea.OVERLAPS; + } + + if (insideGeoAreaShape == ALL_INSIDE && insideShape==ALL_INSIDE) { + return GeoArea.OVERLAPS; + } + + if (intersects(geoShape)){ + return GeoArea.OVERLAPS; + } + + if (insideGeoAreaShape == ALL_INSIDE) { + return GeoArea.WITHIN; + } + + if (insideShape==ALL_INSIDE) { + return GeoArea.CONTAINS; + } + + return GeoArea.DISJOINT; + } + + /** Determine the relationship between the GeoAreShape and the + * shape's edgepoints. + *@param geoShape is the shape. + *@return the relationship. + */ + protected int isShapeInsideGeoAreaShape(final GeoShape geoShape) { + boolean foundOutside = false; + boolean foundInside = false; + for (GeoPoint p : geoShape.getEdgePoints()) { + if (isWithin(p)) { + foundInside = true; + } else { + foundOutside = true; + } + if (foundInside && foundOutside) { + return SOME_INSIDE; + } + } + if (!foundInside && !foundOutside) + return NONE_INSIDE; + if (foundInside && !foundOutside) + return ALL_INSIDE; + if (foundOutside && !foundInside) + return NONE_INSIDE; + return SOME_INSIDE; + } + + /** Determine the relationship between the GeoAreShape's edgepoints and the + * provided shape. + *@param geoshape is the shape. + *@return the relationship. + */ + protected int isGeoAreaShapeInsideShape(final GeoShape geoshape) { + boolean foundOutside = false; + boolean foundInside = false; + for (GeoPoint p : getEdgePoints()) { + if (geoshape.isWithin(p)) { + foundInside = true; + } else { + foundOutside = true; + } + if (foundInside && foundOutside) { + return SOME_INSIDE; + } + } + if (!foundInside && !foundOutside) + return NONE_INSIDE; + if (foundInside && !foundOutside) + return ALL_INSIDE; + if (foundOutside && !foundInside) + return NONE_INSIDE; + return SOME_INSIDE; + } +} diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeMembershipShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeMembershipShape.java new file mode 100644 index 00000000000..0ebc8e2a29f --- /dev/null +++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeMembershipShape.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.lucene.spatial3d.geom; + +/** + * Base class to create a composite of GeoMembershipShapes + * + * @param is the type of GeoMembershipShapes of the composite. + * @lucene.internal + */ +abstract class GeoBaseCompositeMembershipShape + extends GeoBaseCompositeShape implements GeoMembershipShape{ + + /** + * Constructor. + */ + GeoBaseCompositeMembershipShape() { + } + + @Override + public double computeOutsideDistance(final DistanceStyle distanceStyle, final GeoPoint point) { + return computeOutsideDistance(distanceStyle, point.x, point.y, point.z); + } + + @Override + public double computeOutsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) { + if (isWithin(x,y,z)) + return 0.0; + double distance = Double.POSITIVE_INFINITY; + for (GeoMembershipShape shape : shapes) { + final double normalDistance = shape.computeOutsideDistance(distanceStyle, x, y, z); + if (normalDistance < distance) { + distance = normalDistance; + } + } + return distance; + } +} diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeShape.java new file mode 100644 index 00000000000..16a507c14a4 --- /dev/null +++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCompositeShape.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.lucene.spatial3d.geom; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Base class to create a composite of GeoShapes. + * + * @param is the type of GeoShapes of the composite. + * @lucene.experimental + */ +public abstract class GeoBaseCompositeShape implements GeoShape { + + /** + * Shape's container + */ + protected final List shapes = new ArrayList<>(); + + /** + * Constructor. + */ + public GeoBaseCompositeShape() { + } + + /** + * Add a shape to the composite. + * + * @param shape is the shape to add. + */ + public void addShape(final T shape) { + shapes.add(shape); + } + + /** + * Get the number of shapes in the composite + * + * @return the number of shapes + */ + public int size() { + return shapes.size(); + } + + /** + * Get shape at index + * + * @return the shape at given index + */ + public T getShape(int index) { + return shapes.get(index); + } + + @Override + public boolean isWithin(final Vector point) { + return isWithin(point.x, point.y, point.z); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + for (GeoShape shape : shapes) { + if (shape.isWithin(x, y, z)) + return true; + } + return false; + } + + @Override + public GeoPoint[] getEdgePoints() { + List edgePoints = new ArrayList<>(); + for (GeoShape shape : shapes) { + edgePoints.addAll(Arrays.asList(shape.getEdgePoints())); + } + return edgePoints.toArray(new GeoPoint[edgePoints.size()]); + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + for (GeoShape shape : shapes) { + if (shape.intersects(p, notablePoints, bounds)) + return true; + } + return false; + } + + @Override + public void getBounds(Bounds bounds) { + for (GeoShape shape : shapes) { + shape.getBounds(bounds); + } + } + + @Override + public int hashCode() { + return super.hashCode() * 31 + shapes.hashCode();//TODO cache + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoBaseCompositeShape)) + return false; + GeoBaseCompositeShape other = (GeoBaseCompositeShape) o; + return super.equals(o) && shapes.equals(other.shapes); + } +} diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeAreaShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeAreaShape.java index 8671e624a27..695b3ea1f96 100644 --- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeAreaShape.java +++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeAreaShape.java @@ -22,126 +22,17 @@ package org.apache.lucene.spatial3d.geom; * * @lucene.experimental */ -public class GeoCompositeAreaShape extends GeoCompositeMembershipShape implements GeoAreaShape { +public class GeoCompositeAreaShape extends GeoBaseCompositeAreaShape { - /** Constructor. + /** + * Constructor. */ public GeoCompositeAreaShape() { } - /** - * Add a shape to the composite. It throw an IllegalArgumentException - * if the shape is not a GeoAreaShape - *@param shape is the shape to add. - */ @Override - public void addShape(final GeoMembershipShape shape) { - if (!(shape instanceof GeoAreaShape)){ - throw new IllegalArgumentException("GeoCompositeAreaShape must be composed of GeoAreaShapes"); - } - shapes.add(shape); - } - - @Override - public boolean intersects(GeoShape geoShape){ - for(GeoShape inShape : shapes){ - if (((GeoAreaShape)inShape).intersects(geoShape)){ - return true; - } - } - return false; - } - - /** All edgepoints inside shape */ - protected final static int ALL_INSIDE = 0; - /** Some edgepoints inside shape */ - protected final static int SOME_INSIDE = 1; - /** No edgepoints inside shape */ - protected final static int NONE_INSIDE = 2; - - /** Determine the relationship between the GeoAreShape and the - * shape's edgepoints. - *@param geoShape is the shape. - *@return the relationship. - */ - protected int isShapeInsideGeoAreaShape(final GeoShape geoShape) { - boolean foundOutside = false; - boolean foundInside = false; - for (GeoPoint p : geoShape.getEdgePoints()) { - if (isWithin(p)) { - foundInside = true; - } else { - foundOutside = true; - } - if (foundInside && foundOutside) { - return SOME_INSIDE; - } - } - if (!foundInside && !foundOutside) - return NONE_INSIDE; - if (foundInside && !foundOutside) - return ALL_INSIDE; - if (foundOutside && !foundInside) - return NONE_INSIDE; - return SOME_INSIDE; - } - - /** Determine the relationship between the GeoAreShape's edgepoints and the - * provided shape. - *@param geoshape is the shape. - *@return the relationship. - */ - protected int isGeoAreaShapeInsideShape(final GeoShape geoshape) { - boolean foundOutside = false; - boolean foundInside = false; - for (GeoPoint p : getEdgePoints()) { - if (geoshape.isWithin(p)) { - foundInside = true; - } else { - foundOutside = true; - } - if (foundInside && foundOutside) { - return SOME_INSIDE; - } - } - if (!foundInside && !foundOutside) - return NONE_INSIDE; - if (foundInside && !foundOutside) - return ALL_INSIDE; - if (foundOutside && !foundInside) - return NONE_INSIDE; - return SOME_INSIDE; - } - - @Override - public int getRelationship(GeoShape geoShape) { - final int insideGeoAreaShape = isShapeInsideGeoAreaShape(geoShape); - if (insideGeoAreaShape == SOME_INSIDE) { - return GeoArea.OVERLAPS; - } - - final int insideShape = isGeoAreaShapeInsideShape(geoShape); - if (insideShape == SOME_INSIDE) { - return GeoArea.OVERLAPS; - } - - if (insideGeoAreaShape == ALL_INSIDE && insideShape==ALL_INSIDE) { - return GeoArea.OVERLAPS; - } - - if (intersects(geoShape)){ - return GeoArea.OVERLAPS; - } - - if (insideGeoAreaShape == ALL_INSIDE) { - return GeoArea.WITHIN; - } - - if (insideShape==ALL_INSIDE) { - return GeoArea.CONTAINS; - } - - return GeoArea.DISJOINT; + public String toString() { + return "GeoCompositeAreaShape: {" + shapes + '}'; } } diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeMembershipShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeMembershipShape.java index 24428f90970..7aa4007091b 100755 --- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeMembershipShape.java +++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeMembershipShape.java @@ -16,102 +16,17 @@ */ package org.apache.lucene.spatial3d.geom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - /** - * GeoComposite is a set of GeoMembershipShape's, treated as a unit. + * GeoCompositeMembershipShape is a set of GeoMembershipShape's, treated as a unit. * * @lucene.experimental */ -public class GeoCompositeMembershipShape implements GeoMembershipShape { - /** The list of shapes. */ - protected final List shapes = new ArrayList(); - - /** Constructor. - */ - public GeoCompositeMembershipShape() { - } +public class GeoCompositeMembershipShape extends GeoBaseCompositeMembershipShape implements GeoMembershipShape { /** - * Add a shape to the composite. - *@param shape is the shape to add. + * Constructor. */ - public void addShape(final GeoMembershipShape shape) { - shapes.add(shape); - } - - @Override - public boolean isWithin(final Vector point) { - return isWithin(point.x, point.y, point.z); - } - - @Override - public boolean isWithin(final double x, final double y, final double z) { - for (GeoMembershipShape shape : shapes) { - if (shape.isWithin(x, y, z)) - return true; - } - return false; - } - - @Override - public GeoPoint[] getEdgePoints() { - List edgePoints = new ArrayList<>(); - for(int i=0;i implements GeoPolygon { + /** + * Constructor. */ public GeoCompositePolygon() { } + @Override + public String toString() { + return "GeoCompositePolygon: {" + shapes + '}'; + } } diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java index f3ac52bb352..3602e817aa0 100755 --- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java +++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java @@ -975,7 +975,7 @@ shape: points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.6)); points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5)); points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4)); - GeoPolygon polygon = (GeoPolygon)((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points)).shapes.get(0); + GeoPolygon polygon = ((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points)).getShape(0); GeoPolygon polygonConcave = GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE,points); assertEquals(polygon,polygonConcave); } @@ -994,7 +994,7 @@ shape: hole_points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4)); GeoPolygon hole = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,hole_points); - GeoPolygon polygon = (GeoPolygon)((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points,Collections.singletonList(hole))).shapes.get(0); + GeoPolygon polygon = ((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points,Collections.singletonList(hole))).getShape(0); GeoPolygon polygon2 = GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE,points,Collections.singletonList(hole)); assertEquals(polygon,polygon2); } @@ -1006,7 +1006,7 @@ shape: points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, 0.5)); points.add(new GeoPoint(PlanetModel.SPHERE, 0.5, 0.5)); points.add(new GeoPoint(PlanetModel.SPHERE, 0.5, 0)); - GeoPolygon polygon = (GeoPolygon)((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points)).shapes.get(0); + GeoPolygon polygon = ((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points)).getShape(0); GeoPolygon polygon2 = GeoPolygonFactory.makeGeoConvexPolygon(PlanetModel.SPHERE,points); assertEquals(polygon,polygon2); } @@ -1025,7 +1025,7 @@ shape: hole_points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4)); GeoPolygon hole = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,hole_points); - GeoPolygon polygon = (GeoPolygon)((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points,Collections.singletonList(hole))).shapes.get(0); + GeoPolygon polygon = ((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points,Collections.singletonList(hole))).getShape(0); GeoPolygon polygon2 = GeoPolygonFactory.makeGeoConvexPolygon(PlanetModel.SPHERE,points,Collections.singletonList(hole)); assertEquals(polygon,polygon2); }