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-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
|
Optimizations
|
||||||
|
|
||||||
* LUCENE-8922: DisjunctionMaxQuery more efficiently leverages impacts to skip
|
* 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 */
|
/** returns true if the query matches the encoded triangle */
|
||||||
@Override
|
@Override
|
||||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||||
// decode indexed triangle
|
// decode indexed triangle
|
||||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||||
|
|
||||||
int aY = scratchTriangle[0];
|
int aY = scratchTriangle.aY;
|
||||||
int aX = scratchTriangle[1];
|
int aX = scratchTriangle.aX;
|
||||||
int bY = scratchTriangle[2];
|
int bY = scratchTriangle.bY;
|
||||||
int bX = scratchTriangle[3];
|
int bX = scratchTriangle.bX;
|
||||||
int cY = scratchTriangle[4];
|
int cY = scratchTriangle.cY;
|
||||||
int cX = scratchTriangle[5];
|
int cX = scratchTriangle.cX;
|
||||||
|
|
||||||
if (queryRelation == QueryRelation.WITHIN) {
|
if (queryRelation == QueryRelation.WITHIN) {
|
||||||
return rectangle2D.containsTriangle(aX, aY, bX, bY, cX, cY);
|
return rectangle2D.containsTriangle(aX, aY, bX, bY, cX, cY);
|
||||||
|
|
|
@ -84,15 +84,15 @@ final class LatLonShapeLineQuery extends ShapeQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||||
|
|
||||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle[0]);
|
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle.aY);
|
||||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle[1]);
|
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle.aX);
|
||||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle[2]);
|
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle.bY);
|
||||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle[3]);
|
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle.bX);
|
||||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle[4]);
|
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle.cY);
|
||||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle[5]);
|
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||||
|
|
||||||
if (queryRelation == QueryRelation.WITHIN) {
|
if (queryRelation == QueryRelation.WITHIN) {
|
||||||
return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||||
|
|
|
@ -78,15 +78,15 @@ final class LatLonShapePolygonQuery extends ShapeQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||||
|
|
||||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle[0]);
|
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle.aY);
|
||||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle[1]);
|
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle.aX);
|
||||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle[2]);
|
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle.bY);
|
||||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle[3]);
|
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle.bX);
|
||||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle[4]);
|
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle.cY);
|
||||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle[5]);
|
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||||
|
|
||||||
if (queryRelation == QueryRelation.WITHIN) {
|
if (queryRelation == QueryRelation.WITHIN) {
|
||||||
return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.lucene.document;
|
package org.apache.lucene.document;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.apache.lucene.geo.GeoUtils;
|
import org.apache.lucene.geo.GeoUtils;
|
||||||
import org.apache.lucene.geo.Line;
|
import org.apache.lucene.geo.Line;
|
||||||
import org.apache.lucene.geo.Polygon;
|
import org.apache.lucene.geo.Polygon;
|
||||||
|
@ -57,18 +59,22 @@ public final class ShapeField {
|
||||||
*/
|
*/
|
||||||
public static class Triangle extends Field {
|
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) {
|
Triangle(String name, int aXencoded, int aYencoded, int bXencoded, int bYencoded, int cXencoded, int cYencoded) {
|
||||||
super(name, TYPE);
|
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) {
|
Triangle(String name, Tessellator.Triangle t) {
|
||||||
super(name, TYPE);
|
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 */
|
/** 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;
|
final byte[] bytes;
|
||||||
|
|
||||||
if (fieldsData == null) {
|
if (fieldsData == null) {
|
||||||
|
@ -77,7 +83,7 @@ public final class ShapeField {
|
||||||
} else {
|
} else {
|
||||||
bytes = ((BytesRef) fieldsData).bytes;
|
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.
|
* 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.
|
* 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;
|
assert bytes.length == 7 * BYTES;
|
||||||
int aX;
|
int aX;
|
||||||
int bX;
|
int bX;
|
||||||
|
@ -108,6 +114,7 @@ public final class ShapeField {
|
||||||
int aY;
|
int aY;
|
||||||
int bY;
|
int bY;
|
||||||
int cY;
|
int cY;
|
||||||
|
boolean ab, bc, ca;
|
||||||
//change orientation if CW
|
//change orientation if CW
|
||||||
if (GeoUtils.orient(aLon, aLat, bLon, bLat, cLon, cLat) == -1) {
|
if (GeoUtils.orient(aLon, aLat, bLon, bLat, cLon, cLat) == -1) {
|
||||||
aX = cLon;
|
aX = cLon;
|
||||||
|
@ -116,6 +123,9 @@ public final class ShapeField {
|
||||||
aY = cLat;
|
aY = cLat;
|
||||||
bY = bLat;
|
bY = bLat;
|
||||||
cY = aLat;
|
cY = aLat;
|
||||||
|
ab = bcFromShape;
|
||||||
|
bc = abFromShape;
|
||||||
|
ca = caFromShape;
|
||||||
} else {
|
} else {
|
||||||
aX = aLon;
|
aX = aLon;
|
||||||
bX = bLon;
|
bX = bLon;
|
||||||
|
@ -123,27 +133,38 @@ public final class ShapeField {
|
||||||
aY = aLat;
|
aY = aLat;
|
||||||
bY = bLat;
|
bY = bLat;
|
||||||
cY = cLat;
|
cY = cLat;
|
||||||
|
ab = abFromShape;
|
||||||
|
bc = bcFromShape;
|
||||||
|
ca = caFromShape;
|
||||||
}
|
}
|
||||||
//rotate edges and place minX at the beginning
|
//rotate edges and place minX at the beginning
|
||||||
if (bX < aX || cX < aX) {
|
if (bX < aX || cX < aX) {
|
||||||
if (bX < cX) {
|
if (bX < cX) {
|
||||||
int tempX = aX;
|
int tempX = aX;
|
||||||
int tempY = aY;
|
int tempY = aY;
|
||||||
|
boolean tempBool = ab;
|
||||||
aX = bX;
|
aX = bX;
|
||||||
aY = bY;
|
aY = bY;
|
||||||
|
ab = bc;
|
||||||
bX = cX;
|
bX = cX;
|
||||||
bY = cY;
|
bY = cY;
|
||||||
|
bc = ca;
|
||||||
cX = tempX;
|
cX = tempX;
|
||||||
cY = tempY;
|
cY = tempY;
|
||||||
|
ca = tempBool;
|
||||||
} else if (cX < aX) {
|
} else if (cX < aX) {
|
||||||
int tempX = aX;
|
int tempX = aX;
|
||||||
int tempY = aY;
|
int tempY = aY;
|
||||||
|
boolean tempBool = ab;
|
||||||
aX = cX;
|
aX = cX;
|
||||||
aY = cY;
|
aY = cY;
|
||||||
|
ab = ca;
|
||||||
cX = bX;
|
cX = bX;
|
||||||
cY = bY;
|
cY = bY;
|
||||||
|
ca = bc;
|
||||||
bX = tempX;
|
bX = tempX;
|
||||||
bY = tempY;
|
bY = tempY;
|
||||||
|
bc = tempBool;
|
||||||
}
|
}
|
||||||
} else if (aX == bX && aX == cX) {
|
} else if (aX == bX && aX == cX) {
|
||||||
//degenerated case, all points with same longitude
|
//degenerated case, all points with same longitude
|
||||||
|
@ -152,21 +173,29 @@ public final class ShapeField {
|
||||||
if (bY < cY) {
|
if (bY < cY) {
|
||||||
int tempX = aX;
|
int tempX = aX;
|
||||||
int tempY = aY;
|
int tempY = aY;
|
||||||
|
boolean tempBool = ab;
|
||||||
aX = bX;
|
aX = bX;
|
||||||
aY = bY;
|
aY = bY;
|
||||||
|
ab = bc;
|
||||||
bX = cX;
|
bX = cX;
|
||||||
bY = cY;
|
bY = cY;
|
||||||
|
bc = ca;
|
||||||
cX = tempX;
|
cX = tempX;
|
||||||
cY = tempY;
|
cY = tempY;
|
||||||
|
ca = tempBool;
|
||||||
} else if (cY < aY) {
|
} else if (cY < aY) {
|
||||||
int tempX = aX;
|
int tempX = aX;
|
||||||
int tempY = aY;
|
int tempY = aY;
|
||||||
|
boolean tempBool = ab;
|
||||||
aX = cX;
|
aX = cX;
|
||||||
aY = cY;
|
aY = cY;
|
||||||
|
ab = ca;
|
||||||
cX = bX;
|
cX = bX;
|
||||||
cY = bY;
|
cY = bY;
|
||||||
|
ca = bc;
|
||||||
bX = tempX;
|
bX = tempX;
|
||||||
bY = tempY;
|
bY = tempY;
|
||||||
|
bc = tempBool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,6 +245,9 @@ public final class ShapeField {
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Could not encode the provided triangle");
|
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(minY, bytes, 0);
|
||||||
NumericUtils.intToSortableBytes(minX, bytes, BYTES);
|
NumericUtils.intToSortableBytes(minX, bytes, BYTES);
|
||||||
NumericUtils.intToSortableBytes(maxY, bytes, 2 * BYTES);
|
NumericUtils.intToSortableBytes(maxY, bytes, 2 * BYTES);
|
||||||
|
@ -225,83 +257,133 @@ public final class ShapeField {
|
||||||
NumericUtils.intToSortableBytes(bits, bytes, 6 * BYTES);
|
NumericUtils.intToSortableBytes(bits, bytes, 6 * BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Decode a triangle encoded by {@link ShapeField#encodeTriangle(byte[], int, int, boolean, int, int, boolean, int, int, boolean)}.
|
||||||
* Decode a triangle encoded by {@link ShapeField#encodeTriangle(byte[], int, int, int, int, int, int)}.
|
|
||||||
*/
|
*/
|
||||||
public static void decodeTriangle(byte[] t, int[] triangle) {
|
public static void decodeTriangle(byte[] t, DecodedTriangle triangle) {
|
||||||
assert triangle.length == 6;
|
final int aX, aY, bX, bY, cX, cY;
|
||||||
|
final boolean ab, bc, ca;
|
||||||
int bits = NumericUtils.sortableBytesToInt(t, 6 * BYTES);
|
int bits = NumericUtils.sortableBytesToInt(t, 6 * BYTES);
|
||||||
//extract the first three bits
|
//extract the first three bits
|
||||||
int tCode = (((1 << 3) - 1) & (bits >> 0));
|
int tCode = (((1 << 3) - 1) & (bits >> 0));
|
||||||
switch (tCode) {
|
switch (tCode) {
|
||||||
case MINY_MINX_MAXY_MAXX_Y_X:
|
case MINY_MINX_MAXY_MAXX_Y_X:
|
||||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
aY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
bY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
bX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
cY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
cX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||||
break;
|
break;
|
||||||
case MINY_MINX_Y_X_MAXY_MAXX:
|
case MINY_MINX_Y_X_MAXY_MAXX:
|
||||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
aY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
bY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
bX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
cY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
cX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||||
break;
|
break;
|
||||||
case MAXY_MINX_Y_X_MINY_MAXX:
|
case MAXY_MINX_Y_X_MINY_MAXX:
|
||||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
aY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
bY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
bX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
cY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
cX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||||
break;
|
break;
|
||||||
case MAXY_MINX_MINY_MAXX_Y_X:
|
case MAXY_MINX_MINY_MAXX_Y_X:
|
||||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
aY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
bY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
bX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
cY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
cX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||||
break;
|
break;
|
||||||
case Y_MINX_MINY_X_MAXY_MAXX:
|
case Y_MINX_MINY_X_MAXY_MAXX:
|
||||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
aY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
bY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
bX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
cY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
cX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||||
break;
|
break;
|
||||||
case Y_MINX_MINY_MAXX_MAXY_X:
|
case Y_MINX_MINY_MAXX_MAXY_X:
|
||||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
aY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
bY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
bX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
cY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
cX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||||
break;
|
break;
|
||||||
case MAXY_MINX_MINY_X_Y_MAXX:
|
case MAXY_MINX_MINY_X_Y_MAXX:
|
||||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
aY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
bY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
bX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
cY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
cX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||||
break;
|
break;
|
||||||
case MINY_MINX_Y_MAXX_MAXY_X:
|
case MINY_MINX_Y_MAXX_MAXY_X:
|
||||||
triangle[0] = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
aY = NumericUtils.sortableBytesToInt(t, 0 * BYTES);
|
||||||
triangle[1] = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
aX = NumericUtils.sortableBytesToInt(t, 1 * BYTES);
|
||||||
triangle[2] = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
bY = NumericUtils.sortableBytesToInt(t, 4 * BYTES);
|
||||||
triangle[3] = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
bX = NumericUtils.sortableBytesToInt(t, 3 * BYTES);
|
||||||
triangle[4] = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
cY = NumericUtils.sortableBytesToInt(t, 2 * BYTES);
|
||||||
triangle[5] = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
cX = NumericUtils.sortableBytesToInt(t, 5 * BYTES);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Could not decode the provided triangle");
|
throw new IllegalArgumentException("Could not decode the provided triangle");
|
||||||
}
|
}
|
||||||
//Points of the decoded triangle must be co-planar or CCW oriented
|
//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);
|
int maxXOffset, int maxYOffset, byte[] maxTriangle);
|
||||||
|
|
||||||
/** returns true if the provided triangle matches the query */
|
/** 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 */
|
/** relates a range of triangles (internal node) to the query */
|
||||||
protected Relation relateRangeToQuery(byte[] minTriangle, byte[] maxTriangle, QueryRelation queryRelation) {
|
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) */
|
/** create a visitor that adds documents that match the query using a sparse bitset. (Used by INTERSECT) */
|
||||||
protected IntersectVisitor getSparseIntersectVisitor(DocIdSetBuilder result) {
|
protected IntersectVisitor getSparseIntersectVisitor(DocIdSetBuilder result) {
|
||||||
return new IntersectVisitor() {
|
return new IntersectVisitor() {
|
||||||
final int[] scratchTriangle = new int[6];
|
final ShapeField.DecodedTriangle scratchTriangle = new ShapeField.DecodedTriangle();
|
||||||
DocIdSetBuilder.BulkAdder adder;
|
DocIdSetBuilder.BulkAdder adder;
|
||||||
|
|
||||||
@Override
|
@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) */
|
/** 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) {
|
protected IntersectVisitor getDenseIntersectVisitor(FixedBitSet intersect, FixedBitSet disjoint, ShapeField.QueryRelation queryRelation) {
|
||||||
return new IntersectVisitor() {
|
return new IntersectVisitor() {
|
||||||
final int[] scratchTriangle = new int[6];
|
final ShapeField.DecodedTriangle scratchTriangle = new ShapeField.DecodedTriangle();
|
||||||
@Override
|
@Override
|
||||||
public void visit(int docID) throws IOException {
|
public void visit(int docID) throws IOException {
|
||||||
if (queryRelation == ShapeField.QueryRelation.DISJOINT) {
|
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 */
|
/** 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) {
|
private IntersectVisitor getInverseIntersectVisitor(ShapeQuery query, FixedBitSet result, int[] cost) {
|
||||||
return new IntersectVisitor() {
|
return new IntersectVisitor() {
|
||||||
int[] scratchTriangle = new int[6];
|
final ShapeField.DecodedTriangle scratchTriangle = new ShapeField.DecodedTriangle();
|
||||||
@Override
|
@Override
|
||||||
public void visit(int docID) {
|
public void visit(int docID) {
|
||||||
result.clear(docID);
|
result.clear(docID);
|
||||||
|
|
|
@ -46,16 +46,16 @@ public class XYShapeBoundingBoxQuery extends ShapeQuery {
|
||||||
|
|
||||||
/** returns true if the query matches the encoded triangle */
|
/** returns true if the query matches the encoded triangle */
|
||||||
@Override
|
@Override
|
||||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||||
// decode indexed triangle
|
// decode indexed triangle
|
||||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||||
|
|
||||||
int aY = scratchTriangle[0];
|
int aY = scratchTriangle.aY;
|
||||||
int aX = scratchTriangle[1];
|
int aX = scratchTriangle.aX;
|
||||||
int bY = scratchTriangle[2];
|
int bY = scratchTriangle.bY;
|
||||||
int bX = scratchTriangle[3];
|
int bX = scratchTriangle.bX;
|
||||||
int cY = scratchTriangle[4];
|
int cY = scratchTriangle.cY;
|
||||||
int cX = scratchTriangle[5];
|
int cX = scratchTriangle.cX;
|
||||||
|
|
||||||
if (queryRelation == QueryRelation.WITHIN) {
|
if (queryRelation == QueryRelation.WITHIN) {
|
||||||
return rectangle2D.containsTriangle(aX, aY, bX, bY, cX, cY);
|
return rectangle2D.containsTriangle(aX, aY, bX, bY, cX, cY);
|
||||||
|
|
|
@ -86,15 +86,15 @@ final class XYShapeLineQuery extends ShapeQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||||
|
|
||||||
double alat = decode(scratchTriangle[0]);
|
double alat = decode(scratchTriangle.aY);
|
||||||
double alon = decode(scratchTriangle[1]);
|
double alon = decode(scratchTriangle.aX);
|
||||||
double blat = decode(scratchTriangle[2]);
|
double blat = decode(scratchTriangle.bY);
|
||||||
double blon = decode(scratchTriangle[3]);
|
double blon = decode(scratchTriangle.bX);
|
||||||
double clat = decode(scratchTriangle[4]);
|
double clat = decode(scratchTriangle.cY);
|
||||||
double clon = decode(scratchTriangle[5]);
|
double clon = decode(scratchTriangle.cX);
|
||||||
|
|
||||||
if (queryRelation == QueryRelation.WITHIN) {
|
if (queryRelation == QueryRelation.WITHIN) {
|
||||||
return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
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.index.PointValues.Relation;
|
||||||
import org.apache.lucene.util.NumericUtils;
|
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}.
|
* Finds all previously indexed cartesian shapes that intersect the specified arbitrary cartesian {@link XYPolygon}.
|
||||||
*
|
*
|
||||||
|
@ -76,15 +78,15 @@ final class XYShapePolygonQuery extends ShapeQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) {
|
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||||
|
|
||||||
double alat = XYEncodingUtils.decode(scratchTriangle[0]);
|
double alat = decode(scratchTriangle.aY);
|
||||||
double alon = XYEncodingUtils.decode(scratchTriangle[1]);
|
double alon = decode(scratchTriangle.aX);
|
||||||
double blat = XYEncodingUtils.decode(scratchTriangle[2]);
|
double blat = decode(scratchTriangle.bY);
|
||||||
double blon = XYEncodingUtils.decode(scratchTriangle[3]);
|
double blon = decode(scratchTriangle.bX);
|
||||||
double clat = XYEncodingUtils.decode(scratchTriangle[4]);
|
double clat = decode(scratchTriangle.cY);
|
||||||
double clon = XYEncodingUtils.decode(scratchTriangle[5]);
|
double clon = decode(scratchTriangle.cX);
|
||||||
|
|
||||||
if (queryRelation == QueryRelation.WITHIN) {
|
if (queryRelation == QueryRelation.WITHIN) {
|
||||||
return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
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 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)) {
|
if (lastNode != null && isVertexEquals(lastNode, lastNode.next)) {
|
||||||
removeNode(lastNode);
|
removeNode(lastNode, true);
|
||||||
lastNode = lastNode.next;
|
lastNode = lastNode.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,7 +286,7 @@ final public class Tessellator {
|
||||||
Node sharedVertex = getSharedVertex(holeNode, next);
|
Node sharedVertex = getSharedVertex(holeNode, next);
|
||||||
if (sharedVertex != null) {
|
if (sharedVertex != null) {
|
||||||
// Split the resulting polygon.
|
// Split the resulting polygon.
|
||||||
Node node = splitPolygon(next, sharedVertex);
|
Node node = splitPolygon(next, sharedVertex, true);
|
||||||
// Filter the split nodes.
|
// Filter the split nodes.
|
||||||
filterPoints(node, node.next);
|
filterPoints(node, node.next);
|
||||||
return;
|
return;
|
||||||
|
@ -300,8 +300,10 @@ final public class Tessellator {
|
||||||
|
|
||||||
// Determine whether a hole bridge could be fetched.
|
// Determine whether a hole bridge could be fetched.
|
||||||
if(outerNode != null) {
|
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.
|
// Split the resulting polygon.
|
||||||
Node node = splitPolygon(outerNode, holeNode);
|
Node node = splitPolygon(outerNode, holeNode, fromPolygon);
|
||||||
// Filter the split nodes.
|
// Filter the split nodes.
|
||||||
filterPoints(node, node.next);
|
filterPoints(node, node.next);
|
||||||
}
|
}
|
||||||
|
@ -369,7 +371,7 @@ final public class Tessellator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if the provided vertex is in the polygon and return it **/
|
/** 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;
|
Node next = polygon;
|
||||||
do {
|
do {
|
||||||
if (isVertexEquals(next, vertex)) {
|
if (isVertexEquals(next, vertex)) {
|
||||||
|
@ -417,10 +419,14 @@ final public class Tessellator {
|
||||||
// Determine whether the current triangle must be cut off.
|
// 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;
|
final boolean isReflex = area(prevNode.getX(), prevNode.getY(), currEar.getX(), currEar.getY(), nextNode.getX(), nextNode.getY()) >= 0;
|
||||||
if (isReflex == false && isEar(currEar, mortonOptimized) == true) {
|
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
|
// 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.
|
// Remove the ear node.
|
||||||
removeNode(currEar);
|
removeNode(currEar, caFromPolygon);
|
||||||
|
|
||||||
// Skipping to the next node leaves fewer slither triangles.
|
// Skipping to the next node leaves fewer slither triangles.
|
||||||
currEar = nextNode.next;
|
currEar = nextNode.next;
|
||||||
|
@ -439,7 +445,7 @@ final public class Tessellator {
|
||||||
continue earcut;
|
continue earcut;
|
||||||
case CURE:
|
case CURE:
|
||||||
// if this didn't work, try curing all small self-intersections locally
|
// if this didn't work, try curing all small self-intersections locally
|
||||||
currEar = cureLocalIntersections(currEar, tessellation);
|
currEar = cureLocalIntersections(currEar, tessellation, mortonOptimized);
|
||||||
state = State.SPLIT;
|
state = State.SPLIT;
|
||||||
continue earcut;
|
continue earcut;
|
||||||
case SPLIT:
|
case SPLIT:
|
||||||
|
@ -531,7 +537,7 @@ final public class Tessellator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Iterate through all polygon nodes and remove small local self-intersections **/
|
/** 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 node = startNode;
|
||||||
Node nextNode;
|
Node nextNode;
|
||||||
do {
|
do {
|
||||||
|
@ -544,12 +550,17 @@ final public class Tessellator {
|
||||||
&& isIntersectingPolygon(a, a.getX(), a.getY(), b.getX(), b.getY()) == false
|
&& 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())
|
&& linesIntersect(a.getX(), a.getY(), node.getX(), node.getY(), nextNode.getX(), nextNode.getY(), b.getX(), b.getY())
|
||||||
&& isLocallyInside(a, b) && isLocallyInside(b, a)) {
|
&& 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
|
// 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
|
// remove two nodes involved
|
||||||
removeNode(node);
|
removeNode(node, caFromPolygon);
|
||||||
removeNode(node.next);
|
removeNode(node.next, caFromPolygon);
|
||||||
node = startNode = b;
|
node = startNode = b;
|
||||||
}
|
}
|
||||||
node = node.next;
|
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 **/
|
/** 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.
|
// Search for a valid diagonal that divides the polygon into two.
|
||||||
Node searchNode = start;
|
Node searchNode = start;
|
||||||
Node nextNode;
|
Node nextNode;
|
||||||
|
@ -569,17 +580,17 @@ final public class Tessellator {
|
||||||
while (diagonal != searchNode.previous) {
|
while (diagonal != searchNode.previous) {
|
||||||
if(searchNode.idx != diagonal.idx && isValidDiagonal(searchNode, diagonal)) {
|
if(searchNode.idx != diagonal.idx && isValidDiagonal(searchNode, diagonal)) {
|
||||||
// Split the polygon into two at the point of the 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.
|
// Filter the resulting polygon.
|
||||||
searchNode = filterPoints(searchNode, searchNode.next);
|
searchNode = filterPoints(searchNode, searchNode.next);
|
||||||
splitNode = filterPoints(splitNode, splitNode.next);
|
splitNode = filterPoints(splitNode, splitNode.next);
|
||||||
// Attempt to earcut both of the resulting polygons
|
// Attempt to earcut both of the resulting polygons
|
||||||
if (mortonIndexed) {
|
if (mortonOptimized) {
|
||||||
sortByMortonWithReset(searchNode);
|
sortByMortonWithReset(searchNode);
|
||||||
sortByMortonWithReset(splitNode);
|
sortByMortonWithReset(splitNode);
|
||||||
}
|
}
|
||||||
earcutLinkedList(polygon, searchNode, tessellation, State.INIT, mortonIndexed);
|
earcutLinkedList(polygon, searchNode, tessellation, State.INIT, mortonOptimized);
|
||||||
earcutLinkedList(polygon, splitNode, tessellation, State.INIT, mortonIndexed);
|
earcutLinkedList(polygon, splitNode, tessellation, State.INIT, mortonOptimized);
|
||||||
// Finish the iterative search
|
// Finish the iterative search
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -590,14 +601,120 @@ final public class Tessellator {
|
||||||
return false;
|
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. **/
|
/** 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 a2 = new Node(a);
|
||||||
final Node b2 = new Node(b);
|
final Node b2 = new Node(b);
|
||||||
final Node an = a.next;
|
final Node an = a.next;
|
||||||
final Node bp = b.previous;
|
final Node bp = b.previous;
|
||||||
|
|
||||||
a.next = b;
|
a.next = b;
|
||||||
|
a.isNextEdgeFromPolygon = edgeFromPolygon;
|
||||||
a.nextZ = b;
|
a.nextZ = b;
|
||||||
b.previous = a;
|
b.previous = a;
|
||||||
b.previousZ = a;
|
b.previousZ = a;
|
||||||
|
@ -606,6 +723,7 @@ final public class Tessellator {
|
||||||
an.previous = a2;
|
an.previous = a2;
|
||||||
an.previousZ = a2;
|
an.previousZ = a2;
|
||||||
b2.next = a2;
|
b2.next = a2;
|
||||||
|
b2.isNextEdgeFromPolygon = edgeFromPolygon;
|
||||||
b2.nextZ = a2;
|
b2.nextZ = a2;
|
||||||
a2.previous = b2;
|
a2.previous = b2;
|
||||||
a2.previousZ = 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 */
|
/** 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;
|
Node next = start;
|
||||||
double windingSum = 0;
|
double windingSum = 0;
|
||||||
do {
|
do {
|
||||||
|
@ -796,10 +914,13 @@ final public class Tessellator {
|
||||||
continueIteration = false;
|
continueIteration = false;
|
||||||
nextNode = node.next;
|
nextNode = node.next;
|
||||||
prevNode = node.previous;
|
prevNode = node.previous;
|
||||||
if (isVertexEquals(node, nextNode)
|
//We can filter points when they are the same, if not and they are co-linear we can only
|
||||||
|| area(prevNode.getX(), prevNode.getY(), node.getX(), node.getY(), nextNode.getX(), nextNode.getY()) == 0) {
|
//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
|
// Remove the node
|
||||||
removeNode(node);
|
removeNode(node, prevNode.isNextEdgeFromPolygon);
|
||||||
node = end = prevNode;
|
node = end = prevNode;
|
||||||
|
|
||||||
if (node == nextNode) {
|
if (node == nextNode) {
|
||||||
|
@ -835,9 +956,10 @@ final public class Tessellator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Removes a node from the doubly linked list */
|
/** 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.next.previous = node.previous;
|
||||||
node.previous.next = node.next;
|
node.previous.next = node.next;
|
||||||
|
node.previous.isNextEdgeFromPolygon = edgeFromPolygon;
|
||||||
|
|
||||||
if (node.previousZ != null) {
|
if (node.previousZ != null) {
|
||||||
node.previousZ.nextZ = node.nextZ;
|
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 */
|
/** 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) {
|
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 a = orient(x, y, ax, ay, bx, by);
|
||||||
int b = orient(x, y, bx, by, cx, cy);
|
int b = orient(x, y, bx, by, cx, cy);
|
||||||
if (a == 0 || b == 0 || a < 0 == b < 0) {
|
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 c == 0 || (c < 0 == (b < 0 || a < 0));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Brute force compute if a point is in the polygon by traversing entire triangulation
|
/** 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;
|
private final int idx;
|
||||||
// vertex index in the polygon
|
// vertex index in the polygon
|
||||||
private final int vrtxIdx;
|
private final int vrtxIdx;
|
||||||
// reference to the polygon for lat/lon values
|
// reference to the polygon for lat/lon values;
|
||||||
// private final Polygon polygon;
|
|
||||||
private final double[] polyX;
|
private final double[] polyX;
|
||||||
private final double[] polyY;
|
private final double[] polyY;
|
||||||
// encoded x value
|
// encoded x value
|
||||||
|
@ -920,6 +1051,8 @@ final public class Tessellator {
|
||||||
private Node previousZ;
|
private Node previousZ;
|
||||||
// next z node
|
// next z node
|
||||||
private Node nextZ;
|
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) {
|
protected Node(final double[] x, final double[] y, final int index, final int vertexIndex, final boolean isGeo) {
|
||||||
this.idx = index;
|
this.idx = index;
|
||||||
|
@ -933,6 +1066,7 @@ final public class Tessellator {
|
||||||
this.next = null;
|
this.next = null;
|
||||||
this.previousZ = null;
|
this.previousZ = null;
|
||||||
this.nextZ = null;
|
this.nextZ = null;
|
||||||
|
this.isNextEdgeFromPolygon = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** simple deep copy constructor */
|
/** simple deep copy constructor */
|
||||||
|
@ -948,6 +1082,7 @@ final public class Tessellator {
|
||||||
this.next = other.next;
|
this.next = other.next;
|
||||||
this.previousZ = other.previousZ;
|
this.previousZ = other.previousZ;
|
||||||
this.nextZ = other.nextZ;
|
this.nextZ = other.nextZ;
|
||||||
|
this.isNextEdgeFromPolygon = other.isNextEdgeFromPolygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** get the x value */
|
/** get the x value */
|
||||||
|
@ -979,9 +1114,11 @@ final public class Tessellator {
|
||||||
/** Triangle in the tessellated mesh */
|
/** Triangle in the tessellated mesh */
|
||||||
public final static class Triangle {
|
public final static class Triangle {
|
||||||
Node[] vertex;
|
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.vertex = new Node[] {a, b, c};
|
||||||
|
this.edgeFromPolygon = new boolean[] {isABfromPolygon, isBCfromPolygon, isCAfromPolygon};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** get quantized x value for the given vertex */
|
/** get quantized x value for the given vertex */
|
||||||
|
@ -1004,6 +1141,11 @@ final public class Tessellator {
|
||||||
return this.vertex[vertex].getX();
|
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 */
|
/** utility method to compute whether the point is in the triangle */
|
||||||
protected boolean containsPoint(double lat, double lon) {
|
protected boolean containsPoint(double lat, double lon) {
|
||||||
return pointInTriangle(lon, lat,
|
return pointInTriangle(lon, lat,
|
||||||
|
@ -1014,9 +1156,9 @@ final public class Tessellator {
|
||||||
|
|
||||||
/** pretty print the triangle vertices */
|
/** pretty print the triangle vertices */
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String result = vertex[0].x + ", " + vertex[0].y + " " +
|
String result = vertex[0].x + ", " + vertex[0].y + " [" + edgeFromPolygon[0] + "] " +
|
||||||
vertex[1].x + ", " + vertex[1].y + " " +
|
vertex[1].x + ", " + vertex[1].y + " [" + edgeFromPolygon[1] + "] " +
|
||||||
vertex[2].x + ", " + vertex[2].y;
|
vertex[2].x + ", " + vertex[2].y + " [" + edgeFromPolygon[2] + "]";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,18 +264,18 @@ public abstract class BaseLatLonShapeTestCase extends BaseShapeTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
double[] quantizeTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
|
double[] quantizeTriangle(double ax, double ay, boolean ab, double bx, double by, boolean bc, double cx, double cy, boolean ca) {
|
||||||
int[] decoded = encodeDecodeTriangle(ax, ay, bx, by, cx, cy);
|
ShapeField.DecodedTriangle decoded = encodeDecodeTriangle(ax, ay, ab, bx, by, bc, cx, cy, ca);
|
||||||
return new double[]{decodeLatitude(decoded[0]), decodeLongitude(decoded[1]), decodeLatitude(decoded[2]), decodeLongitude(decoded[3]), decodeLatitude(decoded[4]), decodeLongitude(decoded[5])};
|
return new double[]{decodeLatitude(decoded.aY), decodeLongitude(decoded.aX), decodeLatitude(decoded.bY), decodeLongitude(decoded.bX), decodeLatitude(decoded.cY), decodeLongitude(decoded.cX)};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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];
|
byte[] encoded = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(encoded, encodeLatitude(ay), encodeLongitude(ax), encodeLatitude(by), encodeLongitude(bx), encodeLatitude(cy), encodeLongitude(cx));
|
ShapeField.encodeTriangle(encoded, encodeLatitude(ay), encodeLongitude(ax), ab, encodeLatitude(by), encodeLongitude(bx), bc, encodeLatitude(cy), encodeLongitude(cx), ca);
|
||||||
int[] decoded = new int[6];
|
ShapeField.DecodedTriangle triangle = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(encoded, decoded);
|
ShapeField.decodeTriangle(encoded, triangle);
|
||||||
return decoded;
|
return triangle;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.lucene.document;
|
package org.apache.lucene.document;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.apache.lucene.geo.GeoUtils;
|
import org.apache.lucene.geo.GeoUtils;
|
||||||
import org.apache.lucene.geo.Polygon2D;
|
import org.apache.lucene.geo.Polygon2D;
|
||||||
import org.apache.lucene.index.PointValues;
|
import org.apache.lucene.index.PointValues;
|
||||||
|
@ -55,15 +53,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int cxEnc = encodeX(cx);
|
int cxEnc = encodeX(cx);
|
||||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//One shared point with MBR -> MinLat, MaxLon
|
//One shared point with MBR -> MinLat, MaxLon
|
||||||
|
@ -82,15 +80,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int cxEnc = encodeX(cx);
|
int cxEnc = encodeX(cx);
|
||||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//One shared point with MBR -> MaxLat, MaxLon
|
//One shared point with MBR -> MaxLat, MaxLon
|
||||||
|
@ -109,15 +107,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int cxEnc = encodeX(blon);
|
int cxEnc = encodeX(blon);
|
||||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//One shared point with MBR -> MaxLat, MinLon
|
//One shared point with MBR -> MaxLat, MinLon
|
||||||
|
@ -136,15 +134,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int cxEnc = encodeX(blon);
|
int cxEnc = encodeX(blon);
|
||||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Two shared point with MBR -> [MinLat, MinLon], [MaxLat, MaxLon], third point below
|
//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);
|
int cxEnc = encodeX(cx);
|
||||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Two shared point with MBR -> [MinLat, MinLon], [MaxLat, MaxLon], third point above
|
//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);
|
int cxEnc = encodeX(cx);
|
||||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Two shared point with MBR -> [MinLat, MaxLon], [MaxLat, MinLon], third point below
|
//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);
|
int cxEnc = encodeX(cx);
|
||||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Two shared point with MBR -> [MinLat, MaxLon], [MaxLat, MinLon], third point above
|
//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);
|
int cxEnc = encodeX(cx);
|
||||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//all points shared with MBR
|
//all points shared with MBR
|
||||||
|
@ -271,15 +269,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int cxEnc = encodeX(cx);
|
int cxEnc = encodeX(cx);
|
||||||
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
verifyEncodingPermutations(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//all points shared with MBR
|
//all points shared with MBR
|
||||||
|
@ -297,15 +295,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int cyEnc = encodeY(cy);
|
int cyEnc = encodeY(cy);
|
||||||
int cxEnc = encodeX(cx);
|
int cxEnc = encodeX(cx);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == cyEnc);
|
assertEquals(encoded.cY, cyEnc);
|
||||||
assertTrue(encoded[5] == cxEnc);
|
assertEquals(encoded.cX, cxEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//[a,b,c] == [c,a,b] == [b,c,a] == [c,b,a] == [b,a,c] == [a,c,b]
|
//[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);
|
assertTrue(GeoUtils.orient(ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc) != 0);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
//[a,b,c]
|
//[a,b,c]
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, false);
|
||||||
int[] encodedABC = new int[6];
|
ShapeField.DecodedTriangle encodedABC = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encodedABC);
|
ShapeField.decodeTriangle(b, encodedABC);
|
||||||
//[c,a,b]
|
//[c,a,b]
|
||||||
ShapeField.encodeTriangle(b, cyEnc, cxEnc, ayEnc, axEnc, byEnc, bxEnc);
|
ShapeField.encodeTriangle(b, cyEnc, cxEnc, false, ayEnc, axEnc, true, byEnc, bxEnc, true);
|
||||||
int[] encodedCAB = new int[6];
|
ShapeField.DecodedTriangle encodedCAB = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encodedCAB);
|
ShapeField.decodeTriangle(b, encodedCAB);
|
||||||
assertTrue(Arrays.equals(encodedABC, encodedCAB));
|
assertEquals(encodedABC, encodedCAB);
|
||||||
//[b,c,a]
|
//[b,c,a]
|
||||||
ShapeField.encodeTriangle(b, byEnc, bxEnc, cyEnc, cxEnc, ayEnc, axEnc);
|
ShapeField.encodeTriangle(b, byEnc, bxEnc, true, cyEnc, cxEnc, false, ayEnc, axEnc, true);
|
||||||
int[] encodedBCA = new int[6];
|
ShapeField.DecodedTriangle encodedBCA = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encodedBCA);
|
ShapeField.decodeTriangle(b, encodedBCA);
|
||||||
assertTrue(Arrays.equals(encodedABC, encodedBCA));
|
assertEquals(encodedABC, encodedBCA);
|
||||||
//[c,b,a]
|
//[c,b,a]
|
||||||
ShapeField.encodeTriangle(b, cyEnc, cxEnc, byEnc, bxEnc, ayEnc, axEnc);
|
ShapeField.encodeTriangle(b, cyEnc, cxEnc, true, byEnc, bxEnc, true, ayEnc, axEnc, false);
|
||||||
int[] encodedCBA= new int[6];
|
ShapeField.DecodedTriangle encodedCBA= new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encodedCBA);
|
ShapeField.decodeTriangle(b, encodedCBA);
|
||||||
assertTrue(Arrays.equals(encodedABC, encodedCBA));
|
assertEquals(encodedABC, encodedCBA);
|
||||||
//[b,a,c]
|
//[b,a,c]
|
||||||
ShapeField.encodeTriangle(b, byEnc, bxEnc, ayEnc, axEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, byEnc, bxEnc, true, ayEnc, axEnc, false, cyEnc, cxEnc, true);
|
||||||
int[] encodedBAC= new int[6];
|
ShapeField.DecodedTriangle encodedBAC= new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encodedBAC);
|
ShapeField.decodeTriangle(b, encodedBAC);
|
||||||
assertTrue(Arrays.equals(encodedABC, encodedBAC));
|
assertEquals(encodedABC, encodedBAC);
|
||||||
//[a,c,b]
|
//[a,c,b]
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, cyEnc, cxEnc, byEnc, bxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, false, cyEnc, cxEnc, true, byEnc, bxEnc, true);
|
||||||
int[] encodedACB= new int[6];
|
ShapeField.DecodedTriangle encodedACB= new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encodedACB);
|
ShapeField.decodeTriangle(b, encodedACB);
|
||||||
assertTrue(Arrays.equals(encodedABC, encodedACB));
|
assertEquals(encodedABC, encodedACB);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPointEncoding() {
|
public void testPointEncoding() {
|
||||||
|
@ -350,11 +348,15 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int latEnc = encodeY(lat);
|
int latEnc = encodeY(lat);
|
||||||
int lonEnc = encodeX(lon);
|
int lonEnc = encodeX(lon);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, latEnc, lonEnc, latEnc, lonEnc, latEnc, lonEnc);
|
ShapeField.encodeTriangle(b, latEnc, lonEnc, true, latEnc, lonEnc, true, latEnc, lonEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == latEnc && encoded[2] == latEnc && encoded[4] == latEnc);
|
assertEquals(encoded.aY, latEnc);
|
||||||
assertTrue(encoded[1] == lonEnc && encoded[3] == lonEnc && encoded[5] == lonEnc);
|
assertEquals(encoded.aX, lonEnc);
|
||||||
|
assertEquals(encoded.bY, latEnc);
|
||||||
|
assertEquals(encoded.bX, lonEnc);
|
||||||
|
assertEquals(encoded.cY, latEnc);
|
||||||
|
assertEquals(encoded.cX, lonEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testLineEncodingSameLat() {
|
public void testLineEncodingSameLat() {
|
||||||
|
@ -365,33 +367,31 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int axEnc = encodeX(ax);
|
int axEnc = encodeX(ax);
|
||||||
int bxEnc = encodeX(bx);
|
int bxEnc = encodeX(bx);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, latEnc, axEnc, latEnc, bxEnc, latEnc, axEnc);
|
ShapeField.encodeTriangle(b, latEnc, axEnc, true, latEnc, bxEnc, true, latEnc, axEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == latEnc);
|
assertEquals(encoded.aY, latEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == latEnc);
|
assertEquals(encoded.bY, latEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == latEnc);
|
assertEquals(encoded.cY, latEnc);
|
||||||
assertTrue(encoded[5] == axEnc);
|
assertEquals(encoded.cX, axEnc);
|
||||||
ShapeField.encodeTriangle(b, latEnc, axEnc, latEnc, axEnc, latEnc, bxEnc);
|
ShapeField.encodeTriangle(b, latEnc, axEnc, true, latEnc, axEnc, true, latEnc, bxEnc, true);
|
||||||
encoded = new int[6];
|
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == latEnc);
|
assertEquals(encoded.aY, latEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == latEnc);
|
assertEquals(encoded.bY, latEnc);
|
||||||
assertTrue(encoded[3] == axEnc);
|
assertEquals(encoded.bX, axEnc);
|
||||||
assertTrue(encoded[4] == latEnc);
|
assertEquals(encoded.cY, latEnc);
|
||||||
assertTrue(encoded[5] == bxEnc);
|
assertEquals(encoded.cX, bxEnc);
|
||||||
ShapeField.encodeTriangle(b, latEnc, bxEnc, latEnc, axEnc, latEnc, axEnc);
|
ShapeField.encodeTriangle(b, latEnc, bxEnc, true, latEnc, axEnc, true, latEnc, axEnc, true);
|
||||||
encoded = new int[6];
|
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == latEnc);
|
assertEquals(encoded.aY, latEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == latEnc);
|
assertEquals(encoded.bY, latEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == latEnc);
|
assertEquals(encoded.cY, latEnc);
|
||||||
assertTrue(encoded[5] == axEnc);
|
assertEquals(encoded.cX, axEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testLineEncodingSameLon() {
|
public void testLineEncodingSameLon() {
|
||||||
|
@ -402,33 +402,31 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int byEnc = encodeY(by);
|
int byEnc = encodeY(by);
|
||||||
int lonEnc = encodeX(lon);
|
int lonEnc = encodeX(lon);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, lonEnc, byEnc, lonEnc, ayEnc, lonEnc);
|
ShapeField.encodeTriangle(b, ayEnc, lonEnc, true, byEnc, lonEnc, true, ayEnc, lonEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == lonEnc);
|
assertEquals(encoded.aX, lonEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == lonEnc);
|
assertEquals(encoded.bX, lonEnc);
|
||||||
assertTrue(encoded[4] == ayEnc);
|
assertEquals(encoded.cY, ayEnc);
|
||||||
assertTrue(encoded[5] == lonEnc);
|
assertEquals(encoded.cX, lonEnc);
|
||||||
ShapeField.encodeTriangle(b, ayEnc, lonEnc, ayEnc, lonEnc, byEnc, lonEnc);
|
ShapeField.encodeTriangle(b, ayEnc, lonEnc, true, ayEnc, lonEnc, true, byEnc, lonEnc, true);
|
||||||
encoded = new int[6];
|
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == lonEnc);
|
assertEquals(encoded.aX, lonEnc);
|
||||||
assertTrue(encoded[2] == ayEnc);
|
assertEquals(encoded.bY, ayEnc);
|
||||||
assertTrue(encoded[3] == lonEnc);
|
assertEquals(encoded.bX, lonEnc);
|
||||||
assertTrue(encoded[4] == byEnc);
|
assertEquals(encoded.cY, byEnc);
|
||||||
assertTrue(encoded[5] == lonEnc);
|
assertEquals(encoded.cX, lonEnc);
|
||||||
ShapeField.encodeTriangle(b, byEnc, lonEnc, ayEnc, lonEnc, ayEnc, lonEnc);
|
ShapeField.encodeTriangle(b, byEnc, lonEnc, true, ayEnc, lonEnc, true, ayEnc, lonEnc, true);
|
||||||
encoded = new int[6];
|
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == lonEnc);
|
assertEquals(encoded.aX, lonEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == lonEnc);
|
assertEquals(encoded.bX, lonEnc);
|
||||||
assertTrue(encoded[4] == ayEnc);
|
assertEquals(encoded.cY, ayEnc);
|
||||||
assertTrue(encoded[5] == lonEnc);
|
assertEquals(encoded.cX, lonEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testLineEncoding() {
|
public void testLineEncoding() {
|
||||||
|
@ -441,33 +439,31 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int axEnc = encodeX(ax);
|
int axEnc = encodeX(ax);
|
||||||
int bxEnc = encodeX(bx);
|
int bxEnc = encodeX(bx);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, ayEnc, axEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, ayEnc, axEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == ayEnc);
|
assertEquals(encoded.cY, ayEnc);
|
||||||
assertTrue(encoded[5] == axEnc);
|
assertEquals(encoded.cX, axEnc);
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, ayEnc, axEnc, byEnc, bxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, ayEnc, axEnc, true, byEnc, bxEnc, true);
|
||||||
encoded = new int[6];
|
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == ayEnc);
|
assertEquals(encoded.bY, ayEnc);
|
||||||
assertTrue(encoded[3] == axEnc);
|
assertEquals(encoded.bX, axEnc);
|
||||||
assertTrue(encoded[4] == byEnc);
|
assertEquals(encoded.cY, byEnc);
|
||||||
assertTrue(encoded[5] == bxEnc);
|
assertEquals(encoded.cX, bxEnc);
|
||||||
ShapeField.encodeTriangle(b, byEnc, bxEnc, ayEnc, axEnc, ayEnc, axEnc);
|
ShapeField.encodeTriangle(b, byEnc, bxEnc, true, ayEnc, axEnc, true, ayEnc, axEnc, true);
|
||||||
encoded = new int[6];
|
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == ayEnc);
|
assertEquals(encoded.aY, ayEnc);
|
||||||
assertTrue(encoded[1] == axEnc);
|
assertEquals(encoded.aX, axEnc);
|
||||||
assertTrue(encoded[2] == byEnc);
|
assertEquals(encoded.bY, byEnc);
|
||||||
assertTrue(encoded[3] == bxEnc);
|
assertEquals(encoded.bX, bxEnc);
|
||||||
assertTrue(encoded[4] == ayEnc);
|
assertEquals(encoded.cY, ayEnc);
|
||||||
assertTrue(encoded[5] == axEnc);
|
assertEquals(encoded.cX, axEnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRandomPointEncoding() {
|
public void testRandomPointEncoding() {
|
||||||
|
@ -505,16 +501,16 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
|
|
||||||
//quantize the triangle
|
//quantize the triangle
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, original[0], original[1], original[2], original[3], original[4], original[5]);
|
ShapeField.encodeTriangle(b, original[0], original[1], true, original[2], original[3], true, original[4], original[5], true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
double[] encodedQuantize = new double[] {
|
double[] encodedQuantize = new double[] {
|
||||||
decodeY(encoded[0]),
|
decodeY(encoded.aY),
|
||||||
decodeX(encoded[1]),
|
decodeX(encoded.aX),
|
||||||
decodeY(encoded[2]),
|
decodeY(encoded.bY),
|
||||||
decodeX(encoded[3]),
|
decodeX(encoded.bX),
|
||||||
decodeY(encoded[4]),
|
decodeY(encoded.cY),
|
||||||
decodeX(encoded[5])};
|
decodeX(encoded.cX)};
|
||||||
|
|
||||||
int orientation = GeoUtils.orient(original[1], original[0], original[3], original[2], original[5], original[4]);
|
int orientation = GeoUtils.orient(original[1], original[0], original[3], original[2], original[5], original[4]);
|
||||||
//quantize original
|
//quantize original
|
||||||
|
@ -560,14 +556,14 @@ public abstract class BaseShapeEncodingTestCase extends LuceneTestCase {
|
||||||
int cyEnc = encodeY(cy);
|
int cyEnc = encodeY(cy);
|
||||||
int cxEnc = encodeX(cx);
|
int cxEnc = encodeX(cx);
|
||||||
byte[] b = new byte[7 * ShapeField.BYTES];
|
byte[] b = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(b, ayEnc, axEnc, byEnc, bxEnc, cyEnc, cxEnc);
|
ShapeField.encodeTriangle(b, ayEnc, axEnc, true, byEnc, bxEnc, true, cyEnc, cxEnc, true);
|
||||||
int[] encoded = new int[6];
|
ShapeField.DecodedTriangle encoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(b, encoded);
|
ShapeField.decodeTriangle(b, encoded);
|
||||||
assertTrue(encoded[0] == byEnc);
|
assertTrue(encoded.aY == byEnc);
|
||||||
assertTrue(encoded[1] == bxEnc);
|
assertTrue(encoded.aX == bxEnc);
|
||||||
assertTrue(encoded[2] == cyEnc);
|
assertTrue(encoded.bY == cyEnc);
|
||||||
assertTrue(encoded[3] == cxEnc);
|
assertTrue(encoded.bX == cxEnc);
|
||||||
assertTrue(encoded[4] == ayEnc);
|
assertTrue(encoded.cY == ayEnc);
|
||||||
assertTrue(encoded[5] == axEnc);
|
assertTrue(encoded.cX == axEnc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -545,8 +545,8 @@ public abstract class BaseShapeTestCase extends LuceneTestCase {
|
||||||
abstract double quantizeXCeil(double raw);
|
abstract double quantizeXCeil(double raw);
|
||||||
abstract double quantizeY(double raw);
|
abstract double quantizeY(double raw);
|
||||||
abstract double quantizeYCeil(double raw);
|
abstract double quantizeYCeil(double raw);
|
||||||
abstract double[] quantizeTriangle(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 int[] encodeDecodeTriangle(double ax, double ay, double bx, double by, double cx, double cy);
|
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) {
|
private int scaledIterationCount(int shapes) {
|
||||||
|
|
|
@ -144,18 +144,18 @@ public abstract class BaseXYShapeTestCase extends BaseShapeTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
double[] quantizeTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
|
double[] quantizeTriangle(double ax, double ay, boolean ab, double bx, double by, boolean bc, double cx, double cy, boolean ca) {
|
||||||
int[] decoded = encodeDecodeTriangle(ax, ay, bx, by, cx, cy);
|
ShapeField.DecodedTriangle decoded = encodeDecodeTriangle(ax, ay, ab, bx, by, bc, cx, cy, ca);
|
||||||
return new double[]{decode(decoded[0]), decode(decoded[1]), decode(decoded[2]), decode(decoded[3]), decode(decoded[4]), decode(decoded[5])};
|
return new double[]{decode(decoded.aY), decode(decoded.aX), decode(decoded.bY), decode(decoded.bX), decode(decoded.cY), decode(decoded.cX)};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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];
|
byte[] encoded = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(encoded, encode(ay), encode(ax), encode(by), encode(bx), encode(cy), encode(cx));
|
ShapeField.encodeTriangle(encoded, encode(ay), encode(ax), ab, encode(by), encode(bx), bc, encode(cy), encode(cx), ca);
|
||||||
int[] decoded = new int[6];
|
ShapeField.DecodedTriangle triangle = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(encoded, decoded);
|
ShapeField.decodeTriangle(encoded, triangle);
|
||||||
return decoded;
|
return triangle;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,13 +83,13 @@ public class TestLatLonLineShapeQueries extends BaseLatLonShapeTestCase {
|
||||||
Line line = (Line)shape;
|
Line line = (Line)shape;
|
||||||
Rectangle2D rectangle2D = Rectangle2D.create(new Rectangle(minLat, maxLat, minLon, maxLon));
|
Rectangle2D rectangle2D = Rectangle2D.create(new Rectangle(minLat, maxLat, minLon, maxLon));
|
||||||
for (int i = 0, j = 1; j < line.numPoints(); ++i, ++j) {
|
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 (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;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
return queryRelation == QueryRelation.INTERSECTS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ public class TestLatLonLineShapeQueries extends BaseLatLonShapeTestCase {
|
||||||
private boolean testLine(EdgeTree queryPoly, Line line) {
|
private boolean testLine(EdgeTree queryPoly, Line line) {
|
||||||
|
|
||||||
for (int i = 0, j = 1; j < line.numPoints(); ++i, ++j) {
|
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]);
|
Relation r = queryPoly.relateTriangle(qTriangle[1], qTriangle[0], qTriangle[3], qTriangle[2], qTriangle[5], qTriangle[4]);
|
||||||
if (queryRelation == QueryRelation.DISJOINT) {
|
if (queryRelation == QueryRelation.DISJOINT) {
|
||||||
if (r != Relation.CELL_OUTSIDE_QUERY) return false;
|
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));
|
Rectangle2D rectangle2D = Rectangle2D.create(new Rectangle(minLat, maxLat, minLon, maxLon));
|
||||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(p);
|
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(p);
|
||||||
for (Tessellator.Triangle t : tessellation) {
|
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 (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;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
return queryRelation == QueryRelation.INTERSECTS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +101,9 @@ public class TestLatLonPolygonShapeQueries extends BaseLatLonShapeTestCase {
|
||||||
private boolean testPolygon(EdgeTree tree, Polygon shape) {
|
private boolean testPolygon(EdgeTree tree, Polygon shape) {
|
||||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(shape);
|
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(shape);
|
||||||
for (Tessellator.Triangle t : tessellation) {
|
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]);
|
Relation r = tree.relateTriangle(qTriangle[1], qTriangle[0], qTriangle[3], qTriangle[2], qTriangle[5], qTriangle[4]);
|
||||||
if (queryRelation == QueryRelation.DISJOINT) {
|
if (queryRelation == QueryRelation.DISJOINT) {
|
||||||
if (r != Relation.CELL_OUTSIDE_QUERY) return false;
|
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);
|
Tessellator.Triangle t = Tessellator.tessellate(poly).get(0);
|
||||||
|
|
||||||
byte[] encoded = new byte[7 * ShapeField.BYTES];
|
byte[] encoded = new byte[7 * ShapeField.BYTES];
|
||||||
ShapeField.encodeTriangle(encoded, encodeLatitude(t.getY(0)), encodeLongitude(t.getX(0)),
|
ShapeField.encodeTriangle(encoded, encodeLatitude(t.getY(0)), encodeLongitude(t.getX(0)), t.isEdgefromPolygon(0),
|
||||||
encodeLatitude(t.getY(1)), encodeLongitude(t.getX(1)), encodeLatitude(t.getY(2)), encodeLongitude(t.getX(2)));
|
encodeLatitude(t.getY(1)), encodeLongitude(t.getX(1)), t.isEdgefromPolygon(1),
|
||||||
int[] decoded = new int[6];
|
encodeLatitude(t.getY(2)), encodeLongitude(t.getX(2)), t.isEdgefromPolygon(2));
|
||||||
|
ShapeField.DecodedTriangle decoded = new ShapeField.DecodedTriangle();
|
||||||
ShapeField.decodeTriangle(encoded, decoded);
|
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();
|
Document document = new Document();
|
||||||
addPolygonsToDoc(FIELDNAME, document, poly);
|
addPolygonsToDoc(FIELDNAME, document, poly);
|
||||||
|
|
|
@ -81,13 +81,13 @@ public class TestXYLineShapeQueries extends BaseXYShapeTestCase {
|
||||||
XYLine line = (XYLine)shape;
|
XYLine line = (XYLine)shape;
|
||||||
XYRectangle2D rectangle2D = XYRectangle2D.create(new XYRectangle(minX, maxX, minY, maxY));
|
XYRectangle2D rectangle2D = XYRectangle2D.create(new XYRectangle(minX, maxX, minY, maxY));
|
||||||
for (int i = 0, j = 1; j < line.numPoints(); ++i, ++j) {
|
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 (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;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
return queryRelation == QueryRelation.INTERSECTS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ public class TestXYLineShapeQueries extends BaseXYShapeTestCase {
|
||||||
private boolean testLine(EdgeTree queryPoly, XYLine line) {
|
private boolean testLine(EdgeTree queryPoly, XYLine line) {
|
||||||
|
|
||||||
for (int i = 0, j = 1; j < line.numPoints(); ++i, ++j) {
|
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]);
|
Relation r = queryPoly.relateTriangle(qTriangle[1], qTriangle[0], qTriangle[3], qTriangle[2], qTriangle[5], qTriangle[4]);
|
||||||
if (queryRelation == QueryRelation.DISJOINT) {
|
if (queryRelation == QueryRelation.DISJOINT) {
|
||||||
if (r != Relation.CELL_OUTSIDE_QUERY) return false;
|
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));
|
XYRectangle2D rectangle2D = XYRectangle2D.create(new XYRectangle(minX, maxX, minY, maxY));
|
||||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(p);
|
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(p);
|
||||||
for (Tessellator.Triangle t : tessellation) {
|
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 (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;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
return queryRelation == QueryRelation.INTERSECTS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +101,9 @@ public class TestXYPolygonShapeQueries extends BaseXYShapeTestCase {
|
||||||
private boolean testPolygon(EdgeTree tree, XYPolygon shape) {
|
private boolean testPolygon(EdgeTree tree, XYPolygon shape) {
|
||||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(shape);
|
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(shape);
|
||||||
for (Tessellator.Triangle t : tessellation) {
|
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]);
|
Relation r = tree.relateTriangle(qTriangle[1], qTriangle[0], qTriangle[3], qTriangle[2], qTriangle[5], qTriangle[4]);
|
||||||
if (queryRelation == QueryRelation.DISJOINT) {
|
if (queryRelation == QueryRelation.DISJOINT) {
|
||||||
if (r != Relation.CELL_OUTSIDE_QUERY) return false;
|
if (r != Relation.CELL_OUTSIDE_QUERY) return false;
|
||||||
|
|
|
@ -554,6 +554,9 @@ public class TestTessellator extends LuceneTestCase {
|
||||||
Polygon polygon = (Polygon) SimpleWKTShapeParser.parse(wkt);
|
Polygon polygon = (Polygon) SimpleWKTShapeParser.parse(wkt);
|
||||||
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(polygon);
|
List<Tessellator.Triangle> tessellation = Tessellator.tessellate(polygon);
|
||||||
assertEquals(area(polygon), area(tessellation), 0.0);
|
assertEquals(area(polygon), area(tessellation), 0.0);
|
||||||
|
for (Tessellator.Triangle t : tessellation) {
|
||||||
|
checkTriangleEdgesFromPolygon(polygon, t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private double area(Polygon p) {
|
private double area(Polygon p) {
|
||||||
|
@ -578,4 +581,77 @@ public class TestTessellator extends LuceneTestCase {
|
||||||
}
|
}
|
||||||
return area;
|
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