mirror of https://github.com/apache/lucene.git
LUCENE-8620: Update Tessellator logic to label if triangle edges belongs to the original polygon (#771)
This commit is contained in:
parent
9510e06612
commit
62001b9b96
|
@ -127,6 +127,9 @@ Improvements
|
|||
|
||||
* LUCENE-8952: Use a sort key instead of true distance in NearestNeighbor (Julie Tibshirani).
|
||||
|
||||
* LUCENE-8620: Tessellator labels the edges of the generated triangles whether they belong to
|
||||
the original polygon. This information is added to the triangle encoding. (Ignacio Vera)
|
||||
|
||||
Optimizations
|
||||
|
||||
* LUCENE-8922: DisjunctionMaxQuery more efficiently leverages impacts to skip
|
||||
|
|
|
@ -47,16 +47,16 @@ final class LatLonShapeBoundingBoxQuery extends ShapeQuery {
|
|||
|
||||
/** returns true if the query matches the encoded triangle */
|
||||
@Override
|
||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
||||
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||
// decode indexed triangle
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
int aY = scratchTriangle[0];
|
||||
int aX = scratchTriangle[1];
|
||||
int bY = scratchTriangle[2];
|
||||
int bX = scratchTriangle[3];
|
||||
int cY = scratchTriangle[4];
|
||||
int cX = scratchTriangle[5];
|
||||
int aY = scratchTriangle.aY;
|
||||
int aX = scratchTriangle.aX;
|
||||
int bY = scratchTriangle.bY;
|
||||
int bX = scratchTriangle.bX;
|
||||
int cY = scratchTriangle.cY;
|
||||
int cX = scratchTriangle.cX;
|
||||
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
return rectangle2D.containsTriangle(aX, aY, bX, bY, cX, cY);
|
||||
|
|
|
@ -84,15 +84,15 @@ final class LatLonShapeLineQuery extends ShapeQuery {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
||||
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle[0]);
|
||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle[1]);
|
||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle[2]);
|
||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle[3]);
|
||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle[4]);
|
||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle[5]);
|
||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle.aY);
|
||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle.aX);
|
||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle.bY);
|
||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle.bX);
|
||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle.cY);
|
||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||
|
|
|
@ -78,15 +78,15 @@ final class LatLonShapePolygonQuery extends ShapeQuery {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
||||
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle[0]);
|
||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle[1]);
|
||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle[2]);
|
||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle[3]);
|
||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle[4]);
|
||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle[5]);
|
||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle.aY);
|
||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle.aX);
|
||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle.bY);
|
||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle.bX);
|
||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle.cY);
|
||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
*/
|
||||
package org.apache.lucene.document;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
|
@ -57,18 +59,22 @@ public final class ShapeField {
|
|||
*/
|
||||
public static class Triangle extends Field {
|
||||
|
||||
// constructor for points and lines
|
||||
Triangle(String name, int aXencoded, int aYencoded, int bXencoded, int bYencoded, int cXencoded, int cYencoded) {
|
||||
super(name, TYPE);
|
||||
setTriangleValue(aXencoded, aYencoded, bXencoded, bYencoded, cXencoded, cYencoded);
|
||||
setTriangleValue(aXencoded, aYencoded, true, bXencoded, bYencoded, true, cXencoded, cYencoded, true);
|
||||
}
|
||||
|
||||
|
||||
Triangle(String name, Tessellator.Triangle t) {
|
||||
super(name, TYPE);
|
||||
setTriangleValue(t.getEncodedX(0), t.getEncodedY(0), t.getEncodedX(1), t.getEncodedY(1), t.getEncodedX(2), t.getEncodedY(2));
|
||||
setTriangleValue(t.getEncodedX(0), t.getEncodedY(0), t.isEdgefromPolygon(0),
|
||||
t.getEncodedX(1), t.getEncodedY(1), t.isEdgefromPolygon(1),
|
||||
t.getEncodedX(2), t.getEncodedY(2), t.isEdgefromPolygon(2));
|
||||
}
|
||||
|
||||
/** sets the vertices of the triangle as integer encoded values */
|
||||
protected void setTriangleValue(int aX, int aY, int bX, int bY, int cX, int cY) {
|
||||
protected void setTriangleValue(int aX, int aY, boolean abFromShape, int bX, int bY, boolean bcFromShape, int cX, int cY, boolean caFromShape) {
|
||||
final byte[] bytes;
|
||||
|
||||
if (fieldsData == null) {
|
||||
|
@ -77,7 +83,7 @@ public final class ShapeField {
|
|||
} else {
|
||||
bytes = ((BytesRef) fieldsData).bytes;
|
||||
}
|
||||
encodeTriangle(bytes, aY, aX, bY, bX, cY, cX);
|
||||
encodeTriangle(bytes, aY, aX, abFromShape, bY, bX, bcFromShape, cY, cX, caFromShape);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,7 +106,7 @@ public final class ShapeField {
|
|||
* Triangles are encoded with CCW orientation and might be rotated to limit the number of possible reconstructions to 2^3.
|
||||
* Reconstruction always happens from west to east.
|
||||
*/
|
||||
public static void encodeTriangle(byte[] bytes, int aLat, int aLon, int bLat, int bLon, int cLat, int cLon) {
|
||||
public static void encodeTriangle(byte[] bytes, int aLat, int aLon, boolean abFromShape, int bLat, int bLon, boolean bcFromShape, int cLat, int cLon, boolean caFromShape) {
|
||||
assert bytes.length == 7 * BYTES;
|
||||
int aX;
|
||||
int bX;
|
||||
|
@ -108,6 +114,7 @@ public final class ShapeField {
|
|||
int aY;
|
||||
int bY;
|
||||
int cY;
|
||||
boolean ab, bc, ca;
|
||||
//change orientation if CW
|
||||
if (GeoUtils.orient(aLon, aLat, bLon, bLat, cLon, cLat) == -1) {
|
||||
aX = cLon;
|
||||
|
@ -116,6 +123,9 @@ public final class ShapeField {
|
|||
aY = cLat;
|
||||
bY = bLat;
|
||||
cY = aLat;
|
||||
ab = bcFromShape;
|
||||
bc = abFromShape;
|
||||
ca = caFromShape;
|
||||
} else {
|
||||
aX = aLon;
|
||||
bX = bLon;
|
||||
|
@ -123,27 +133,38 @@ public final class ShapeField {
|
|||
aY = aLat;
|
||||
bY = bLat;
|
||||
cY = cLat;
|
||||
ab = abFromShape;
|
||||
bc = bcFromShape;
|
||||
ca = caFromShape;
|
||||
}
|
||||
//rotate edges and place minX at the beginning
|
||||
if (bX < aX || cX < aX) {
|
||||
if (bX < cX) {
|
||||
int tempX = aX;
|
||||
int tempY = aY;
|
||||
boolean tempBool = ab;
|
||||
aX = bX;
|
||||
aY = bY;
|
||||
ab = bc;
|
||||
bX = cX;
|
||||
bY = cY;
|
||||
bc = ca;
|
||||
cX = tempX;
|
||||
cY = tempY;
|
||||
ca = tempBool;
|
||||
} else if (cX < aX) {
|
||||
int tempX = aX;
|
||||
int tempY = aY;
|
||||
boolean tempBool = ab;
|
||||
aX = cX;
|
||||
aY = cY;
|
||||
ab = ca;
|
||||
cX = bX;
|
||||
cY = bY;
|
||||
ca = bc;
|
||||
bX = tempX;
|
||||
bY = tempY;
|
||||
bc = tempBool;
|
||||
}
|
||||
} else if (aX == bX && aX == cX) {
|
||||
//degenerated case, all points with same longitude
|
||||
|
@ -152,21 +173,29 @@ public final class ShapeField {
|
|||
if (bY < cY) {
|
||||
int tempX = aX;
|
||||
int tempY = aY;
|
||||
boolean tempBool = ab;
|
||||
aX = bX;
|
||||
aY = bY;
|
||||
ab = bc;
|
||||
bX = cX;
|
||||
bY = cY;
|
||||
bc = ca;
|
||||
cX = tempX;
|
||||
cY = tempY;
|
||||
ca = tempBool;
|
||||
} else if (cY < aY) {
|
||||
int tempX = aX;
|
||||
int tempY = aY;
|
||||
boolean tempBool = ab;
|
||||
aX = cX;
|
||||
aY = cY;
|
||||
ab = ca;
|
||||
cX = bX;
|
||||
cY = bY;
|
||||
ca = bc;
|
||||
bX = tempX;
|
||||
bY = tempY;
|
||||
bc = tempBool;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -216,6 +245,9 @@ public final class ShapeField {
|
|||
} else {
|
||||
throw new IllegalArgumentException("Could not encode the provided triangle");
|
||||
}
|
||||
bits |= (ab) ? (1 << 3) : 0;
|
||||
bits |= (bc) ? (1 << 4) : 0;
|
||||
bits |= (ca) ? (1 << 5) : 0;
|
||||
NumericUtils.intToSortableBytes(minY, bytes, 0);
|
||||
NumericUtils.intToSortableBytes(minX, bytes, BYTES);
|
||||
NumericUtils.intToSortableBytes(maxY, bytes, 2 * BYTES);
|
||||
|
@ -225,83 +257,133 @@ public final class ShapeField {
|
|||
NumericUtils.intToSortableBytes(bits, bytes, 6 * BYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a triangle encoded by {@link ShapeField#encodeTriangle(byte[], int, int, int, int, int, int)}.
|
||||
/** Decode a triangle encoded by {@link ShapeField#encodeTriangle(byte[], int, int, boolean, int, int, boolean, int, int, boolean)}.
|
||||
*/
|
||||
public static void decodeTriangle(byte[] t, int[] triangle) {
|
||||
assert triangle.length == 6;
|
||||
public static void decodeTriangle(byte[] t, DecodedTriangle triangle) {
|
||||
final int aX, aY, bX, bY, cX, cY;
|
||||
final boolean ab, bc, ca;
|
||||
int bits = NumericUtils.sortableBytesToInt(t, 6 * BYTES);
|
||||
//extract the first three bits
|
||||
int tCode = (((1 << 3) - 1) & (bits >> 0));
|
||||
switch (tCode) {
|
||||
case MINY_MINX_MAXY_MAXX_Y_X:
|
||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
aY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
bY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
bX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
cY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
cX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
break;
|
||||
case MINY_MINX_Y_X_MAXY_MAXX:
|
||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
aY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
bY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
bX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
cY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
cX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
break;
|
||||
case MAXY_MINX_Y_X_MINY_MAXX:
|
||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
aY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
bY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
bX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
cY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
cX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
break;
|
||||
case MAXY_MINX_MINY_MAXX_Y_X:
|
||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
aY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
bY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
bX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
cY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
cX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
break;
|
||||
case Y_MINX_MINY_X_MAXY_MAXX:
|
||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
aY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
bY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
bX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
cY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
cX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
break;
|
||||
case Y_MINX_MINY_MAXX_MAXY_X:
|
||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
aY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
bY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
bX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
cY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
cX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
break;
|
||||
case MAXY_MINX_MINY_X_Y_MAXX:
|
||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
aY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
bY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
bX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
cY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
cX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
break;
|
||||
case MINY_MINX_Y_MAXX_MAXY_X:
|
||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
aY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||
bY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||
bX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||
cY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||
cX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Could not decode the provided triangle");
|
||||
}
|
||||
//Points of the decoded triangle must be co-planar or CCW oriented
|
||||
assert GeoUtils.orient(triangle[1], triangle[0], triangle[3], triangle[2], triangle[5], triangle[4]) >= 0;
|
||||
assert GeoUtils.orient(aX, aY, bX, bY, cX, cY) >= 0;
|
||||
ab = (bits & 1 << 3) == 1 << 3;
|
||||
bc = (bits & 1 << 4) == 1 << 4;
|
||||
ca = (bits & 1 << 5) == 1 << 5;
|
||||
triangle.setValues(aX, aY, ab, bX, bY, bc, cX, cY, ca);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a encoded triangle using {@link ShapeField#decodeTriangle(byte[], DecodedTriangle)}.
|
||||
*/
|
||||
public static class DecodedTriangle {
|
||||
//Triangle vertices
|
||||
public int aX, aY, bX, bY, cX, cY;
|
||||
//Represent if edges belongs to original shape
|
||||
public boolean ab, bc, ca;
|
||||
|
||||
public DecodedTriangle() {
|
||||
}
|
||||
|
||||
private void setValues(int aX, int aY, boolean ab, int bX, int bY, boolean bc, int cX, int cY, boolean ca) {
|
||||
this.aX = aX;
|
||||
this.aY = aY;
|
||||
this.ab = ab;
|
||||
this.bX = bX;
|
||||
this.bY = bY;
|
||||
this.bc = bc;
|
||||
this.cX = cX;
|
||||
this.cY = cY;
|
||||
this.ca = ca;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(aX, aY, bX, bY, cX, cY, ab, bc, ca);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
DecodedTriangle other = (DecodedTriangle) o;
|
||||
return aX == other.aX && bX == other.bX && cX == other.cX
|
||||
&& aY == other.aY && bY == other.bY && cY == other.cY
|
||||
&& ab == other.ab && bc == other.bc && ca == other.ca;
|
||||
}
|
||||
|
||||
/** pretty print the triangle vertices */
|
||||
public String toString() {
|
||||
String result = aX + ", " + aY + " " +
|
||||
bX + ", " + bY + " " +
|
||||
cX + ", " + cY + " " + "[" + ab + "," +bc + "," + ca + "]";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ abstract class ShapeQuery extends Query {
|
|||
int maxXOffset, int maxYOffset, byte[] maxTriangle);
|
||||
|
||||
/** returns true if the provided triangle matches the query */
|
||||
protected abstract boolean queryMatches(byte[] triangle, int[] scratchTriangle, ShapeField.QueryRelation queryRelation);
|
||||
protected abstract boolean queryMatches(byte[] triangle, ShapeField.DecodedTriangle scratchTriangle, ShapeField.QueryRelation queryRelation);
|
||||
|
||||
/** relates a range of triangles (internal node) to the query */
|
||||
protected Relation relateRangeToQuery(byte[] minTriangle, byte[] maxTriangle, QueryRelation queryRelation) {
|
||||
|
@ -110,7 +110,7 @@ abstract class ShapeQuery extends Query {
|
|||
/** create a visitor that adds documents that match the query using a sparse bitset. (Used by INTERSECT) */
|
||||
protected IntersectVisitor getSparseIntersectVisitor(DocIdSetBuilder result) {
|
||||
return new IntersectVisitor() {
|
||||
final int[] scratchTriangle = new int[6];
|
||||
final ShapeField.DecodedTriangle scratchTriangle = new ShapeField.DecodedTriangle();
|
||||
DocIdSetBuilder.BulkAdder adder;
|
||||
|
||||
@Override
|
||||
|
@ -150,7 +150,7 @@ abstract class ShapeQuery extends Query {
|
|||
/** create a visitor that adds documents that match the query using a dense bitset. (Used by WITHIN, DISJOINT) */
|
||||
protected IntersectVisitor getDenseIntersectVisitor(FixedBitSet intersect, FixedBitSet disjoint, ShapeField.QueryRelation queryRelation) {
|
||||
return new IntersectVisitor() {
|
||||
final int[] scratchTriangle = new int[6];
|
||||
final ShapeField.DecodedTriangle scratchTriangle = new ShapeField.DecodedTriangle();
|
||||
@Override
|
||||
public void visit(int docID) throws IOException {
|
||||
if (queryRelation == ShapeField.QueryRelation.DISJOINT) {
|
||||
|
@ -330,7 +330,7 @@ abstract class ShapeQuery extends Query {
|
|||
/** create a visitor that clears documents that do NOT match the polygon query; used with INTERSECTS */
|
||||
private IntersectVisitor getInverseIntersectVisitor(ShapeQuery query, FixedBitSet result, int[] cost) {
|
||||
return new IntersectVisitor() {
|
||||
int[] scratchTriangle = new int[6];
|
||||
final ShapeField.DecodedTriangle scratchTriangle = new ShapeField.DecodedTriangle();
|
||||
@Override
|
||||
public void visit(int docID) {
|
||||
result.clear(docID);
|
||||
|
|
|
@ -46,16 +46,16 @@ public class XYShapeBoundingBoxQuery extends ShapeQuery {
|
|||
|
||||
/** returns true if the query matches the encoded triangle */
|
||||
@Override
|
||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
||||
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||
// decode indexed triangle
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
int aY = scratchTriangle[0];
|
||||
int aX = scratchTriangle[1];
|
||||
int bY = scratchTriangle[2];
|
||||
int bX = scratchTriangle[3];
|
||||
int cY = scratchTriangle[4];
|
||||
int cX = scratchTriangle[5];
|
||||
int aY = scratchTriangle.aY;
|
||||
int aX = scratchTriangle.aX;
|
||||
int bY = scratchTriangle.bY;
|
||||
int bX = scratchTriangle.bX;
|
||||
int cY = scratchTriangle.cY;
|
||||
int cX = scratchTriangle.cX;
|
||||
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
return rectangle2D.containsTriangle(aX, aY, bX, bY, cX, cY);
|
||||
|
|
|
@ -86,15 +86,15 @@ final class XYShapeLineQuery extends ShapeQuery {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
||||
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
double alat = decode(scratchTriangle[0]);
|
||||
double alon = decode(scratchTriangle[1]);
|
||||
double blat = decode(scratchTriangle[2]);
|
||||
double blon = decode(scratchTriangle[3]);
|
||||
double clat = decode(scratchTriangle[4]);
|
||||
double clon = decode(scratchTriangle[5]);
|
||||
double alat = decode(scratchTriangle.aY);
|
||||
double alon = decode(scratchTriangle.aX);
|
||||
double blat = decode(scratchTriangle.bY);
|
||||
double blon = decode(scratchTriangle.bX);
|
||||
double clat = decode(scratchTriangle.cY);
|
||||
double clon = decode(scratchTriangle.cX);
|
||||
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||
|
|
|
@ -26,6 +26,8 @@ import org.apache.lucene.geo.XYPolygon2D;
|
|||
import org.apache.lucene.index.PointValues.Relation;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
|
||||
import static org.apache.lucene.geo.XYEncodingUtils.decode;
|
||||
|
||||
/**
|
||||
* Finds all previously indexed cartesian shapes that intersect the specified arbitrary cartesian {@link XYPolygon}.
|
||||
*
|
||||
|
@ -76,15 +78,15 @@ final class XYShapePolygonQuery extends ShapeQuery {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
||||
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
double alat = XYEncodingUtils.decode(scratchTriangle[0]);
|
||||
double alon = XYEncodingUtils.decode(scratchTriangle[1]);
|
||||
double blat = XYEncodingUtils.decode(scratchTriangle[2]);
|
||||
double blon = XYEncodingUtils.decode(scratchTriangle[3]);
|
||||
double clat = XYEncodingUtils.decode(scratchTriangle[4]);
|
||||
double clon = XYEncodingUtils.decode(scratchTriangle[5]);
|
||||
double alat = decode(scratchTriangle.aY);
|
||||
double alon = decode(scratchTriangle.aX);
|
||||
double blat = decode(scratchTriangle.bY);
|
||||
double blon = decode(scratchTriangle.bX);
|
||||
double clat = decode(scratchTriangle.cY);
|
||||
double clon = decode(scratchTriangle.cX);
|
||||
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||
|
|
|
@ -175,7 +175,7 @@ final public class Tessellator {
|
|||
}
|
||||
// if first and last node are the same then remove the end node and set lastNode to the start
|
||||
if (lastNode != null && isVertexEquals(lastNode, lastNode.next)) {
|
||||
removeNode(lastNode);
|
||||
removeNode(lastNode, true);
|
||||
lastNode = lastNode.next;
|
||||
}
|
||||
|
||||
|
@ -286,7 +286,7 @@ final public class Tessellator {
|
|||
Node sharedVertex = getSharedVertex(holeNode, next);
|
||||
if (sharedVertex != null) {
|
||||
// Split the resulting polygon.
|
||||
Node node = splitPolygon(next, sharedVertex);
|
||||
Node node = splitPolygon(next, sharedVertex, true);
|
||||
// Filter the split nodes.
|
||||
filterPoints(node, node.next);
|
||||
return;
|
||||
|
@ -300,8 +300,10 @@ final public class Tessellator {
|
|||
|
||||
// Determine whether a hole bridge could be fetched.
|
||||
if(outerNode != null) {
|
||||
// compute if the bridge overlaps with a polygon edge.
|
||||
boolean fromPolygon = isPointInLine(outerNode, outerNode.next, holeNode) || isPointInLine(holeNode, holeNode.next, outerNode);
|
||||
// Split the resulting polygon.
|
||||
Node node = splitPolygon(outerNode, holeNode);
|
||||
Node node = splitPolygon(outerNode, holeNode, fromPolygon);
|
||||
// Filter the split nodes.
|
||||
filterPoints(node, node.next);
|
||||
}
|
||||
|
@ -369,7 +371,7 @@ final public class Tessellator {
|
|||
}
|
||||
|
||||
/** Check if the provided vertex is in the polygon and return it **/
|
||||
private static Node getSharedVertex(Node polygon, Node vertex) {
|
||||
private static Node getSharedVertex(final Node polygon, final Node vertex) {
|
||||
Node next = polygon;
|
||||
do {
|
||||
if (isVertexEquals(next, vertex)) {
|
||||
|
@ -417,10 +419,14 @@ final public class Tessellator {
|
|||
// Determine whether the current triangle must be cut off.
|
||||
final boolean isReflex = area(prevNode.getX(), prevNode.getY(), currEar.getX(), currEar.getY(), nextNode.getX(), nextNode.getY()) >= 0;
|
||||
if (isReflex == false && isEar(currEar, mortonOptimized) == true) {
|
||||
// Compute if edges belong to the polygon
|
||||
boolean abFromPolygon = prevNode.isNextEdgeFromPolygon;
|
||||
boolean bcFromPolygon = currEar.isNextEdgeFromPolygon;
|
||||
boolean caFromPolygon = isEdgeFromPolygon(prevNode, nextNode, mortonOptimized);
|
||||
// Return the triangulated data
|
||||
tessellation.add(new Triangle(prevNode, currEar, nextNode));
|
||||
tessellation.add(new Triangle(prevNode, abFromPolygon, currEar, bcFromPolygon, nextNode, caFromPolygon));
|
||||
// Remove the ear node.
|
||||
removeNode(currEar);
|
||||
removeNode(currEar, caFromPolygon);
|
||||
|
||||
// Skipping to the next node leaves fewer slither triangles.
|
||||
currEar = nextNode.next;
|
||||
|
@ -439,7 +445,7 @@ final public class Tessellator {
|
|||
continue earcut;
|
||||
case CURE:
|
||||
// if this didn't work, try curing all small self-intersections locally
|
||||
currEar = cureLocalIntersections(currEar, tessellation);
|
||||
currEar = cureLocalIntersections(currEar, tessellation, mortonOptimized);
|
||||
state = State.SPLIT;
|
||||
continue earcut;
|
||||
case SPLIT:
|
||||
|
@ -531,7 +537,7 @@ final public class Tessellator {
|
|||
}
|
||||
|
||||
/** Iterate through all polygon nodes and remove small local self-intersections **/
|
||||
private static final Node cureLocalIntersections(Node startNode, final List<Triangle> tessellation) {
|
||||
private static final Node cureLocalIntersections(Node startNode, final List<Triangle> tessellation, final boolean mortonOptimized) {
|
||||
Node node = startNode;
|
||||
Node nextNode;
|
||||
do {
|
||||
|
@ -544,12 +550,17 @@ final public class Tessellator {
|
|||
&& isIntersectingPolygon(a, a.getX(), a.getY(), b.getX(), b.getY()) == false
|
||||
&& linesIntersect(a.getX(), a.getY(), node.getX(), node.getY(), nextNode.getX(), nextNode.getY(), b.getX(), b.getY())
|
||||
&& isLocallyInside(a, b) && isLocallyInside(b, a)) {
|
||||
// compute edges from polygon
|
||||
boolean abFromPolygon = (a.next == node) ? a.isNextEdgeFromPolygon : isEdgeFromPolygon(a, node, mortonOptimized);
|
||||
boolean bcFromPolygon = (node.next == b) ? node.isNextEdgeFromPolygon : isEdgeFromPolygon(node, b, mortonOptimized);
|
||||
boolean caFromPolygon = (b.next == a) ? b.isNextEdgeFromPolygon : isEdgeFromPolygon(a, b, mortonOptimized);
|
||||
tessellation.add(new Triangle(a, abFromPolygon, node, bcFromPolygon, b, caFromPolygon));
|
||||
// Return the triangulated vertices to the tessellation
|
||||
tessellation.add(new Triangle(a, node, b));
|
||||
tessellation.add(new Triangle(a, abFromPolygon, node, bcFromPolygon, b, caFromPolygon));
|
||||
|
||||
// remove two nodes involved
|
||||
removeNode(node);
|
||||
removeNode(node.next);
|
||||
removeNode(node, caFromPolygon);
|
||||
removeNode(node.next, caFromPolygon);
|
||||
node = startNode = b;
|
||||
}
|
||||
node = node.next;
|
||||
|
@ -559,7 +570,7 @@ final public class Tessellator {
|
|||
}
|
||||
|
||||
/** Attempt to split a polygon and independently triangulate each side. Return true if the polygon was splitted **/
|
||||
private static final boolean splitEarcut(Object polygon, final Node start, final List<Triangle> tessellation, final boolean mortonIndexed) {
|
||||
private static final boolean splitEarcut(final Object polygon, final Node start, final List<Triangle> tessellation, final boolean mortonOptimized) {
|
||||
// Search for a valid diagonal that divides the polygon into two.
|
||||
Node searchNode = start;
|
||||
Node nextNode;
|
||||
|
@ -569,17 +580,17 @@ final public class Tessellator {
|
|||
while (diagonal != searchNode.previous) {
|
||||
if(searchNode.idx != diagonal.idx && isValidDiagonal(searchNode, diagonal)) {
|
||||
// Split the polygon into two at the point of the diagonal
|
||||
Node splitNode = splitPolygon(searchNode, diagonal);
|
||||
Node splitNode = splitPolygon(searchNode, diagonal, isEdgeFromPolygon(searchNode, diagonal, mortonOptimized));
|
||||
// Filter the resulting polygon.
|
||||
searchNode = filterPoints(searchNode, searchNode.next);
|
||||
splitNode = filterPoints(splitNode, splitNode.next);
|
||||
// Attempt to earcut both of the resulting polygons
|
||||
if (mortonIndexed) {
|
||||
if (mortonOptimized) {
|
||||
sortByMortonWithReset(searchNode);
|
||||
sortByMortonWithReset(splitNode);
|
||||
}
|
||||
earcutLinkedList(polygon, searchNode, tessellation, State.INIT, mortonIndexed);
|
||||
earcutLinkedList(polygon, splitNode, tessellation, State.INIT, mortonIndexed);
|
||||
earcutLinkedList(polygon, searchNode, tessellation, State.INIT, mortonOptimized);
|
||||
earcutLinkedList(polygon, splitNode, tessellation, State.INIT, mortonOptimized);
|
||||
// Finish the iterative search
|
||||
return true;
|
||||
}
|
||||
|
@ -590,14 +601,120 @@ final public class Tessellator {
|
|||
return false;
|
||||
}
|
||||
|
||||
/** Computes if edge defined by a and b overlaps with a polygon edge **/
|
||||
private static boolean isEdgeFromPolygon(final Node a, final Node b, final boolean isMorton) {
|
||||
if (isMorton) {
|
||||
return isMortonEdgeFromPolygon(a, b);
|
||||
}
|
||||
Node next = a;
|
||||
do {
|
||||
if (isPointInLine(next, next.next, a) && isPointInLine(next, next.next, b)) {
|
||||
return next.isNextEdgeFromPolygon;
|
||||
}
|
||||
if (isPointInLine(next, next.previous, a) && isPointInLine(next, next.previous, b)) {
|
||||
return next.previous.isNextEdgeFromPolygon;
|
||||
}
|
||||
next = next.next;
|
||||
} while(next != a);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Uses morton code for speed to determine whether or not and edge defined by a and b overlaps with a polygon edge */
|
||||
private static final boolean isMortonEdgeFromPolygon(final Node a, final Node b) {
|
||||
// edge bbox (flip the bits so negative encoded values are < positive encoded values)
|
||||
final int minTX = StrictMath.min(a.x, b.x) ^ 0x80000000;
|
||||
final int minTY = StrictMath.min(a.y, b.y) ^ 0x80000000;
|
||||
final int maxTX = StrictMath.max(a.x, b.x) ^ 0x80000000;
|
||||
final int maxTY = StrictMath.max(a.y, b.y) ^ 0x80000000;
|
||||
|
||||
// z-order range for the current edge;
|
||||
final long minZ = BitUtil.interleave(minTX, minTY);
|
||||
final long maxZ = BitUtil.interleave(maxTX, maxTY);
|
||||
|
||||
// now make sure we don't have other points inside the potential ear;
|
||||
|
||||
// look for points inside edge in both directions
|
||||
Node p = a.previousZ;
|
||||
Node n = a.nextZ;
|
||||
while (p != null && Long.compareUnsigned(p.morton, minZ) >= 0
|
||||
&& n != null && Long.compareUnsigned(n.morton, maxZ) <= 0) {
|
||||
if (isPointInLine(p, p.next, a) && isPointInLine(p, p.next, b)) {
|
||||
return p.isNextEdgeFromPolygon;
|
||||
}
|
||||
if (isPointInLine(p, p.previous, a) && isPointInLine(p, p.previous, b)) {
|
||||
return p.previous.isNextEdgeFromPolygon;
|
||||
}
|
||||
|
||||
p = p.previousZ;
|
||||
|
||||
if (isPointInLine(n, n.next, a) && isPointInLine(n, n.next, b)) {
|
||||
return n.isNextEdgeFromPolygon;
|
||||
}
|
||||
if (isPointInLine(n, n.previous, a) && isPointInLine(n, n.previous, b)) {
|
||||
return n.previous.isNextEdgeFromPolygon;
|
||||
}
|
||||
|
||||
n = n.nextZ;
|
||||
}
|
||||
|
||||
// first look for points inside the edge in decreasing z-order
|
||||
while (p != null && Long.compareUnsigned(p.morton, minZ) >= 0) {
|
||||
if (isPointInLine(p, p.next, a) && isPointInLine(p, p.next, b)) {
|
||||
return p.isNextEdgeFromPolygon;
|
||||
}
|
||||
if (isPointInLine(p, p.previous, a) && isPointInLine(p, p.previous, b)) {
|
||||
return p.previous.isNextEdgeFromPolygon;
|
||||
}
|
||||
p = p.previousZ;
|
||||
}
|
||||
// then look for points in increasing z-order
|
||||
while (n != null &&
|
||||
Long.compareUnsigned(n.morton, maxZ) <= 0) {
|
||||
if (isPointInLine(n, n.next, a) && isPointInLine(n, n.next, b)) {
|
||||
return n.isNextEdgeFromPolygon;
|
||||
}
|
||||
if (isPointInLine(n, n.previous, a) && isPointInLine(n, n.previous, b)) {
|
||||
return n.previous.isNextEdgeFromPolygon;
|
||||
}
|
||||
n = n.nextZ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isPointInLine(final Node a, final Node b, final Node point) {
|
||||
return isPointInLine(a, b, point.getX(), point.getY());
|
||||
}
|
||||
|
||||
private static boolean isPointInLine(final Node a, final Node b, final double lon, final double lat) {
|
||||
final double dxc = lon - a.getX();
|
||||
final double dyc = lat - a.getY();
|
||||
|
||||
final double dxl = b.getX() - a.getX();
|
||||
final double dyl = b.getY() - a.getY();
|
||||
|
||||
if (dxc * dyl - dyc * dxl == 0) {
|
||||
if (Math.abs(dxl) >= Math.abs(dyl)) {
|
||||
return dxl > 0 ?
|
||||
a.getX() <= lon && lon <= b.getX() :
|
||||
b.getX() <= lon && lon <= a.getX();
|
||||
} else {
|
||||
return dyl > 0 ?
|
||||
a.getY() <= lat && lat <= b.getY() :
|
||||
b.getY() <= lat && lat <= a.getY();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Links two polygon vertices using a bridge. **/
|
||||
private static final Node splitPolygon(final Node a, final Node b) {
|
||||
private static final Node splitPolygon(final Node a, final Node b, boolean edgeFromPolygon) {
|
||||
final Node a2 = new Node(a);
|
||||
final Node b2 = new Node(b);
|
||||
final Node an = a.next;
|
||||
final Node bp = b.previous;
|
||||
|
||||
a.next = b;
|
||||
a.isNextEdgeFromPolygon = edgeFromPolygon;
|
||||
a.nextZ = b;
|
||||
b.previous = a;
|
||||
b.previousZ = a;
|
||||
|
@ -606,6 +723,7 @@ final public class Tessellator {
|
|||
an.previous = a2;
|
||||
an.previousZ = a2;
|
||||
b2.next = a2;
|
||||
b2.isNextEdgeFromPolygon = edgeFromPolygon;
|
||||
b2.nextZ = a2;
|
||||
a2.previous = b2;
|
||||
a2.previousZ = b2;
|
||||
|
@ -628,7 +746,7 @@ final public class Tessellator {
|
|||
}
|
||||
|
||||
/** Determine whether the polygon defined between node start and node end is CW */
|
||||
private static boolean isCWPolygon(Node start, Node end) {
|
||||
private static boolean isCWPolygon(final Node start, final Node end) {
|
||||
Node next = start;
|
||||
double windingSum = 0;
|
||||
do {
|
||||
|
@ -796,10 +914,13 @@ final public class Tessellator {
|
|||
continueIteration = false;
|
||||
nextNode = node.next;
|
||||
prevNode = node.previous;
|
||||
if (isVertexEquals(node, nextNode)
|
||||
|| area(prevNode.getX(), prevNode.getY(), node.getX(), node.getY(), nextNode.getX(), nextNode.getY()) == 0) {
|
||||
//We can filter points when they are the same, if not and they are co-linear we can only
|
||||
//remove it if both edges have the same value in .isNextEdgeFromPolygon
|
||||
if (isVertexEquals(node, nextNode) ||
|
||||
(prevNode.isNextEdgeFromPolygon == node.isNextEdgeFromPolygon &&
|
||||
area(prevNode.getX(), prevNode.getY(), node.getX(), node.getY(), nextNode.getX(), nextNode.getY()) == 0)) {
|
||||
// Remove the node
|
||||
removeNode(node);
|
||||
removeNode(node, prevNode.isNextEdgeFromPolygon);
|
||||
node = end = prevNode;
|
||||
|
||||
if (node == nextNode) {
|
||||
|
@ -835,9 +956,10 @@ final public class Tessellator {
|
|||
}
|
||||
|
||||
/** Removes a node from the doubly linked list */
|
||||
private static final void removeNode(Node node) {
|
||||
private static final void removeNode(Node node, boolean edgeFromPolygon) {
|
||||
node.next.previous = node.previous;
|
||||
node.previous.next = node.next;
|
||||
node.previous.isNextEdgeFromPolygon = edgeFromPolygon;
|
||||
|
||||
if (node.previousZ != null) {
|
||||
node.previousZ.nextZ = node.nextZ;
|
||||
|
@ -873,6 +995,13 @@ final public class Tessellator {
|
|||
|
||||
/** compute whether the given x, y point is in a triangle; uses the winding order method */
|
||||
public static boolean pointInTriangle (double x, double y, double ax, double ay, double bx, double by, double cx, double cy) {
|
||||
double minX = StrictMath.min(ax, StrictMath.min(bx, cx));
|
||||
double minY = StrictMath.min(ay, StrictMath.min(by, cy));
|
||||
double maxX = StrictMath.max(ax, StrictMath.max(bx, cx));
|
||||
double maxY = StrictMath.max(ay, StrictMath.max(by, cy));
|
||||
//check the bounding box because if the triangle is degenerated, e.g points and lines, we need to filter out
|
||||
//coplanar points that are not part of the triangle.
|
||||
if (x >= minX && x <= maxX && y >= minY && y <= maxY ) {
|
||||
int a = orient(x, y, ax, ay, bx, by);
|
||||
int b = orient(x, y, bx, by, cx, cy);
|
||||
if (a == 0 || b == 0 || a < 0 == b < 0) {
|
||||
|
@ -880,6 +1009,9 @@ final public class Tessellator {
|
|||
return c == 0 || (c < 0 == (b < 0 || a < 0));
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Brute force compute if a point is in the polygon by traversing entire triangulation
|
||||
|
@ -901,8 +1033,7 @@ final public class Tessellator {
|
|||
private final int idx;
|
||||
// vertex index in the polygon
|
||||
private final int vrtxIdx;
|
||||
// reference to the polygon for lat/lon values
|
||||
// private final Polygon polygon;
|
||||
// reference to the polygon for lat/lon values;
|
||||
private final double[] polyX;
|
||||
private final double[] polyY;
|
||||
// encoded x value
|
||||
|
@ -920,6 +1051,8 @@ final public class Tessellator {
|
|||
private Node previousZ;
|
||||
// next z node
|
||||
private Node nextZ;
|
||||
// if the edge from this node to the next node is part of the polygon edges
|
||||
private boolean isNextEdgeFromPolygon;
|
||||
|
||||
protected Node(final double[] x, final double[] y, final int index, final int vertexIndex, final boolean isGeo) {
|
||||
this.idx = index;
|
||||
|
@ -933,6 +1066,7 @@ final public class Tessellator {
|
|||
this.next = null;
|
||||
this.previousZ = null;
|
||||
this.nextZ = null;
|
||||
this.isNextEdgeFromPolygon = true;
|
||||
}
|
||||
|
||||
/** simple deep copy constructor */
|
||||
|
@ -948,6 +1082,7 @@ final public class Tessellator {
|
|||
this.next = other.next;
|
||||
this.previousZ = other.previousZ;
|
||||
this.nextZ = other.nextZ;
|
||||
this.isNextEdgeFromPolygon = other.isNextEdgeFromPolygon;
|
||||
}
|
||||
|
||||
/** get the x value */
|
||||
|
@ -979,9 +1114,11 @@ final public class Tessellator {
|
|||
/** Triangle in the tessellated mesh */
|
||||
public final static class Triangle {
|
||||
Node[] vertex;
|
||||
boolean[] edgeFromPolygon;
|
||||
|
||||
protected Triangle(Node a, Node b, Node c) {
|
||||
protected Triangle(Node a, boolean isABfromPolygon, Node b, boolean isBCfromPolygon, Node c, boolean isCAfromPolygon) {
|
||||
this.vertex = new Node[] {a, b, c};
|
||||
this.edgeFromPolygon = new boolean[] {isABfromPolygon, isBCfromPolygon, isCAfromPolygon};
|
||||
}
|
||||
|
||||
/** get quantized x value for the given vertex */
|
||||
|
@ -1004,6 +1141,11 @@ final public class Tessellator {
|
|||
return this.vertex[vertex].getX();
|
||||
}
|
||||
|
||||
/** get if edge is shared with the polygon for the given edge */
|
||||
public boolean isEdgefromPolygon(int startVertex) {
|
||||
return edgeFromPolygon[startVertex];
|
||||
}
|
||||
|
||||
/** utility method to compute whether the point is in the triangle */
|
||||
protected boolean containsPoint(double lat, double lon) {
|
||||
return pointInTriangle(lon, lat,
|
||||
|
@ -1014,9 +1156,9 @@ final public class Tessellator {
|
|||
|
||||
/** pretty print the triangle vertices */
|
||||
public String toString() {
|
||||
String result = vertex[0].x + ", " + vertex[0].y + " " +
|
||||
vertex[1].x + ", " + vertex[1].y + " " +
|
||||
vertex[2].x + ", " + vertex[2].y;
|
||||
String result = vertex[0].x + ", " + vertex[0].y + " [" + edgeFromPolygon[0] + "] " +
|
||||
vertex[1].x + ", " + vertex[1].y + " [" + edgeFromPolygon[1] + "] " +
|
||||
vertex[2].x + ", " + vertex[2].y + " [" + edgeFromPolygon[2] + "]";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -264,18 +264,18 @@ public abstract class BaseLatLonShapeTestCase extends BaseShapeTestCase {
|
|||
}
|
||||
|
||||
@Override
|
||||
double[] quantizeTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
|
||||
int[] decoded = encodeDecodeTriangle(ax, ay, bx, by, cx, cy);
|
||||
return new double[]{decodeLatitude(decoded[0]), decodeLongitude(decoded[1]), decodeLatitude(decoded[2]), decodeLongitude(decoded[3]), decodeLatitude(decoded[4]), decodeLongitude(decoded[5])};
|
||||
double[] quantizeTriangle(double ax, double ay, boolean ab, double bx, double by, boolean bc, double cx, double cy, boolean ca) {
|
||||
ShapeField.DecodedTriangle decoded = encodeDecodeTriangle(ax, ay, ab, bx, by, bc, cx, cy, ca);
|
||||
return new double[]{decodeLatitude(decoded.aY), decodeLongitude(decoded.aX), decodeLatitude(decoded.bY), decodeLongitude(decoded.bX), decodeLatitude(decoded.cY), decodeLongitude(decoded.cX)};
|
||||
}
|
||||
|
||||
@Override
|
||||
int[] encodeDecodeTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
|
||||
ShapeField.DecodedTriangle encodeDecodeTriangle(double ax, double ay, boolean ab, double bx, double by, boolean bc, double cx, double cy, boolean ca) {
|
||||
byte[] encoded = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(encoded, encodeLatitude(ay), encodeLongitude(ax), encodeLatitude(by), encodeLongitude(bx), encodeLatitude(cy), encodeLongitude(cx));
|
||||
int[] decoded = new int[6];
|
||||
ShapeField.decodeTriangle(encoded, decoded);
|
||||
return decoded;
|
||||
ShapeField.encodeTriangle(encoded, encodeLatitude(ay), encodeLongitude(ax), ab, encodeLatitude(by), encodeLongitude(bx), bc, encodeLatitude(cy), encodeLongitude(cx), ca);
|
||||
ShapeField.DecodedTriangle triangle = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(encoded, triangle);
|
||||
return triangle;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
*/
|
||||
package org.apache.lucene.document;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.geo.Polygon2D;
|
||||
import org.apache.lucene.index.PointValues;
|
||||
|
@ -55,15 +53,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cxEnc = encodeX(cx);
|
||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//One shared point with MBR -> MinLat, MaxLon
|
||||
|
@ -82,15 +80,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cxEnc = encodeX(cx);
|
||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//One shared point with MBR -> MaxLat, MaxLon
|
||||
|
@ -109,15 +107,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cxEnc = encodeX(blon);
|
||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//One shared point with MBR -> MaxLat, MinLon
|
||||
|
@ -136,15 +134,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cxEnc = encodeX(blon);
|
||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//Two shared point with MBR -> [MinLat, MinLon], [MaxLat, MaxLon], third point below
|
||||
|
@ -163,15 +161,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cxEnc = encodeX(cx);
|
||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//Two shared point with MBR -> [MinLat, MinLon], [MaxLat, MaxLon], third point above
|
||||
|
@ -190,15 +188,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cxEnc = encodeX(cx);
|
||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//Two shared point with MBR -> [MinLat, MaxLon], [MaxLat, MinLon], third point below
|
||||
|
@ -217,15 +215,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cxEnc = encodeX(cx);
|
||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//Two shared point with MBR -> [MinLat, MaxLon], [MaxLat, MinLon], third point above
|
||||
|
@ -244,15 +242,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cxEnc = encodeX(cx);
|
||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//all points shared with MBR
|
||||
|
@ -271,15 +269,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cxEnc = encodeX(cx);
|
||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//all points shared with MBR
|
||||
|
@ -297,15 +295,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cyEnc = encodeY(cy);
|
||||
int cxEnc = encodeX(cx);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == cyEnc);
|
||||
assertTrue(encoded[5] == cxEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, cyEnc);
|
||||
assertEquals(encoded.cX, cxEnc);
|
||||
}
|
||||
|
||||
//[a,b,c] == [c,a,b] == [b,c,a] == [c,b,a] == [b,a,c] == [a,c,b]
|
||||
|
@ -314,34 +312,34 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
assertTrue(GeoUtils.orient(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc) != 0);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
//[a,b,c]
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encodedABC = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, false);
|
||||
ShapeField.DecodedTriangle encodedABC = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encodedABC);
|
||||
//[c,a,b]
|
||||
ShapeField.encodeTriangle(b, cyEnc, cxEnc, ayEnc, axEnc, byEnc, bxEnc);
|
||||
int[] encodedCAB = new int[6];
|
||||
ShapeField.encodeTriangle(b, cyEnc, cxEnc, false, ayEnc, axEnc, true, byEnc, bxEnc, true);
|
||||
ShapeField.DecodedTriangle encodedCAB = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encodedCAB);
|
||||
assertTrue(Arrays.equals(encodedABC, encodedCAB));
|
||||
assertEquals(encodedABC, encodedCAB);
|
||||
//[b,c,a]
|
||||
ShapeField.encodeTriangle(b, byEnc, bxEnc, cyEnc, cxEnc, ayEnc, axEnc);
|
||||
int[] encodedBCA = new int[6];
|
||||
ShapeField.encodeTriangle(b, byEnc, bxEnc, true, cyEnc, cxEnc, false, ayEnc, axEnc, true);
|
||||
ShapeField.DecodedTriangle encodedBCA = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encodedBCA);
|
||||
assertTrue(Arrays.equals(encodedABC, encodedBCA));
|
||||
assertEquals(encodedABC, encodedBCA);
|
||||
//[c,b,a]
|
||||
ShapeField.encodeTriangle(b, cyEnc, cxEnc, byEnc, bxEnc, ayEnc, axEnc);
|
||||
int[] encodedCBA= new int[6];
|
||||
ShapeField.encodeTriangle(b, cyEnc, cxEnc, true, byEnc, bxEnc, true, ayEnc, axEnc, false);
|
||||
ShapeField.DecodedTriangle encodedCBA= new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encodedCBA);
|
||||
assertTrue(Arrays.equals(encodedABC, encodedCBA));
|
||||
assertEquals(encodedABC, encodedCBA);
|
||||
//[b,a,c]
|
||||
ShapeField.encodeTriangle(b, byEnc, bxEnc, ayEnc, axEnc, cyEnc, cxEnc);
|
||||
int[] encodedBAC= new int[6];
|
||||
ShapeField.encodeTriangle(b, byEnc, bxEnc, true, ayEnc, axEnc, false, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encodedBAC= new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encodedBAC);
|
||||
assertTrue(Arrays.equals(encodedABC, encodedBAC));
|
||||
assertEquals(encodedABC, encodedBAC);
|
||||
//[a,c,b]
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, cyEnc, cxEnc, byEnc, bxEnc);
|
||||
int[] encodedACB= new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, false, cyEnc, cxEnc, true, byEnc, bxEnc, true);
|
||||
ShapeField.DecodedTriangle encodedACB= new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encodedACB);
|
||||
assertTrue(Arrays.equals(encodedABC, encodedACB));
|
||||
assertEquals(encodedABC, encodedACB);
|
||||
}
|
||||
|
||||
public void testPointEncoding() {
|
||||
|
@ -350,11 +348,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int latEnc = encodeY(lat);
|
||||
int lonEnc = encodeX(lon);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, latEnc, lonEnc, latEnc, lonEnc, latEnc, lonEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, latEnc, lonEnc, true, latEnc, lonEnc, true, latEnc, lonEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == latEnc && encoded[2] == latEnc && encoded[4] == latEnc);
|
||||
assertTrue(encoded[1] == lonEnc && encoded[3] == lonEnc && encoded[5] == lonEnc);
|
||||
assertEquals(encoded.aY, latEnc);
|
||||
assertEquals(encoded.aX, lonEnc);
|
||||
assertEquals(encoded.bY, latEnc);
|
||||
assertEquals(encoded.bX, lonEnc);
|
||||
assertEquals(encoded.cY, latEnc);
|
||||
assertEquals(encoded.cX, lonEnc);
|
||||
}
|
||||
|
||||
public void testLineEncodingSameLat() {
|
||||
|
@ -365,33 +367,31 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int axEnc = encodeX(ax);
|
||||
int bxEnc = encodeX(bx);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, latEnc, axEnc, latEnc, bxEnc, latEnc, axEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, latEnc, axEnc, true, latEnc, bxEnc, true, latEnc, axEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == latEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == latEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == latEnc);
|
||||
assertTrue(encoded[5] == axEnc);
|
||||
ShapeField.encodeTriangle(b, latEnc, axEnc, latEnc, axEnc, latEnc, bxEnc);
|
||||
encoded = new int[6];
|
||||
assertEquals(encoded.aY, latEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, latEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, latEnc);
|
||||
assertEquals(encoded.cX, axEnc);
|
||||
ShapeField.encodeTriangle(b, latEnc, axEnc, true, latEnc, axEnc, true, latEnc, bxEnc, true);
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == latEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == latEnc);
|
||||
assertTrue(encoded[3] == axEnc);
|
||||
assertTrue(encoded[4] == latEnc);
|
||||
assertTrue(encoded[5] == bxEnc);
|
||||
ShapeField.encodeTriangle(b, latEnc, bxEnc, latEnc, axEnc, latEnc, axEnc);
|
||||
encoded = new int[6];
|
||||
assertEquals(encoded.aY, latEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, latEnc);
|
||||
assertEquals(encoded.bX, axEnc);
|
||||
assertEquals(encoded.cY, latEnc);
|
||||
assertEquals(encoded.cX, bxEnc);
|
||||
ShapeField.encodeTriangle(b, latEnc, bxEnc, true, latEnc, axEnc, true, latEnc, axEnc, true);
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == latEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == latEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == latEnc);
|
||||
assertTrue(encoded[5] == axEnc);
|
||||
assertEquals(encoded.aY, latEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, latEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, latEnc);
|
||||
assertEquals(encoded.cX, axEnc);
|
||||
}
|
||||
|
||||
public void testLineEncodingSameLon() {
|
||||
|
@ -402,33 +402,31 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int byEnc = encodeY(by);
|
||||
int lonEnc = encodeX(lon);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, lonEnc, byEnc, lonEnc, ayEnc, lonEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, lonEnc, true, byEnc, lonEnc, true, ayEnc, lonEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == lonEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == lonEnc);
|
||||
assertTrue(encoded[4] == ayEnc);
|
||||
assertTrue(encoded[5] == lonEnc);
|
||||
ShapeField.encodeTriangle(b, ayEnc, lonEnc, ayEnc, lonEnc, byEnc, lonEnc);
|
||||
encoded = new int[6];
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, lonEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, lonEnc);
|
||||
assertEquals(encoded.cY, ayEnc);
|
||||
assertEquals(encoded.cX, lonEnc);
|
||||
ShapeField.encodeTriangle(b, ayEnc, lonEnc, true, ayEnc, lonEnc, true, byEnc, lonEnc, true);
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == lonEnc);
|
||||
assertTrue(encoded[2] == ayEnc);
|
||||
assertTrue(encoded[3] == lonEnc);
|
||||
assertTrue(encoded[4] == byEnc);
|
||||
assertTrue(encoded[5] == lonEnc);
|
||||
ShapeField.encodeTriangle(b, byEnc, lonEnc, ayEnc, lonEnc, ayEnc, lonEnc);
|
||||
encoded = new int[6];
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, lonEnc);
|
||||
assertEquals(encoded.bY, ayEnc);
|
||||
assertEquals(encoded.bX, lonEnc);
|
||||
assertEquals(encoded.cY, byEnc);
|
||||
assertEquals(encoded.cX, lonEnc);
|
||||
ShapeField.encodeTriangle(b, byEnc, lonEnc, true, ayEnc, lonEnc, true, ayEnc, lonEnc, true);
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == lonEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == lonEnc);
|
||||
assertTrue(encoded[4] == ayEnc);
|
||||
assertTrue(encoded[5] == lonEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, lonEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, lonEnc);
|
||||
assertEquals(encoded.cY, ayEnc);
|
||||
assertEquals(encoded.cX, lonEnc);
|
||||
}
|
||||
|
||||
public void testLineEncoding() {
|
||||
|
@ -441,33 +439,31 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int axEnc = encodeX(ax);
|
||||
int bxEnc = encodeX(bx);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, ayEnc, axEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, ayEnc, axEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == ayEnc);
|
||||
assertTrue(encoded[5] == axEnc);
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, ayEnc, axEnc, byEnc, bxEnc);
|
||||
encoded = new int[6];
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, ayEnc);
|
||||
assertEquals(encoded.cX, axEnc);
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, ayEnc, axEnc, true, byEnc, bxEnc, true);
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == ayEnc);
|
||||
assertTrue(encoded[3] == axEnc);
|
||||
assertTrue(encoded[4] == byEnc);
|
||||
assertTrue(encoded[5] == bxEnc);
|
||||
ShapeField.encodeTriangle(b, byEnc, bxEnc, ayEnc, axEnc, ayEnc, axEnc);
|
||||
encoded = new int[6];
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, ayEnc);
|
||||
assertEquals(encoded.bX, axEnc);
|
||||
assertEquals(encoded.cY, byEnc);
|
||||
assertEquals(encoded.cX, bxEnc);
|
||||
ShapeField.encodeTriangle(b, byEnc, bxEnc, true, ayEnc, axEnc, true, ayEnc, axEnc, true);
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == ayEnc);
|
||||
assertTrue(encoded[1] == axEnc);
|
||||
assertTrue(encoded[2] == byEnc);
|
||||
assertTrue(encoded[3] == bxEnc);
|
||||
assertTrue(encoded[4] == ayEnc);
|
||||
assertTrue(encoded[5] == axEnc);
|
||||
assertEquals(encoded.aY, ayEnc);
|
||||
assertEquals(encoded.aX, axEnc);
|
||||
assertEquals(encoded.bY, byEnc);
|
||||
assertEquals(encoded.bX, bxEnc);
|
||||
assertEquals(encoded.cY, ayEnc);
|
||||
assertEquals(encoded.cX, axEnc);
|
||||
}
|
||||
|
||||
public void testRandomPointEncoding() {
|
||||
|
@ -505,16 +501,16 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
|
||||
//quantize the triangle
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, original[0], original[1], original[2], original[3], original[4], original[5]);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, original[0], original[1], true, original[2], original[3], true, original[4], original[5], true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
double[] encodedQuantize = new double[] {
|
||||
decodeY(encoded[0]),
|
||||
decodeX(encoded[1]),
|
||||
decodeY(encoded[2]),
|
||||
decodeX(encoded[3]),
|
||||
decodeY(encoded[4]),
|
||||
decodeX(encoded[5])};
|
||||
decodeY(encoded.aY),
|
||||
decodeX(encoded.aX),
|
||||
decodeY(encoded.bY),
|
||||
decodeX(encoded.bX),
|
||||
decodeY(encoded.cY),
|
||||
decodeX(encoded.cX)};
|
||||
|
||||
int orientation = GeoUtils.orient(original[1], original[0], original[3], original[2], original[5], original[4]);
|
||||
//quantize original
|
||||
|
@ -560,14 +556,14 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
|||
int cyEnc = encodeY(cy);
|
||||
int cxEnc = encodeX(cx);
|
||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||
int[] encoded = new int[6];
|
||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(b, encoded);
|
||||
assertTrue(encoded[0] == byEnc);
|
||||
assertTrue(encoded[1] == bxEnc);
|
||||
assertTrue(encoded[2] == cyEnc);
|
||||
assertTrue(encoded[3] == cxEnc);
|
||||
assertTrue(encoded[4] == ayEnc);
|
||||
assertTrue(encoded[5] == axEnc);
|
||||
assertTrue(encoded.aY == byEnc);
|
||||
assertTrue(encoded.aX == bxEnc);
|
||||
assertTrue(encoded.bY == cyEnc);
|
||||
assertTrue(encoded.bX == cxEnc);
|
||||
assertTrue(encoded.cY == ayEnc);
|
||||
assertTrue(encoded.cX == axEnc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -545,8 +545,8 @@ public abstract class BaseShapeTestCase extends LuceneTestCase {
|
|||
abstract double quantizeXCeil(double raw);
|
||||
abstract double quantizeY(double raw);
|
||||
abstract double quantizeYCeil(double raw);
|
||||
abstract double[] quantizeTriangle(double ax, double ay, double bx, double by, double cx, double cy);
|
||||
abstract int[] encodeDecodeTriangle(double ax, double ay, double bx, double by, double cx, double cy);
|
||||
abstract double[] quantizeTriangle(double ax, double ay, boolean ab, double bx, double by, boolean bc, double cx, double cy, boolean ca);
|
||||
abstract ShapeField.DecodedTriangle encodeDecodeTriangle(double ax, double ay, boolean ab, double bx, double by, boolean bc, double cx, double cy, boolean ca);
|
||||
}
|
||||
|
||||
private int scaledIterationCount(int shapes) {
|
||||
|
|
|
@ -144,18 +144,18 @@ public abstract class BaseXYShapeTestCase extends BaseShapeTestCase {
|
|||
}
|
||||
|
||||
@Override
|
||||
double[] quantizeTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
|
||||
int[] decoded = encodeDecodeTriangle(ax, ay, bx, by, cx, cy);
|
||||
return new double[]{decode(decoded[0]), decode(decoded[1]), decode(decoded[2]), decode(decoded[3]), decode(decoded[4]), decode(decoded[5])};
|
||||
double[] quantizeTriangle(double ax, double ay, boolean ab, double bx, double by, boolean bc, double cx, double cy, boolean ca) {
|
||||
ShapeField.DecodedTriangle decoded = encodeDecodeTriangle(ax, ay, ab, bx, by, bc, cx, cy, ca);
|
||||
return new double[]{decode(decoded.aY), decode(decoded.aX), decode(decoded.bY), decode(decoded.bX), decode(decoded.cY), decode(decoded.cX)};
|
||||
}
|
||||
|
||||
@Override
|
||||
int[] encodeDecodeTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
|
||||
ShapeField.DecodedTriangle encodeDecodeTriangle(double ax, double ay, boolean ab, double bx, double by, boolean bc, double cx, double cy, boolean ca) {
|
||||
byte[] encoded = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(encoded, encode(ay), encode(ax), encode(by), encode(bx), encode(cy), encode(cx));
|
||||
int[] decoded = new int[6];
|
||||
ShapeField.decodeTriangle(encoded, decoded);
|
||||
return decoded;
|
||||
ShapeField.encodeTriangle(encoded, encode(ay), encode(ax), ab, encode(by), encode(bx), bc, encode(cy), encode(cx), ca);
|
||||
ShapeField.DecodedTriangle triangle = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(encoded, triangle);
|
||||
return triangle;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -83,13 +83,13 @@ public class TestLatLonLineShapeQueries extends BaseLatLonShapeTestCase {
|
|||
Line line = (Line)shape;
|
||||
Rectangle2D rectangle2D = Rectangle2D.create(new Rectangle(minLat, maxLat, minLon, maxLon));
|
||||
for (int i = 0, j = 1; j < line.numPoints(); ++i, ++j) {
|
||||
int[] decoded = encoder.encodeDecodeTriangle(line.getLon(i), line.getLat(i), line.getLon(j), line.getLat(j), line.getLon(i), line.getLat(i));
|
||||
ShapeField.DecodedTriangle decoded = encoder.encodeDecodeTriangle(line.getLon(i), line.getLat(i), true, line.getLon(j), line.getLat(j), true, line.getLon(i), line.getLat(i), true);
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
if (rectangle2D.containsTriangle(decoded[1], decoded[0], decoded[3], decoded[2], decoded[5], decoded[4]) == false) {
|
||||
if (rectangle2D.containsTriangle(decoded.aX, decoded.aY, decoded.bX, decoded.bY, decoded.cX, decoded.cY) == false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (rectangle2D.intersectsTriangle(decoded[1], decoded[0], decoded[3], decoded[2], decoded[5], decoded[4]) == true) {
|
||||
if (rectangle2D.intersectsTriangle(decoded.aX, decoded.aY, decoded.bX, decoded.bY, decoded.cX, decoded.cY) == true) {
|
||||
return queryRelation == QueryRelation.INTERSECTS;
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ public class TestLatLonLineShapeQueries extends BaseLatLonShapeTestCase {
|
|||
private boolean testLine(EdgeTree queryPoly, Line line) {
|
||||
|
||||
for (int i = 0, j = 1; j < line.numPoints(); ++i, ++j) {
|
||||
double[] qTriangle = encoder.quantizeTriangle(line.getLon(i), line.getLat(i), line.getLon(j), line.getLat(j), line.getLon(i), line.getLat(i));
|
||||
double[] qTriangle = encoder.quantizeTriangle(line.getLon(i), line.getLat(i), true, line.getLon(j), line.getLat(j), true, line.getLon(i), line.getLat(i), true);
|
||||
Relation r = queryPoly.relateTriangle(qTriangle[1], qTriangle[0], qTriangle[3], qTriangle[2], qTriangle[5], qTriangle[4]);
|
||||
if (queryRelation == QueryRelation.DISJOINT) {
|
||||
if (r != Relation.CELL_OUTSIDE_QUERY) return false;
|
||||
|
|
|
@ -72,13 +72,15 @@ public class TestLatLonPolygonShapeQueries extends BaseLatLonShapeTestCase {
|
|||
Rectangle2D rectangle2D = Rectangle2D.create(new Rectangle(minLat, maxLat, minLon, maxLon));
|
||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(p);
|
||||
for (Tessellator.Triangle t : tessellation) {
|
||||
int[] decoded = encoder.encodeDecodeTriangle(t.getX(0), t.getY(0), t.getX(1), t.getY(1), t.getX(2), t.getY(2));
|
||||
ShapeField.DecodedTriangle decoded = encoder.encodeDecodeTriangle(t.getX(0), t.getY(0), t.isEdgefromPolygon(0),
|
||||
t.getX(1), t.getY(1), t.isEdgefromPolygon(1),
|
||||
t.getX(2), t.getY(2), t.isEdgefromPolygon(2));
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
if (rectangle2D.containsTriangle(decoded[1], decoded[0], decoded[3], decoded[2], decoded[5], decoded[4]) == false) {
|
||||
if (rectangle2D.containsTriangle(decoded.aX, decoded.aY, decoded.bX, decoded.bY, decoded.cX, decoded.cY) == false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (rectangle2D.intersectsTriangle(decoded[1], decoded[0], decoded[3], decoded[2], decoded[5], decoded[4]) == true) {
|
||||
if (rectangle2D.intersectsTriangle(decoded.aX, decoded.aY, decoded.bX, decoded.bY, decoded.cX, decoded.cY) == true) {
|
||||
return queryRelation == QueryRelation.INTERSECTS;
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +101,9 @@ public class TestLatLonPolygonShapeQueries extends BaseLatLonShapeTestCase {
|
|||
private boolean testPolygon(EdgeTree tree, Polygon shape) {
|
||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(shape);
|
||||
for (Tessellator.Triangle t : tessellation) {
|
||||
double[] qTriangle = encoder.quantizeTriangle(t.getX(0), t.getY(0), t.getX(1), t.getY(1), t.getX(2), t.getY(2));
|
||||
double[] qTriangle = encoder.quantizeTriangle(t.getX(0), t.getY(0), t.isEdgefromPolygon(0),
|
||||
t.getX(1), t.getY(1), t.isEdgefromPolygon(1),
|
||||
t.getX(2), t.getY(2), t.isEdgefromPolygon(2));
|
||||
Relation r = tree.relateTriangle(qTriangle[1], qTriangle[0], qTriangle[3], qTriangle[2], qTriangle[5], qTriangle[4]);
|
||||
if (queryRelation == QueryRelation.DISJOINT) {
|
||||
if (r != Relation.CELL_OUTSIDE_QUERY) return false;
|
||||
|
|
|
@ -236,12 +236,13 @@ public class TestLatLonShape extends LuceneTestCase {
|
|||
Tessellator.Triangle t = Tessellator.tessellate(poly).get(0);
|
||||
|
||||
byte[] encoded = new byte[7 * ShapeField.BYTES];
|
||||
ShapeField.encodeTriangle(encoded, encodeLatitude(t.getY(0)), encodeLongitude(t.getX(0)),
|
||||
encodeLatitude(t.getY(1)), encodeLongitude(t.getX(1)), encodeLatitude(t.getY(2)), encodeLongitude(t.getX(2)));
|
||||
int[] decoded = new int[6];
|
||||
ShapeField.encodeTriangle(encoded, encodeLatitude(t.getY(0)), encodeLongitude(t.getX(0)), t.isEdgefromPolygon(0),
|
||||
encodeLatitude(t.getY(1)), encodeLongitude(t.getX(1)), t.isEdgefromPolygon(1),
|
||||
encodeLatitude(t.getY(2)), encodeLongitude(t.getX(2)), t.isEdgefromPolygon(2));
|
||||
ShapeField.DecodedTriangle decoded = new ShapeField.DecodedTriangle();
|
||||
ShapeField.decodeTriangle(encoded, decoded);
|
||||
|
||||
int expected =rectangle2D.intersectsTriangle(decoded[1], decoded[0], decoded[3], decoded[2], decoded[5], decoded[4]) ? 0 : 1;
|
||||
int expected =rectangle2D.intersectsTriangle(decoded.aX, decoded.aY, decoded.bX, decoded.bY, decoded.cX, decoded.cY) ? 0 : 1;
|
||||
|
||||
Document document = new Document();
|
||||
addPolygonsToDoc(FIELDNAME, document, poly);
|
||||
|
|
|
@ -81,13 +81,13 @@ public class TestXYLineShapeQueries extends BaseXYShapeTestCase {
|
|||
XYLine line = (XYLine)shape;
|
||||
XYRectangle2D rectangle2D = XYRectangle2D.create(new XYRectangle(minX, maxX, minY, maxY));
|
||||
for (int i = 0, j = 1; j < line.numPoints(); ++i, ++j) {
|
||||
int[] decoded = encoder.encodeDecodeTriangle(line.getX(i), line.getY(i), line.getX(j), line.getY(j), line.getX(i), line.getY(i));
|
||||
ShapeField.DecodedTriangle decoded = encoder.encodeDecodeTriangle(line.getX(i), line.getY(i), true, line.getX(j), line.getY(j), true, line.getX(i), line.getY(i), true);
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
if (rectangle2D.containsTriangle(decoded[1], decoded[0], decoded[3], decoded[2], decoded[5], decoded[4]) == false) {
|
||||
if (rectangle2D.containsTriangle(decoded.aX, decoded.aY, decoded.bX, decoded.bY, decoded.cX, decoded.cY) == false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (rectangle2D.intersectsTriangle(decoded[1], decoded[0], decoded[3], decoded[2], decoded[5], decoded[4]) == true) {
|
||||
if (rectangle2D.intersectsTriangle(decoded.aX, decoded.aY, decoded.bX, decoded.bY, decoded.cX, decoded.cY) == true) {
|
||||
return queryRelation == QueryRelation.INTERSECTS;
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ public class TestXYLineShapeQueries extends BaseXYShapeTestCase {
|
|||
private boolean testLine(EdgeTree queryPoly, XYLine line) {
|
||||
|
||||
for (int i = 0, j = 1; j < line.numPoints(); ++i, ++j) {
|
||||
double[] qTriangle = encoder.quantizeTriangle(line.getX(i), line.getY(i), line.getX(j), line.getY(j), line.getX(i), line.getY(i));
|
||||
double[] qTriangle = encoder.quantizeTriangle(line.getX(i), line.getY(i), true, line.getX(j), line.getY(j), true, line.getX(i), line.getY(i), true);
|
||||
Relation r = queryPoly.relateTriangle(qTriangle[1], qTriangle[0], qTriangle[3], qTriangle[2], qTriangle[5], qTriangle[4]);
|
||||
if (queryRelation == QueryRelation.DISJOINT) {
|
||||
if (r != Relation.CELL_OUTSIDE_QUERY) return false;
|
||||
|
|
|
@ -72,13 +72,15 @@ public class TestXYPolygonShapeQueries extends BaseXYShapeTestCase {
|
|||
XYRectangle2D rectangle2D = XYRectangle2D.create(new XYRectangle(minX, maxX, minY, maxY));
|
||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(p);
|
||||
for (Tessellator.Triangle t : tessellation) {
|
||||
int[] decoded = encoder.encodeDecodeTriangle(t.getX(0), t.getY(0), t.getX(1), t.getY(1), t.getX(2), t.getY(2));
|
||||
ShapeField.DecodedTriangle decoded = encoder.encodeDecodeTriangle(t.getX(0), t.getY(0), t.isEdgefromPolygon(0),
|
||||
t.getX(1), t.getY(1), t.isEdgefromPolygon(1),
|
||||
t.getX(2), t.getY(2), t.isEdgefromPolygon(2));
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
if (rectangle2D.containsTriangle(decoded[1], decoded[0], decoded[3], decoded[2], decoded[5], decoded[4]) == false) {
|
||||
if (rectangle2D.containsTriangle(decoded.aX, decoded.aY, decoded.bX, decoded.bY, decoded.cX, decoded.cY) == false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (rectangle2D.intersectsTriangle(decoded[1], decoded[0], decoded[3], decoded[2], decoded[5], decoded[4]) == true) {
|
||||
if (rectangle2D.intersectsTriangle(decoded.aX, decoded.aY, decoded.bX, decoded.bY, decoded.cX, decoded.cY) == true) {
|
||||
return queryRelation == QueryRelation.INTERSECTS;
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +101,9 @@ public class TestXYPolygonShapeQueries extends BaseXYShapeTestCase {
|
|||
private boolean testPolygon(EdgeTree tree, XYPolygon shape) {
|
||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(shape);
|
||||
for (Tessellator.Triangle t : tessellation) {
|
||||
double[] qTriangle = encoder.quantizeTriangle(t.getX(0), t.getY(0), t.getX(1), t.getY(1), t.getX(2), t.getY(2));
|
||||
double[] qTriangle = encoder.quantizeTriangle(t.getX(0), t.getY(0), t.isEdgefromPolygon(0),
|
||||
t.getX(1), t.getY(1), t.isEdgefromPolygon(1),
|
||||
t.getX(2), t.getY(2), t.isEdgefromPolygon(2));
|
||||
Relation r = tree.relateTriangle(qTriangle[1], qTriangle[0], qTriangle[3], qTriangle[2], qTriangle[5], qTriangle[4]);
|
||||
if (queryRelation == QueryRelation.DISJOINT) {
|
||||
if (r != Relation.CELL_OUTSIDE_QUERY) return false;
|
||||
|
|
|
@ -554,6 +554,9 @@ public class TestTessellator extends LuceneTestCase {
|
|||
Polygon polygon = (Polygon) SimpleWKTShapeParser.parse(wkt);
|
||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(polygon);
|
||||
assertEquals(area(polygon), area(tessellation), 0.0);
|
||||
for (Tessellator.Triangle t : tessellation) {
|
||||
checkTriangleEdgesFromPolygon(polygon, t);
|
||||
}
|
||||
}
|
||||
|
||||
private double area(Polygon p) {
|
||||
|
@ -578,4 +581,77 @@ public class TestTessellator extends LuceneTestCase {
|
|||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
private void checkTriangleEdgesFromPolygon(Polygon p, Tessellator.Triangle t) {
|
||||
// first edge
|
||||
assertEquals(t.isEdgefromPolygon(0), isEdgeFromPolygon(p, t.getX(0), t.getY(0), t.getX(1), t.getY(1)));
|
||||
// second edge
|
||||
assertEquals(t.isEdgefromPolygon(1), isEdgeFromPolygon(p, t.getX(1), t.getY(1), t.getX(2), t.getY(2)));
|
||||
// third edge
|
||||
assertEquals(t.isEdgefromPolygon(2), isEdgeFromPolygon(p, t.getX(2), t.getY(2), t.getX(0), t.getY(0)));
|
||||
}
|
||||
|
||||
private boolean isEdgeFromPolygon(Polygon p, double aLon, double aLat, double bLon, double bLat) {
|
||||
for (int i = 0; i < p.getPolyLats().length - 1; i++) {
|
||||
if (isPointInLine(p.getPolyLon(i), p.getPolyLat(i), p.getPolyLon(i + 1), p.getPolyLat(i + 1), aLon, aLat) &&
|
||||
isPointInLine(p.getPolyLon(i), p.getPolyLat(i), p.getPolyLon(i + 1), p.getPolyLat(i + 1), bLon, bLat)) {
|
||||
return true;
|
||||
}
|
||||
if (p.getPolyLon(i) != p.getPolyLon(i + 1) || p.getPolyLat(i) != p.getPolyLat(i + 1)) {
|
||||
//Check for co-planar points
|
||||
final int length = p.getPolyLats().length;
|
||||
final int offset = i + 2;
|
||||
int j = 0;
|
||||
int index = getIndex(length, j + offset);
|
||||
while (j < length && area(p.getPolyLon(i), p.getPolyLat(i), p.getPolyLon(i + 1), p.getPolyLat(i + 1), p.getPolyLon(index), p.getPolyLat(index)) == 0) {
|
||||
if (isPointInLine(p.getPolyLon(i), p.getPolyLat(i), p.getPolyLon(index), p.getPolyLat(index), aLon, aLat) &&
|
||||
isPointInLine(p.getPolyLon(i), p.getPolyLat(i), p.getPolyLon(index), p.getPolyLat(index), bLon, bLat)) {
|
||||
return true;
|
||||
}
|
||||
index = getIndex(length, ++j + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p.getHoles() != null && p.getHoles().length > 0) {
|
||||
for (Polygon hole : p.getHoles()) {
|
||||
if (isEdgeFromPolygon(hole, aLon, aLat, bLon, bLat)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getIndex(int size, int index) {
|
||||
if (index < size) {
|
||||
return index;
|
||||
}
|
||||
return index - size;
|
||||
}
|
||||
|
||||
/** Compute signed area of triangle */
|
||||
private double area(final double aX, final double aY, final double bX, final double bY,
|
||||
final double cX, final double cY) {
|
||||
return (bY - aY) * (cX - bX) - (bX - aX) * (cY - bY);
|
||||
}
|
||||
|
||||
private boolean isPointInLine(final double aX, final double aY, final double bX, final double bY, double lon, double lat) {
|
||||
double dxc = lon - aX;
|
||||
double dyc = lat - aY;
|
||||
|
||||
double dxl = bX - aX;
|
||||
double dyl = bY - aY;
|
||||
|
||||
if (dxc * dyl - dyc * dxl == 0) {
|
||||
if (Math.abs(dxl) >= Math.abs(dyl))
|
||||
return dxl > 0 ?
|
||||
aX <= lon && lon <= bX :
|
||||
bX <= lon && lon <= aX;
|
||||
else
|
||||
return dyl > 0 ?
|
||||
aY <= lat && lat <= bY :
|
||||
bY <= lat && lat <= aY;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue