[GEO] Update GeoPolygonFilter to handle ambiguous polygons

PR #8672 addresses ambiguous polygons - those that either cross the dateline or span the map - by complying with the OGC standard right-hand rule. Since ```GeoPolygonFilter``` is self contained logic, the fix in #8672 did not address the issue for the ```GeoPolygonFilter```. This was identified in issue #5968

This fixes the ambiguous polygon issue in ```GeoPolygonFilter``` by moving the dateline crossing code from ```ShapeBuilder``` to ```GeoUtils``` and reusing the logic inside the ```pointInPolygon``` method.  Unit tests are added to ensure support for coordinates specified in either standard lat/lon or great-circle coordinate systems.

closes #5968
closes #9304
This commit is contained in:
Nicholas Knize 2015-01-14 22:06:42 -06:00
parent 9ac6d78308
commit 06667c6aa8
17 changed files with 409 additions and 325 deletions

View File

@ -20,13 +20,12 @@
package org.elasticsearch.common.geo; package org.elasticsearch.common.geo;
import com.vividsolutions.jts.geom.Coordinate;
/** /**
* *
*/ */
public final class GeoPoint { public final class GeoPoint extends Coordinate {
private double lat;
private double lon;
public GeoPoint() { public GeoPoint() {
} }
@ -41,32 +40,36 @@ public final class GeoPoint {
this.resetFromString(value); this.resetFromString(value);
} }
public GeoPoint(GeoPoint other) {
super(other);
}
public GeoPoint(double lat, double lon) { public GeoPoint(double lat, double lon) {
this.lat = lat; this.y = lat;
this.lon = lon; this.x = lon;
} }
public GeoPoint reset(double lat, double lon) { public GeoPoint reset(double lat, double lon) {
this.lat = lat; this.y = lat;
this.lon = lon; this.x = lon;
return this; return this;
} }
public GeoPoint resetLat(double lat) { public GeoPoint resetLat(double lat) {
this.lat = lat; this.y = lat;
return this; return this;
} }
public GeoPoint resetLon(double lon) { public GeoPoint resetLon(double lon) {
this.lon = lon; this.x = lon;
return this; return this;
} }
public GeoPoint resetFromString(String value) { public GeoPoint resetFromString(String value) {
int comma = value.indexOf(','); int comma = value.indexOf(',');
if (comma != -1) { if (comma != -1) {
lat = Double.parseDouble(value.substring(0, comma).trim()); this.y = Double.parseDouble(value.substring(0, comma).trim());
lon = Double.parseDouble(value.substring(comma + 1).trim()); this.x = Double.parseDouble(value.substring(comma + 1).trim());
} else { } else {
resetFromGeoHash(value); resetFromGeoHash(value);
} }
@ -79,38 +82,40 @@ public final class GeoPoint {
} }
public final double lat() { public final double lat() {
return this.lat; return this.y;
} }
public final double getLat() { public final double getLat() {
return this.lat; return this.y;
} }
public final double lon() { public final double lon() {
return this.lon; return this.x;
} }
public final double getLon() { public final double getLon() {
return this.lon; return this.x;
} }
public final String geohash() { public final String geohash() {
return GeoHashUtils.encode(lat, lon); return GeoHashUtils.encode(y, x);
} }
public final String getGeohash() { public final String getGeohash() {
return GeoHashUtils.encode(lat, lon); return GeoHashUtils.encode(y, x);
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null) return false;
if (o instanceof Coordinate) {
GeoPoint geoPoint = (GeoPoint) o; Coordinate c = (Coordinate)o;
return Double.compare(c.x, this.x) == 0
if (Double.compare(geoPoint.lat, lat) != 0) return false; && Double.compare(c.y, this.y) == 0
if (Double.compare(geoPoint.lon, lon) != 0) return false; && Double.compare(c.z, this.z) == 0;
}
if (getClass() != o.getClass()) return false;
return true; return true;
} }
@ -119,15 +124,15 @@ public final class GeoPoint {
public int hashCode() { public int hashCode() {
int result; int result;
long temp; long temp;
temp = lat != +0.0d ? Double.doubleToLongBits(lat) : 0L; temp = y != +0.0d ? Double.doubleToLongBits(y) : 0L;
result = (int) (temp ^ (temp >>> 32)); result = (int) (temp ^ (temp >>> 32));
temp = lon != +0.0d ? Double.doubleToLongBits(lon) : 0L; temp = x != +0.0d ? Double.doubleToLongBits(x) : 0L;
result = 31 * result + (int) (temp ^ (temp >>> 32)); result = 31 * result + (int) (temp ^ (temp >>> 32));
return result; return result;
} }
public String toString() { public String toString() {
return "[" + lat + ", " + lon + "]"; return "[" + y + ", " + x + "]";
} }
public static GeoPoint parseFromLatLon(String latLon) { public static GeoPoint parseFromLatLon(String latLon) {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.common.geo; package org.elasticsearch.common.geo;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree; import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree; import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.apache.lucene.util.SloppyMath; import org.apache.lucene.util.SloppyMath;
@ -37,7 +38,9 @@ public class GeoUtils {
public static final String LATITUDE = GeoPointFieldMapper.Names.LAT; public static final String LATITUDE = GeoPointFieldMapper.Names.LAT;
public static final String LONGITUDE = GeoPointFieldMapper.Names.LON; public static final String LONGITUDE = GeoPointFieldMapper.Names.LON;
public static final String GEOHASH = GeoPointFieldMapper.Names.GEOHASH; public static final String GEOHASH = GeoPointFieldMapper.Names.GEOHASH;
public static final double DATELINE = 180.0D;
/** Earth ellipsoid major axis defined by WGS 84 in meters */ /** Earth ellipsoid major axis defined by WGS 84 in meters */
public static final double EARTH_SEMI_MAJOR_AXIS = 6378137.0; // meters (WGS 84) public static final double EARTH_SEMI_MAJOR_AXIS = 6378137.0; // meters (WGS 84)
@ -422,6 +425,113 @@ public class GeoUtils {
} }
} }
public static boolean correctPolyAmbiguity(GeoPoint[] points, boolean handedness) {
return correctPolyAmbiguity(points, handedness, computePolyOrientation(points), 0, points.length, false);
}
public static boolean correctPolyAmbiguity(GeoPoint[] points, boolean handedness, boolean orientation, int component, int length,
boolean shellCorrected) {
// OGC requires shell as ccw (Right-Handedness) and holes as cw (Left-Handedness)
// since GeoJSON doesn't specify (and doesn't need to) GEO core will assume OGC standards
// thus if orientation is computed as cw, the logic will translate points across dateline
// and convert to a right handed system
// compute the bounding box and calculate range
Pair<Pair, Pair> range = GeoUtils.computeBBox(points, length);
final double rng = (Double)range.getLeft().getRight() - (Double)range.getLeft().getLeft();
// translate the points if the following is true
// 1. shell orientation is cw and range is greater than a hemisphere (180 degrees) but not spanning 2 hemispheres
// (translation would result in a collapsed poly)
// 2. the shell of the candidate hole has been translated (to preserve the coordinate system)
boolean incorrectOrientation = component == 0 && handedness != orientation;
boolean translated = ((incorrectOrientation && (rng > DATELINE && rng != 360.0)) || (shellCorrected && component != 0));
if (translated) {
for (GeoPoint c : points) {
if (c.x < 0.0) {
c.x += 360.0;
}
}
}
return translated;
}
public static boolean computePolyOrientation(GeoPoint[] points) {
return computePolyOrientation(points, points.length);
}
public static boolean computePolyOrientation(GeoPoint[] points, int length) {
// calculate the direction of the points:
// find the point at the top of the set and check its
// neighbors orientation. So direction is equivalent
// to clockwise/counterclockwise
final int top = computePolyOrigin(points, length);
final int prev = ((top + length - 1) % length);
final int next = ((top + 1) % length);
return (points[prev].x > points[next].x);
}
private static final int computePolyOrigin(GeoPoint[] points, int length) {
int top = 0;
// we start at 1 here since top points to 0
for (int i = 1; i < length; i++) {
if (points[i].y < points[top].y) {
top = i;
} else if (points[i].y == points[top].y) {
if (points[i].x < points[top].x) {
top = i;
}
}
}
return top;
}
public static final Pair computeBBox(GeoPoint[] points) {
return computeBBox(points, 0);
}
public static final Pair computeBBox(GeoPoint[] points, int length) {
double minX = points[0].x;
double maxX = points[0].x;
double minY = points[0].y;
double maxY = points[0].y;
// compute the bounding coordinates (@todo: cleanup brute force)
for (int i = 1; i < length; ++i) {
if (points[i].x < minX) {
minX = points[i].x;
}
if (points[i].x > maxX) {
maxX = points[i].x;
}
if (points[i].y < minY) {
minY = points[i].y;
}
if (points[i].y > maxY) {
maxY = points[i].y;
}
}
// return a pair of ranges on the X and Y axis, respectively
return Pair.of(Pair.of(minX, maxX), Pair.of(minY, maxY));
}
public static GeoPoint convertToGreatCircle(GeoPoint point) {
return convertToGreatCircle(point.y, point.x);
}
public static GeoPoint convertToGreatCircle(double lat, double lon) {
GeoPoint p = new GeoPoint(lat, lon);
// convert the point to standard lat/lon bounds
normalizePoint(p);
if (p.x < 0.0D) {
p.x += 360.0D;
}
if (p.y < 0.0D) {
p.y +=180.0D;
}
return p;
}
private GeoUtils() { private GeoUtils() {
} }
} }

View File

@ -23,11 +23,11 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import com.spatial4j.core.shape.ShapeCollection; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import com.spatial4j.core.shape.Shape; import com.spatial4j.core.shape.Shape;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LineString;
@ -35,10 +35,10 @@ import com.vividsolutions.jts.geom.LineString;
public abstract class BaseLineStringBuilder<E extends BaseLineStringBuilder<E>> extends PointCollection<E> { public abstract class BaseLineStringBuilder<E extends BaseLineStringBuilder<E>> extends PointCollection<E> {
protected BaseLineStringBuilder() { protected BaseLineStringBuilder() {
this(new ArrayList<Coordinate>()); this(new ArrayList<GeoPoint>());
} }
protected BaseLineStringBuilder(ArrayList<Coordinate> points) { protected BaseLineStringBuilder(ArrayList<GeoPoint> points) {
super(points); super(points);
} }
@ -49,7 +49,7 @@ public abstract class BaseLineStringBuilder<E extends BaseLineStringBuilder<E>>
@Override @Override
public Shape build() { public Shape build() {
Coordinate[] coordinates = points.toArray(new Coordinate[points.size()]); GeoPoint[] coordinates = points.toArray(new GeoPoint[points.size()]);
Geometry geometry; Geometry geometry;
if(wrapdateline) { if(wrapdateline) {
ArrayList<LineString> strings = decompose(FACTORY, coordinates, new ArrayList<LineString>()); ArrayList<LineString> strings = decompose(FACTORY, coordinates, new ArrayList<LineString>());
@ -67,9 +67,9 @@ public abstract class BaseLineStringBuilder<E extends BaseLineStringBuilder<E>>
return jtsGeometry(geometry); return jtsGeometry(geometry);
} }
protected static ArrayList<LineString> decompose(GeometryFactory factory, Coordinate[] coordinates, ArrayList<LineString> strings) { protected static ArrayList<LineString> decompose(GeometryFactory factory, GeoPoint[] coordinates, ArrayList<LineString> strings) {
for(Coordinate[] part : decompose(+DATELINE, coordinates)) { for(GeoPoint[] part : decompose(+DATELINE, coordinates)) {
for(Coordinate[] line : decompose(-DATELINE, part)) { for(GeoPoint[] line : decompose(-DATELINE, part)) {
strings.add(factory.createLineString(line)); strings.add(factory.createLineString(line));
} }
} }
@ -83,16 +83,16 @@ public abstract class BaseLineStringBuilder<E extends BaseLineStringBuilder<E>>
* @param coordinates coordinates forming the linestring * @param coordinates coordinates forming the linestring
* @return array of linestrings given as coordinate arrays * @return array of linestrings given as coordinate arrays
*/ */
protected static Coordinate[][] decompose(double dateline, Coordinate[] coordinates) { protected static GeoPoint[][] decompose(double dateline, GeoPoint[] coordinates) {
int offset = 0; int offset = 0;
ArrayList<Coordinate[]> parts = new ArrayList<>(); ArrayList<GeoPoint[]> parts = new ArrayList<>();
double shift = coordinates[0].x > DATELINE ? DATELINE : (coordinates[0].x < -DATELINE ? -DATELINE : 0); double shift = coordinates[0].x > DATELINE ? DATELINE : (coordinates[0].x < -DATELINE ? -DATELINE : 0);
for (int i = 1; i < coordinates.length; i++) { for (int i = 1; i < coordinates.length; i++) {
double t = intersection(coordinates[i-1], coordinates[i], dateline); double t = intersection(coordinates[i-1], coordinates[i], dateline);
if(!Double.isNaN(t)) { if(!Double.isNaN(t)) {
Coordinate[] part; GeoPoint[] part;
if(t<1) { if(t<1) {
part = Arrays.copyOfRange(coordinates, offset, i+1); part = Arrays.copyOfRange(coordinates, offset, i+1);
part[part.length-1] = Edge.position(coordinates[i-1], coordinates[i], t); part[part.length-1] = Edge.position(coordinates[i-1], coordinates[i], t);
@ -111,16 +111,16 @@ public abstract class BaseLineStringBuilder<E extends BaseLineStringBuilder<E>>
if(offset == 0) { if(offset == 0) {
parts.add(shift(shift, coordinates)); parts.add(shift(shift, coordinates));
} else if(offset < coordinates.length-1) { } else if(offset < coordinates.length-1) {
Coordinate[] part = Arrays.copyOfRange(coordinates, offset, coordinates.length); GeoPoint[] part = Arrays.copyOfRange(coordinates, offset, coordinates.length);
parts.add(shift(shift, part)); parts.add(shift(shift, part));
} }
return parts.toArray(new Coordinate[parts.size()][]); return parts.toArray(new GeoPoint[parts.size()][]);
} }
private static Coordinate[] shift(double shift, Coordinate...coordinates) { private static GeoPoint[] shift(double shift, GeoPoint...coordinates) {
if(shift != 0) { if(shift != 0) {
for (int j = 0; j < coordinates.length; j++) { for (int j = 0; j < coordinates.length; j++) {
coordinates[j] = new Coordinate(coordinates[j].x - 2 * shift, coordinates[j].y); coordinates[j] = new GeoPoint(coordinates[j].y, coordinates[j].x - 2 * shift);
} }
} }
return coordinates; return coordinates;

View File

@ -20,8 +20,14 @@
package org.elasticsearch.common.geo.builders; package org.elasticsearch.common.geo.builders;
import com.spatial4j.core.shape.Shape; import com.spatial4j.core.shape.Shape;
import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException; import java.io.IOException;
@ -67,7 +73,7 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
* @param coordinate coordinate of the new point * @param coordinate coordinate of the new point
* @return this * @return this
*/ */
public E point(Coordinate coordinate) { public E point(GeoPoint coordinate) {
shell.point(coordinate); shell.point(coordinate);
return thisRef(); return thisRef();
} }
@ -77,7 +83,7 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
* @param coordinates coordinates of the new points to add * @param coordinates coordinates of the new points to add
* @return this * @return this
*/ */
public E points(Coordinate...coordinates) { public E points(GeoPoint...coordinates) {
shell.points(coordinates); shell.points(coordinates);
return thisRef(); return thisRef();
} }
@ -121,7 +127,7 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
* *
* @return coordinates of the polygon * @return coordinates of the polygon
*/ */
public Coordinate[][][] coordinates() { public GeoPoint[][][] coordinates() {
int numEdges = shell.points.size()-1; // Last point is repeated int numEdges = shell.points.size()-1; // Last point is repeated
for (int i = 0; i < holes.size(); i++) { for (int i = 0; i < holes.size(); i++) {
numEdges += holes.get(i).points.size()-1; numEdges += holes.get(i).points.size()-1;
@ -170,7 +176,7 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
public Geometry buildGeometry(GeometryFactory factory, boolean fixDateline) { public Geometry buildGeometry(GeometryFactory factory, boolean fixDateline) {
if(fixDateline) { if(fixDateline) {
Coordinate[][][] polygons = coordinates(); GeoPoint[][][] polygons = coordinates();
return polygons.length == 1 return polygons.length == 1
? polygon(factory, polygons[0]) ? polygon(factory, polygons[0])
: multipolygon(factory, polygons); : multipolygon(factory, polygons);
@ -193,8 +199,8 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
return factory.createPolygon(shell, holes); return factory.createPolygon(shell, holes);
} }
protected static LinearRing linearRing(GeometryFactory factory, ArrayList<Coordinate> coordinates) { protected static LinearRing linearRing(GeometryFactory factory, ArrayList<GeoPoint> coordinates) {
return factory.createLinearRing(coordinates.toArray(new Coordinate[coordinates.size()])); return factory.createLinearRing(coordinates.toArray(new GeoPoint[coordinates.size()]));
} }
@Override @Override
@ -202,7 +208,7 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
return TYPE; return TYPE;
} }
protected static Polygon polygon(GeometryFactory factory, Coordinate[][] polygon) { protected static Polygon polygon(GeometryFactory factory, GeoPoint[][] polygon) {
LinearRing shell = factory.createLinearRing(polygon[0]); LinearRing shell = factory.createLinearRing(polygon[0]);
LinearRing[] holes; LinearRing[] holes;
@ -227,7 +233,7 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
* @param polygons definition of polygons * @param polygons definition of polygons
* @return a new Multipolygon * @return a new Multipolygon
*/ */
protected static MultiPolygon multipolygon(GeometryFactory factory, Coordinate[][][] polygons) { protected static MultiPolygon multipolygon(GeometryFactory factory, GeoPoint[][][] polygons) {
Polygon[] polygonSet = new Polygon[polygons.length]; Polygon[] polygonSet = new Polygon[polygons.length];
for (int i = 0; i < polygonSet.length; i++) { for (int i = 0; i < polygonSet.length; i++) {
polygonSet[i] = polygon(factory, polygons[i]); polygonSet[i] = polygon(factory, polygons[i]);
@ -283,18 +289,18 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
* @param coordinates Array of coordinates to write the result to * @param coordinates Array of coordinates to write the result to
* @return the coordinates parameter * @return the coordinates parameter
*/ */
private static Coordinate[] coordinates(Edge component, Coordinate[] coordinates) { private static GeoPoint[] coordinates(Edge component, GeoPoint[] coordinates) {
for (int i = 0; i < coordinates.length; i++) { for (int i = 0; i < coordinates.length; i++) {
coordinates[i] = (component = component.next).coordinate; coordinates[i] = (component = component.next).coordinate;
} }
return coordinates; return coordinates;
} }
private static Coordinate[][][] buildCoordinates(ArrayList<ArrayList<Coordinate[]>> components) { private static GeoPoint[][][] buildCoordinates(ArrayList<ArrayList<GeoPoint[]>> components) {
Coordinate[][][] result = new Coordinate[components.size()][][]; GeoPoint[][][] result = new GeoPoint[components.size()][][];
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.length; i++) {
ArrayList<Coordinate[]> component = components.get(i); ArrayList<GeoPoint[]> component = components.get(i);
result[i] = component.toArray(new Coordinate[component.size()][]); result[i] = component.toArray(new GeoPoint[component.size()][]);
} }
if(debugEnabled()) { if(debugEnabled()) {
@ -309,30 +315,30 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
return result; return result;
} }
private static final Coordinate[][] EMPTY = new Coordinate[0][]; private static final GeoPoint[][] EMPTY = new GeoPoint[0][];
private static Coordinate[][] holes(Edge[] holes, int numHoles) { private static GeoPoint[][] holes(Edge[] holes, int numHoles) {
if (numHoles == 0) { if (numHoles == 0) {
return EMPTY; return EMPTY;
} }
final Coordinate[][] points = new Coordinate[numHoles][]; final GeoPoint[][] points = new GeoPoint[numHoles][];
for (int i = 0; i < numHoles; i++) { for (int i = 0; i < numHoles; i++) {
int length = component(holes[i], -(i+1), null); // mark as visited by inverting the sign int length = component(holes[i], -(i+1), null); // mark as visited by inverting the sign
points[i] = coordinates(holes[i], new Coordinate[length+1]); points[i] = coordinates(holes[i], new GeoPoint[length+1]);
} }
return points; return points;
} }
private static Edge[] edges(Edge[] edges, int numHoles, ArrayList<ArrayList<Coordinate[]>> components) { private static Edge[] edges(Edge[] edges, int numHoles, ArrayList<ArrayList<GeoPoint[]>> components) {
ArrayList<Edge> mainEdges = new ArrayList<>(edges.length); ArrayList<Edge> mainEdges = new ArrayList<>(edges.length);
for (int i = 0; i < edges.length; i++) { for (int i = 0; i < edges.length; i++) {
if (edges[i].component >= 0) { if (edges[i].component >= 0) {
int length = component(edges[i], -(components.size()+numHoles+1), mainEdges); int length = component(edges[i], -(components.size()+numHoles+1), mainEdges);
ArrayList<Coordinate[]> component = new ArrayList<>(); ArrayList<GeoPoint[]> component = new ArrayList<>();
component.add(coordinates(edges[i], new Coordinate[length+1])); component.add(coordinates(edges[i], new GeoPoint[length+1]));
components.add(component); components.add(component);
} }
} }
@ -340,13 +346,14 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
return mainEdges.toArray(new Edge[mainEdges.size()]); return mainEdges.toArray(new Edge[mainEdges.size()]);
} }
private static Coordinate[][][] compose(Edge[] edges, Edge[] holes, int numHoles) { private static GeoPoint[][][] compose(Edge[] edges, Edge[] holes, int numHoles) {
final ArrayList<ArrayList<Coordinate[]>> components = new ArrayList<>(); final ArrayList<ArrayList<GeoPoint[]>> components = new ArrayList<>();
assign(holes, holes(holes, numHoles), numHoles, edges(edges, numHoles, components), components); assign(holes, holes(holes, numHoles), numHoles, edges(edges, numHoles, components), components);
return buildCoordinates(components); return buildCoordinates(components);
} }
private static void assign(Edge[] holes, Coordinate[][] points, int numHoles, Edge[] edges, ArrayList<ArrayList<Coordinate[]>> components) { private static void assign(Edge[] holes, GeoPoint[][] points, int numHoles, Edge[] edges, ArrayList<ArrayList<GeoPoint[]>>
components) {
// Assign Hole to related components // Assign Hole to related components
// To find the new component the hole belongs to all intersections of the // To find the new component the hole belongs to all intersections of the
// polygon edges with a vertical line are calculated. This vertical line // polygon edges with a vertical line are calculated. This vertical line
@ -461,14 +468,13 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
} }
private static int createEdges(int component, Orientation orientation, BaseLineStringBuilder<?> shell, private static int createEdges(int component, Orientation orientation, BaseLineStringBuilder<?> shell,
BaseLineStringBuilder<?> hole, BaseLineStringBuilder<?> hole, Edge[] edges, int edgeOffset) {
Edge[] edges, int offset) {
// inner rings (holes) have an opposite direction than the outer rings // inner rings (holes) have an opposite direction than the outer rings
// XOR will invert the orientation for outer ring cases (Truth Table:, T/T = F, T/F = T, F/T = T, F/F = F) // XOR will invert the orientation for outer ring cases (Truth Table:, T/T = F, T/F = T, F/T = T, F/F = F)
boolean direction = (component != 0 ^ orientation == Orientation.RIGHT); boolean direction = (component != 0 ^ orientation == Orientation.RIGHT);
// set the points array accordingly (shell or hole) // set the points array accordingly (shell or hole)
Coordinate[] points = (hole != null) ? hole.coordinates(false) : shell.coordinates(false); GeoPoint[] points = (hole != null) ? hole.coordinates(false) : shell.coordinates(false);
Edge.ring(component, direction, orientation == Orientation.LEFT, shell, points, 0, edges, offset, points.length-1); Edge.ring(component, direction, orientation == Orientation.LEFT, shell, points, edges, edgeOffset, points.length-1);
return points.length-1; return points.length-1;
} }
@ -477,17 +483,17 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
private final P parent; private final P parent;
protected Ring(P parent) { protected Ring(P parent) {
this(parent, new ArrayList<Coordinate>()); this(parent, new ArrayList<GeoPoint>());
} }
protected Ring(P parent, ArrayList<Coordinate> points) { protected Ring(P parent, ArrayList<GeoPoint> points) {
super(points); super(points);
this.parent = parent; this.parent = parent;
} }
public P close() { public P close() {
Coordinate start = points.get(0); GeoPoint start = points.get(0);
Coordinate end = points.get(points.size()-1); GeoPoint end = points.get(points.size()-1);
if(start.x != end.x || start.y != end.y) { if(start.x != end.x || start.y != end.y) {
points.add(start); points.add(start);
} }

View File

@ -20,7 +20,7 @@
package org.elasticsearch.common.geo.builders; package org.elasticsearch.common.geo.builders;
import com.spatial4j.core.shape.Circle; import com.spatial4j.core.shape.Circle;
import com.vividsolutions.jts.geom.Coordinate; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.unit.DistanceUnit.Distance; import org.elasticsearch.common.unit.DistanceUnit.Distance;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -34,7 +34,7 @@ public class CircleBuilder extends ShapeBuilder {
private DistanceUnit unit; private DistanceUnit unit;
private double radius; private double radius;
private Coordinate center; private GeoPoint center;
/** /**
* Set the center of the circle * Set the center of the circle
@ -42,7 +42,7 @@ public class CircleBuilder extends ShapeBuilder {
* @param center coordinate of the circles center * @param center coordinate of the circles center
* @return this * @return this
*/ */
public CircleBuilder center(Coordinate center) { public CircleBuilder center(GeoPoint center) {
this.center = center; this.center = center;
return this; return this;
} }
@ -54,7 +54,7 @@ public class CircleBuilder extends ShapeBuilder {
* @return this * @return this
*/ */
public CircleBuilder center(double lon, double lat) { public CircleBuilder center(double lon, double lat) {
return center(new Coordinate(lon, lat)); return center(new GeoPoint(lat, lon));
} }
/** /**

View File

@ -20,7 +20,7 @@
package org.elasticsearch.common.geo.builders; package org.elasticsearch.common.geo.builders;
import com.spatial4j.core.shape.Rectangle; import com.spatial4j.core.shape.Rectangle;
import com.vividsolutions.jts.geom.Coordinate; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException; import java.io.IOException;
@ -29,8 +29,8 @@ public class EnvelopeBuilder extends ShapeBuilder {
public static final GeoShapeType TYPE = GeoShapeType.ENVELOPE; public static final GeoShapeType TYPE = GeoShapeType.ENVELOPE;
protected Coordinate topLeft; protected GeoPoint topLeft;
protected Coordinate bottomRight; protected GeoPoint bottomRight;
public EnvelopeBuilder() { public EnvelopeBuilder() {
this(Orientation.RIGHT); this(Orientation.RIGHT);
@ -40,7 +40,7 @@ public class EnvelopeBuilder extends ShapeBuilder {
super(orientation); super(orientation);
} }
public EnvelopeBuilder topLeft(Coordinate topLeft) { public EnvelopeBuilder topLeft(GeoPoint topLeft) {
this.topLeft = topLeft; this.topLeft = topLeft;
return this; return this;
} }
@ -49,7 +49,7 @@ public class EnvelopeBuilder extends ShapeBuilder {
return topLeft(coordinate(longitude, latitude)); return topLeft(coordinate(longitude, latitude));
} }
public EnvelopeBuilder bottomRight(Coordinate bottomRight) { public EnvelopeBuilder bottomRight(GeoPoint bottomRight) {
this.bottomRight = bottomRight; this.bottomRight = bottomRight;
return this; return this;
} }

View File

@ -19,11 +19,10 @@
package org.elasticsearch.common.geo.builders; package org.elasticsearch.common.geo.builders;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import com.spatial4j.core.shape.Shape; import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.jts.JtsGeometry;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LineString;
@ -48,8 +47,8 @@ public class MultiLineStringBuilder extends ShapeBuilder {
return this; return this;
} }
public Coordinate[][] coordinates() { public GeoPoint[][] coordinates() {
Coordinate[][] result = new Coordinate[lines.size()][]; GeoPoint[][] result = new GeoPoint[lines.size()][];
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.length; i++) {
result[i] = lines.get(i).coordinates(false); result[i] = lines.get(i).coordinates(false);
} }
@ -113,7 +112,7 @@ public class MultiLineStringBuilder extends ShapeBuilder {
return collection; return collection;
} }
public Coordinate[] coordinates() { public GeoPoint[] coordinates() {
return super.coordinates(false); return super.coordinates(false);
} }

View File

@ -22,7 +22,7 @@ package org.elasticsearch.common.geo.builders;
import com.spatial4j.core.shape.Point; import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape; import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.ShapeCollection; import com.spatial4j.core.shape.ShapeCollection;
import com.vividsolutions.jts.geom.Coordinate; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException; import java.io.IOException;
@ -48,7 +48,7 @@ public class MultiPointBuilder extends PointCollection<MultiPointBuilder> {
//Could wrap JtsGeometry but probably slower due to conversions to/from JTS in relate() //Could wrap JtsGeometry but probably slower due to conversions to/from JTS in relate()
//MultiPoint geometry = FACTORY.createMultiPoint(points.toArray(new Coordinate[points.size()])); //MultiPoint geometry = FACTORY.createMultiPoint(points.toArray(new Coordinate[points.size()]));
List<Point> shapes = new ArrayList<>(points.size()); List<Point> shapes = new ArrayList<>(points.size());
for (Coordinate coord : points) { for (GeoPoint coord : points) {
shapes.add(SPATIAL_CONTEXT.makePoint(coord.x, coord.y)); shapes.add(SPATIAL_CONTEXT.makePoint(coord.x, coord.y));
} }
return new ShapeCollection<>(shapes, SPATIAL_CONTEXT); return new ShapeCollection<>(shapes, SPATIAL_CONTEXT);

View File

@ -24,10 +24,10 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.spatial4j.core.shape.ShapeCollection; import com.spatial4j.core.shape.ShapeCollection;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import com.spatial4j.core.shape.Shape; import com.spatial4j.core.shape.Shape;
import com.vividsolutions.jts.geom.Coordinate;
public class MultiPolygonBuilder extends ShapeBuilder { public class MultiPolygonBuilder extends ShapeBuilder {
@ -84,7 +84,7 @@ public class MultiPolygonBuilder extends ShapeBuilder {
if(wrapdateline) { if(wrapdateline) {
for (BasePolygonBuilder<?> polygon : this.polygons) { for (BasePolygonBuilder<?> polygon : this.polygons) {
for(Coordinate[][] part : polygon.coordinates()) { for(GeoPoint[][] part : polygon.coordinates()) {
shapes.add(jtsGeometry(PolygonBuilder.polygon(FACTORY, part))); shapes.add(jtsGeometry(PolygonBuilder.polygon(FACTORY, part)));
} }
} }

View File

@ -21,18 +21,18 @@ package org.elasticsearch.common.geo.builders;
import java.io.IOException; import java.io.IOException;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import com.spatial4j.core.shape.Point; import com.spatial4j.core.shape.Point;
import com.vividsolutions.jts.geom.Coordinate;
public class PointBuilder extends ShapeBuilder { public class PointBuilder extends ShapeBuilder {
public static final GeoShapeType TYPE = GeoShapeType.POINT; public static final GeoShapeType TYPE = GeoShapeType.POINT;
private Coordinate coordinate; private GeoPoint coordinate;
public PointBuilder coordinate(Coordinate coordinate) { public PointBuilder coordinate(GeoPoint coordinate) {
this.coordinate = coordinate; this.coordinate = coordinate;
return this; return this;
} }

View File

@ -24,23 +24,22 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import com.vividsolutions.jts.geom.Coordinate;
/** /**
* The {@link PointCollection} is an abstract base implementation for all GeoShapes. It simply handles a set of points. * The {@link PointCollection} is an abstract base implementation for all GeoShapes. It simply handles a set of points.
*/ */
public abstract class PointCollection<E extends PointCollection<E>> extends ShapeBuilder { public abstract class PointCollection<E extends PointCollection<E>> extends ShapeBuilder {
protected final ArrayList<Coordinate> points; protected final ArrayList<GeoPoint> points;
protected boolean translated = false; protected boolean translated = false;
protected PointCollection() { protected PointCollection() {
this(new ArrayList<Coordinate>()); this(new ArrayList<GeoPoint>());
} }
protected PointCollection(ArrayList<Coordinate> points) { protected PointCollection(ArrayList<GeoPoint> points) {
this.points = points; this.points = points;
} }
@ -64,7 +63,7 @@ public abstract class PointCollection<E extends PointCollection<E>> extends Shap
* @param coordinate coordinate of the point * @param coordinate coordinate of the point
* @return this * @return this
*/ */
public E point(Coordinate coordinate) { public E point(GeoPoint coordinate) {
this.points.add(coordinate); this.points.add(coordinate);
return thisRef(); return thisRef();
} }
@ -72,20 +71,20 @@ public abstract class PointCollection<E extends PointCollection<E>> extends Shap
/** /**
* Add a array of points to the collection * Add a array of points to the collection
* *
* @param coordinates array of {@link Coordinate}s to add * @param coordinates array of {@link GeoPoint}s to add
* @return this * @return this
*/ */
public E points(Coordinate...coordinates) { public E points(GeoPoint...coordinates) {
return this.points(Arrays.asList(coordinates)); return this.points(Arrays.asList(coordinates));
} }
/** /**
* Add a collection of points to the collection * Add a collection of points to the collection
* *
* @param coordinates array of {@link Coordinate}s to add * @param coordinates array of {@link GeoPoint}s to add
* @return this * @return this
*/ */
public E points(Collection<? extends Coordinate> coordinates) { public E points(Collection<? extends GeoPoint> coordinates) {
this.points.addAll(coordinates); this.points.addAll(coordinates);
return thisRef(); return thisRef();
} }
@ -96,8 +95,8 @@ public abstract class PointCollection<E extends PointCollection<E>> extends Shap
* @param closed if set to true the first point of the array is repeated as last element * @param closed if set to true the first point of the array is repeated as last element
* @return Array of coordinates * @return Array of coordinates
*/ */
protected Coordinate[] coordinates(boolean closed) { protected GeoPoint[] coordinates(boolean closed) {
Coordinate[] result = points.toArray(new Coordinate[points.size() + (closed?1:0)]); GeoPoint[] result = points.toArray(new GeoPoint[points.size() + (closed?1:0)]);
if(closed) { if(closed) {
result[result.length-1] = result[0]; result[result.length-1] = result[0];
} }
@ -114,12 +113,12 @@ public abstract class PointCollection<E extends PointCollection<E>> extends Shap
*/ */
protected XContentBuilder coordinatesToXcontent(XContentBuilder builder, boolean closed) throws IOException { protected XContentBuilder coordinatesToXcontent(XContentBuilder builder, boolean closed) throws IOException {
builder.startArray(); builder.startArray();
for(Coordinate point : points) { for(GeoPoint point : points) {
toXContent(builder, point); toXContent(builder, point);
} }
if(closed) { if(closed) {
Coordinate start = points.get(0); GeoPoint start = points.get(0);
Coordinate end = points.get(points.size()-1); GeoPoint end = points.get(points.size()-1);
if(start.x != end.x || start.y != end.y) { if(start.x != end.x || start.y != end.y) {
toXContent(builder, points.get(0)); toXContent(builder, points.get(0));
} }

View File

@ -21,19 +21,19 @@ package org.elasticsearch.common.geo.builders;
import java.util.ArrayList; import java.util.ArrayList;
import com.vividsolutions.jts.geom.Coordinate; import org.elasticsearch.common.geo.GeoPoint;
public class PolygonBuilder extends BasePolygonBuilder<PolygonBuilder> { public class PolygonBuilder extends BasePolygonBuilder<PolygonBuilder> {
public PolygonBuilder() { public PolygonBuilder() {
this(new ArrayList<Coordinate>(), Orientation.RIGHT); this(new ArrayList<GeoPoint>(), Orientation.RIGHT);
} }
public PolygonBuilder(Orientation orientation) { public PolygonBuilder(Orientation orientation) {
this(new ArrayList<Coordinate>(), orientation); this(new ArrayList<GeoPoint>(), orientation);
} }
protected PolygonBuilder(ArrayList<Coordinate> points, Orientation orientation) { protected PolygonBuilder(ArrayList<GeoPoint> points, Orientation orientation) {
super(orientation); super(orientation);
this.shell = new Ring<>(this, points); this.shell = new Ring<>(this, points);
} }

View File

@ -22,12 +22,12 @@ package org.elasticsearch.common.geo.builders;
import com.spatial4j.core.context.jts.JtsSpatialContext; import com.spatial4j.core.context.jts.JtsSpatialContext;
import com.spatial4j.core.shape.Shape; import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.jts.JtsGeometry; import com.spatial4j.core.shape.jts.JtsGeometry;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.GeometryFactory;
import org.apache.commons.lang3.tuple.Pair;
import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.unit.DistanceUnit.Distance; import org.elasticsearch.common.unit.DistanceUnit.Distance;
@ -57,7 +57,7 @@ public abstract class ShapeBuilder implements ToXContent {
DEBUG = debug; DEBUG = debug;
} }
public static final double DATELINE = 180; public static final double DATELINE = GeoUtils.DATELINE;
// TODO how might we use JtsSpatialContextFactory to configure the context (esp. for non-geo)? // TODO how might we use JtsSpatialContextFactory to configure the context (esp. for non-geo)?
public static final JtsSpatialContext SPATIAL_CONTEXT = JtsSpatialContext.GEO; public static final JtsSpatialContext SPATIAL_CONTEXT = JtsSpatialContext.GEO;
public static final GeometryFactory FACTORY = SPATIAL_CONTEXT.getGeometryFactory(); public static final GeometryFactory FACTORY = SPATIAL_CONTEXT.getGeometryFactory();
@ -84,8 +84,8 @@ public abstract class ShapeBuilder implements ToXContent {
this.orientation = orientation; this.orientation = orientation;
} }
protected static Coordinate coordinate(double longitude, double latitude) { protected static GeoPoint coordinate(double longitude, double latitude) {
return new Coordinate(longitude, latitude); return new GeoPoint(latitude, longitude);
} }
protected JtsGeometry jtsGeometry(Geometry geom) { protected JtsGeometry jtsGeometry(Geometry geom) {
@ -106,15 +106,15 @@ public abstract class ShapeBuilder implements ToXContent {
* @return a new {@link PointBuilder} * @return a new {@link PointBuilder}
*/ */
public static PointBuilder newPoint(double longitude, double latitude) { public static PointBuilder newPoint(double longitude, double latitude) {
return newPoint(new Coordinate(longitude, latitude)); return newPoint(new GeoPoint(latitude, longitude));
} }
/** /**
* Create a new {@link PointBuilder} from a {@link Coordinate} * Create a new {@link PointBuilder} from a {@link GeoPoint}
* @param coordinate coordinate defining the position of the point * @param coordinate coordinate defining the position of the point
* @return a new {@link PointBuilder} * @return a new {@link PointBuilder}
*/ */
public static PointBuilder newPoint(Coordinate coordinate) { public static PointBuilder newPoint(GeoPoint coordinate) {
return new PointBuilder().coordinate(coordinate); return new PointBuilder().coordinate(coordinate);
} }
@ -250,7 +250,7 @@ public abstract class ShapeBuilder implements ToXContent {
token = parser.nextToken(); token = parser.nextToken();
double lat = parser.doubleValue(); double lat = parser.doubleValue();
token = parser.nextToken(); token = parser.nextToken();
return new CoordinateNode(new Coordinate(lon, lat)); return new CoordinateNode(new GeoPoint(lat, lon));
} else if (token == XContentParser.Token.VALUE_NULL) { } else if (token == XContentParser.Token.VALUE_NULL) {
throw new ElasticsearchIllegalArgumentException("coordinates cannot contain NULL values)"); throw new ElasticsearchIllegalArgumentException("coordinates cannot contain NULL values)");
} }
@ -289,7 +289,7 @@ public abstract class ShapeBuilder implements ToXContent {
return GeoShapeType.parse(parser, geoDocMapper); return GeoShapeType.parse(parser, geoDocMapper);
} }
protected static XContentBuilder toXContent(XContentBuilder builder, Coordinate coordinate) throws IOException { protected static XContentBuilder toXContent(XContentBuilder builder, GeoPoint coordinate) throws IOException {
return builder.startArray().value(coordinate.x).value(coordinate.y).endArray(); return builder.startArray().value(coordinate.x).value(coordinate.y).endArray();
} }
@ -309,11 +309,11 @@ public abstract class ShapeBuilder implements ToXContent {
} }
} }
protected static Coordinate shift(Coordinate coordinate, double dateline) { protected static GeoPoint shift(GeoPoint coordinate, double dateline) {
if (dateline == 0) { if (dateline == 0) {
return coordinate; return coordinate;
} else { } else {
return new Coordinate(-2 * dateline + coordinate.x, coordinate.y); return new GeoPoint(coordinate.y, -2 * dateline + coordinate.x);
} }
} }
@ -325,7 +325,7 @@ public abstract class ShapeBuilder implements ToXContent {
/** /**
* Calculate the intersection of a line segment and a vertical dateline. * Calculate the intersection of a line segment and a vertical dateline.
* *
* @param p1 * @param p1
* start-point of the line segment * start-point of the line segment
* @param p2 * @param p2
@ -336,7 +336,7 @@ public abstract class ShapeBuilder implements ToXContent {
* segment intersects with the line segment. Otherwise this method * segment intersects with the line segment. Otherwise this method
* returns {@link Double#NaN} * returns {@link Double#NaN}
*/ */
protected static final double intersection(Coordinate p1, Coordinate p2, double dateline) { protected static final double intersection(GeoPoint p1, GeoPoint p2, double dateline) {
if (p1.x == p2.x && p1.x != dateline) { if (p1.x == p2.x && p1.x != dateline) {
return Double.NaN; return Double.NaN;
} else if (p1.x == p2.x && p1.x == dateline) { } else if (p1.x == p2.x && p1.x == dateline) {
@ -366,8 +366,8 @@ public abstract class ShapeBuilder implements ToXContent {
int numIntersections = 0; int numIntersections = 0;
assert !Double.isNaN(dateline); assert !Double.isNaN(dateline);
for (int i = 0; i < edges.length; i++) { for (int i = 0; i < edges.length; i++) {
Coordinate p1 = edges[i].coordinate; GeoPoint p1 = edges[i].coordinate;
Coordinate p2 = edges[i].next.coordinate; GeoPoint p2 = edges[i].next.coordinate;
assert !Double.isNaN(p2.x) && !Double.isNaN(p1.x); assert !Double.isNaN(p2.x) && !Double.isNaN(p1.x);
edges[i].intersect = Edge.MAX_COORDINATE; edges[i].intersect = Edge.MAX_COORDINATE;
@ -384,21 +384,21 @@ public abstract class ShapeBuilder implements ToXContent {
/** /**
* Node used to represent a tree of coordinates. * Node used to represent a tree of coordinates.
* <p/> * <p/>
* Can either be a leaf node consisting of a Coordinate, or a parent with * Can either be a leaf node consisting of a GeoPoint, or a parent with
* children * children
*/ */
protected static class CoordinateNode implements ToXContent { protected static class CoordinateNode implements ToXContent {
protected final Coordinate coordinate; protected final GeoPoint coordinate;
protected final List<CoordinateNode> children; protected final List<CoordinateNode> children;
/** /**
* Creates a new leaf CoordinateNode * Creates a new leaf CoordinateNode
* *
* @param coordinate * @param coordinate
* Coordinate for the Node * GeoPoint for the Node
*/ */
protected CoordinateNode(Coordinate coordinate) { protected CoordinateNode(GeoPoint coordinate) {
this.coordinate = coordinate; this.coordinate = coordinate;
this.children = null; this.children = null;
} }
@ -434,17 +434,17 @@ public abstract class ShapeBuilder implements ToXContent {
} }
/** /**
* This helper class implements a linked list for {@link Coordinate}. It contains * This helper class implements a linked list for {@link GeoPoint}. It contains
* fields for a dateline intersection and component id * fields for a dateline intersection and component id
*/ */
protected static final class Edge { protected static final class Edge {
Coordinate coordinate; // coordinate of the start point GeoPoint coordinate; // coordinate of the start point
Edge next; // next segment Edge next; // next segment
Coordinate intersect; // potential intersection with dateline GeoPoint intersect; // potential intersection with dateline
int component = -1; // id of the component this edge belongs to int component = -1; // id of the component this edge belongs to
public static final Coordinate MAX_COORDINATE = new Coordinate(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); public static final GeoPoint MAX_COORDINATE = new GeoPoint(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
protected Edge(Coordinate coordinate, Edge next, Coordinate intersection) { protected Edge(GeoPoint coordinate, Edge next, GeoPoint intersection) {
this.coordinate = coordinate; this.coordinate = coordinate;
this.next = next; this.next = next;
this.intersect = intersection; this.intersect = intersection;
@ -453,11 +453,11 @@ public abstract class ShapeBuilder implements ToXContent {
} }
} }
protected Edge(Coordinate coordinate, Edge next) { protected Edge(GeoPoint coordinate, Edge next) {
this(coordinate, next, Edge.MAX_COORDINATE); this(coordinate, next, Edge.MAX_COORDINATE);
} }
private static final int top(Coordinate[] points, int offset, int length) { private static final int top(GeoPoint[] points, int offset, int length) {
int top = 0; // we start at 1 here since top points to 0 int top = 0; // we start at 1 here since top points to 0
for (int i = 1; i < length; i++) { for (int i = 1; i < length; i++) {
if (points[offset + i].y < points[offset + top].y) { if (points[offset + i].y < points[offset + top].y) {
@ -471,29 +471,6 @@ public abstract class ShapeBuilder implements ToXContent {
return top; return top;
} }
private static final Pair range(Coordinate[] points, int offset, int length) {
double minX = points[0].x;
double maxX = points[0].x;
double minY = points[0].y;
double maxY = points[0].y;
// compute the bounding coordinates (@todo: cleanup brute force)
for (int i = 1; i < length; ++i) {
if (points[offset + i].x < minX) {
minX = points[offset + i].x;
}
if (points[offset + i].x > maxX) {
maxX = points[offset + i].x;
}
if (points[offset + i].y < minY) {
minY = points[offset + i].y;
}
if (points[offset + i].y > maxY) {
maxY = points[offset + i].y;
}
}
return Pair.of(Pair.of(minX, maxX), Pair.of(minY, maxY));
}
/** /**
* Concatenate a set of points to a polygon * Concatenate a set of points to a polygon
* *
@ -503,8 +480,6 @@ public abstract class ShapeBuilder implements ToXContent {
* direction of the ring * direction of the ring
* @param points * @param points
* list of points to concatenate * list of points to concatenate
* @param pointOffset
* index of the first point
* @param edges * @param edges
* Array of edges to write the result to * Array of edges to write the result to
* @param edgeOffset * @param edgeOffset
@ -513,27 +488,29 @@ public abstract class ShapeBuilder implements ToXContent {
* number of points to use * number of points to use
* @return the edges creates * @return the edges creates
*/ */
private static Edge[] concat(int component, boolean direction, Coordinate[] points, final int pointOffset, Edge[] edges, final int edgeOffset, private static Edge[] concat(int component, boolean direction, GeoPoint[] points, Edge[] edges, final int edgeOffset,
int length) { int length) {
assert edges.length >= length+edgeOffset; assert edges.length >= length+edgeOffset;
assert points.length >= length+pointOffset; assert points.length >= length;
edges[edgeOffset] = new Edge(points[pointOffset], null); edges[edgeOffset] = new Edge(points[0], null);
for (int i = 1; i < length; i++) { int edgeEnd = edgeOffset + length;
for (int i = edgeOffset+1, p = 1; i < edgeEnd; ++i, ++p) {
if (direction) { if (direction) {
edges[edgeOffset + i] = new Edge(points[pointOffset + i], edges[edgeOffset + i - 1]); edges[i] = new Edge(points[p], edges[i - 1]);
edges[edgeOffset + i].component = component; edges[i].component = component;
} else { } else {
edges[edgeOffset + i - 1].next = edges[edgeOffset + i] = new Edge(points[pointOffset + i], null); edges[i - 1].next = edges[i] = new Edge(points[p], null);
edges[edgeOffset + i - 1].component = component; edges[i - 1].component = component;
} }
} }
if (direction) { if (direction) {
edges[edgeOffset].next = edges[edgeOffset + length - 1]; edges[edgeOffset].next = edges[edgeEnd - 1];
edges[edgeOffset].component = component; edges[edgeOffset].component = component;
} else { } else {
edges[edgeOffset + length - 1].next = edges[edgeOffset]; edges[edgeEnd - 1].next = edges[edgeOffset];
edges[edgeOffset + length - 1].component = component; edges[edgeEnd - 1].component = component;
} }
return edges; return edges;
@ -544,60 +521,25 @@ public abstract class ShapeBuilder implements ToXContent {
* *
* @param points * @param points
* array of point * array of point
* @param offset
* index of the first point
* @param length * @param length
* number of points * number of points
* @return Array of edges * @return Array of edges
*/ */
protected static Edge[] ring(int component, boolean direction, boolean handedness, BaseLineStringBuilder<?> shell, protected static Edge[] ring(int component, boolean direction, boolean handedness, BaseLineStringBuilder<?> shell,
Coordinate[] points, int offset, Edge[] edges, int toffset, int length) { GeoPoint[] points, Edge[] edges, int edgeOffset, int length) {
// calculate the direction of the points: // calculate the direction of the points:
// find the point a the top of the set and check its boolean orientation = GeoUtils.computePolyOrientation(points, length);
// neighbors orientation. So direction is equivalent boolean corrected = GeoUtils.correctPolyAmbiguity(points, handedness, orientation, component, length,
// to clockwise/counterclockwise shell.translated);
final int top = top(points, offset, length);
final int prev = (offset + ((top + length - 1) % length));
final int next = (offset + ((top + 1) % length));
boolean orientation = points[offset + prev].x > points[offset + next].x;
// OGC requires shell as ccw (Right-Handedness) and holes as cw (Left-Handedness) // correct the orientation post translation (ccw for shell, cw for holes)
// since GeoJSON doesn't specify (and doesn't need to) GEO core will assume OGC standards if (corrected && (component == 0 || (component != 0 && handedness == orientation))) {
// thus if orientation is computed as cw, the logic will translate points across dateline
// and convert to a right handed system
// compute the bounding box and calculate range
Pair<Pair, Pair> range = range(points, offset, length);
final double rng = (Double)range.getLeft().getRight() - (Double)range.getLeft().getLeft();
// translate the points if the following is true
// 1. shell orientation is cw and range is greater than a hemisphere (180 degrees) but not spanning 2 hemispheres
// (translation would result in a collapsed poly)
// 2. the shell of the candidate hole has been translated (to preserve the coordinate system)
boolean incorrectOrientation = component == 0 && handedness != orientation;
if ( (incorrectOrientation && (rng > DATELINE && rng != 2*DATELINE)) || (shell.translated && component != 0)) {
translate(points);
// flip the translation bit if the shell is being translated
if (component == 0) { if (component == 0) {
shell.translated = true; shell.translated = corrected;
}
// correct the orientation post translation (ccw for shell, cw for holes)
if (component == 0 || (component != 0 && handedness == orientation)) {
orientation = !orientation;
}
}
return concat(component, direction ^ orientation, points, offset, edges, toffset, length);
}
/**
* Transforms coordinates in the eastern hemisphere (-180:0) to a (180:360) range
* @param points
*/
protected static void translate(Coordinate[] points) {
for (Coordinate c : points) {
if (c.x < 0) {
c.x += 2*DATELINE;
} }
orientation = !orientation;
} }
return concat(component, direction ^ orientation, points, edges, edgeOffset, length);
} }
/** /**
@ -605,13 +547,13 @@ public abstract class ShapeBuilder implements ToXContent {
* *
* @param position * @param position
* position of the intersection [0..1] * position of the intersection [0..1]
* @return the {@link Coordinate} of the intersection * @return the {@link GeoPoint} of the intersection
*/ */
protected Coordinate intersection(double position) { protected GeoPoint intersection(double position) {
return intersect = position(coordinate, next.coordinate, position); return intersect = position(coordinate, next.coordinate, position);
} }
public static Coordinate position(Coordinate p1, Coordinate p2, double position) { public static GeoPoint position(GeoPoint p1, GeoPoint p2, double position) {
if (position == 0) { if (position == 0) {
return p1; return p1;
} else if (position == 1) { } else if (position == 1) {
@ -619,7 +561,7 @@ public abstract class ShapeBuilder implements ToXContent {
} else { } else {
final double x = p1.x + position * (p2.x - p1.x); final double x = p1.x + position * (p2.x - p1.x);
final double y = p1.y + position * (p2.y - p1.y); final double y = p1.y + position * (p2.y - p1.y);
return new Coordinate(x, y); return new GeoPoint(y, x);
} }
} }
@ -793,12 +735,12 @@ public abstract class ShapeBuilder implements ToXContent {
"geo_shape ('envelope') when expecting an array of 2 coordinates"); "geo_shape ('envelope') when expecting an array of 2 coordinates");
} }
// verify coordinate bounds, correct if necessary // verify coordinate bounds, correct if necessary
Coordinate uL = coordinates.children.get(0).coordinate; GeoPoint uL = coordinates.children.get(0).coordinate;
Coordinate lR = coordinates.children.get(1).coordinate; GeoPoint lR = coordinates.children.get(1).coordinate;
if (((lR.x < uL.x) || (uL.y < lR.y))) { if (((lR.x < uL.x) || (uL.y < lR.y))) {
Coordinate uLtmp = uL; GeoPoint uLtmp = uL;
uL = new Coordinate(Math.min(uL.x, lR.x), Math.max(uL.y, lR.y)); uL = new GeoPoint(Math.max(uL.y, lR.y), Math.min(uL.x, lR.x));
lR = new Coordinate(Math.max(uLtmp.x, lR.x), Math.min(uLtmp.y, lR.y)); lR = new GeoPoint(Math.min(uLtmp.y, lR.y), Math.max(uLtmp.x, lR.x));
} }
return newEnvelope(orientation).topLeft(uL).bottomRight(lR); return newEnvelope(orientation).topLeft(uL).bottomRight(lR);
} }

View File

@ -294,6 +294,10 @@ public class GeoShapeFieldMapper extends AbstractFieldMapper<String> {
if (includeDefaults || defaultStrategy.getDistErrPct() != Defaults.DISTANCE_ERROR_PCT) { if (includeDefaults || defaultStrategy.getDistErrPct() != Defaults.DISTANCE_ERROR_PCT) {
builder.field(Names.DISTANCE_ERROR_PCT, defaultStrategy.getDistErrPct()); builder.field(Names.DISTANCE_ERROR_PCT, defaultStrategy.getDistErrPct());
} }
if (includeDefaults || shapeOrientation != Defaults.ORIENTATION ) {
builder.field(Names.ORIENTATION, shapeOrientation);
}
} }
@Override @Override

View File

@ -29,6 +29,7 @@ import org.apache.lucene.search.Filter;
import org.apache.lucene.util.Bits; import org.apache.lucene.util.Bits;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.index.fielddata.IndexGeoPointFieldData; import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
import org.elasticsearch.index.fielddata.MultiGeoPointValues; import org.elasticsearch.index.fielddata.MultiGeoPointValues;
@ -93,15 +94,27 @@ public class GeoPolygonFilter extends Filter {
private static boolean pointInPolygon(GeoPoint[] points, double lat, double lon) { private static boolean pointInPolygon(GeoPoint[] points, double lat, double lon) {
boolean inPoly = false; boolean inPoly = false;
// @TODO handedness will be an option provided by the parser
boolean corrected = GeoUtils.correctPolyAmbiguity(points, false);
GeoPoint p = (corrected) ?
GeoUtils.convertToGreatCircle(lat, lon) :
new GeoPoint(lat, lon);
GeoPoint pp0 = (corrected) ? GeoUtils.convertToGreatCircle(points[0]) : points[0] ;
GeoPoint pp1;
// simple even-odd PIP computation
// 1. Determine if point is contained in the longitudinal range
// 2. Determine whether point crosses the edge by computing the latitudinal delta
// between the end-point of a parallel vector (originating at the point) and the
// y-component of the edge sink
for (int i = 1; i < points.length; i++) { for (int i = 1; i < points.length; i++) {
if (points[i].lon() < lon && points[i-1].lon() >= lon pp1 = points[i];
|| points[i-1].lon() < lon && points[i].lon() >= lon) { if (pp1.x < p.x && pp0.x >= p.x || pp0.x < p.x && pp1.x >= p.x) {
if (points[i].lat() + (lon - points[i].lon()) / if (pp1.y + (p.x - pp1.x) / (pp0.x - pp1.x) * (pp0.y - pp1.y) < p.y) {
(points[i-1].lon() - points[i].lon()) * (points[i-1].lat() - points[i].lat()) < lat) {
inPoly = !inPoly; inPoly = !inPoly;
} }
} }
pp0 = pp1;
} }
return inPoly; return inPoly;
} }

View File

@ -26,7 +26,13 @@ import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.ShapeCollection; import com.spatial4j.core.shape.ShapeCollection;
import com.spatial4j.core.shape.jts.JtsGeometry; import com.spatial4j.core.shape.jts.JtsGeometry;
import com.spatial4j.core.shape.jts.JtsPoint; import com.spatial4j.core.shape.jts.JtsPoint;
import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
@ -57,7 +63,7 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
.startArray("coordinates").value(100.0).value(0.0).endArray() .startArray("coordinates").value(100.0).value(0.0).endArray()
.endObject().string(); .endObject().string();
Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0)); Point expected = GEOMETRY_FACTORY.createPoint(new GeoPoint(0.0, 100.0));
assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson); assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson);
} }
@ -69,12 +75,12 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
.endArray() .endArray()
.endObject().string(); .endObject().string();
List<Coordinate> lineCoordinates = new ArrayList<>(); List<GeoPoint> lineCoordinates = new ArrayList<>();
lineCoordinates.add(new Coordinate(100, 0)); lineCoordinates.add(new GeoPoint(0, 100));
lineCoordinates.add(new Coordinate(101, 1)); lineCoordinates.add(new GeoPoint(1, 101));
LineString expected = GEOMETRY_FACTORY.createLineString( LineString expected = GEOMETRY_FACTORY.createLineString(
lineCoordinates.toArray(new Coordinate[lineCoordinates.size()])); lineCoordinates.toArray(new GeoPoint[lineCoordinates.size()]));
assertGeometryEquals(jtsGeom(expected), lineGeoJson); assertGeometryEquals(jtsGeom(expected), lineGeoJson);
} }
@ -93,13 +99,13 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
.endObject().string(); .endObject().string();
MultiLineString expected = GEOMETRY_FACTORY.createMultiLineString(new LineString[]{ MultiLineString expected = GEOMETRY_FACTORY.createMultiLineString(new LineString[]{
GEOMETRY_FACTORY.createLineString(new Coordinate[]{ GEOMETRY_FACTORY.createLineString(new GeoPoint[]{
new Coordinate(100, 0), new GeoPoint(0, 100),
new Coordinate(101, 1), new GeoPoint(1, 101),
}), }),
GEOMETRY_FACTORY.createLineString(new Coordinate[]{ GEOMETRY_FACTORY.createLineString(new GeoPoint[]{
new Coordinate(102, 2), new GeoPoint(2, 102),
new Coordinate(103, 3), new GeoPoint(3, 103),
}), }),
}); });
assertGeometryEquals(jtsGeom(expected), multilinesGeoJson); assertGeometryEquals(jtsGeom(expected), multilinesGeoJson);
@ -173,14 +179,14 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
.endArray() .endArray()
.endObject().string(); .endObject().string();
List<Coordinate> shellCoordinates = new ArrayList<>(); List<GeoPoint> shellCoordinates = new ArrayList<>();
shellCoordinates.add(new Coordinate(100, 0)); shellCoordinates.add(new GeoPoint(0, 100));
shellCoordinates.add(new Coordinate(101, 0)); shellCoordinates.add(new GeoPoint(0, 101));
shellCoordinates.add(new Coordinate(101, 1)); shellCoordinates.add(new GeoPoint(1, 101));
shellCoordinates.add(new Coordinate(100, 1)); shellCoordinates.add(new GeoPoint(1, 100));
shellCoordinates.add(new Coordinate(100, 0)); shellCoordinates.add(new GeoPoint(0, 100));
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new GeoPoint[shellCoordinates.size()]));
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null); Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null);
assertGeometryEquals(jtsGeom(expected), polygonGeoJson); assertGeometryEquals(jtsGeom(expected), polygonGeoJson);
} }
@ -567,25 +573,25 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
.endArray() .endArray()
.endObject().string(); .endObject().string();
List<Coordinate> shellCoordinates = new ArrayList<>(); List<GeoPoint> shellCoordinates = new ArrayList<>();
shellCoordinates.add(new Coordinate(100, 0)); shellCoordinates.add(new GeoPoint(0, 100));
shellCoordinates.add(new Coordinate(101, 0)); shellCoordinates.add(new GeoPoint(0, 101));
shellCoordinates.add(new Coordinate(101, 1)); shellCoordinates.add(new GeoPoint(1, 101));
shellCoordinates.add(new Coordinate(100, 1)); shellCoordinates.add(new GeoPoint(1, 100));
shellCoordinates.add(new Coordinate(100, 0)); shellCoordinates.add(new GeoPoint(0, 100));
List<Coordinate> holeCoordinates = new ArrayList<>(); List<GeoPoint> holeCoordinates = new ArrayList<>();
holeCoordinates.add(new Coordinate(100.2, 0.2)); holeCoordinates.add(new GeoPoint(0.2, 100.2));
holeCoordinates.add(new Coordinate(100.8, 0.2)); holeCoordinates.add(new GeoPoint(0.2, 100.8));
holeCoordinates.add(new Coordinate(100.8, 0.8)); holeCoordinates.add(new GeoPoint(0.8, 100.8));
holeCoordinates.add(new Coordinate(100.2, 0.8)); holeCoordinates.add(new GeoPoint(0.8, 100.2));
holeCoordinates.add(new Coordinate(100.2, 0.2)); holeCoordinates.add(new GeoPoint(0.2, 100.2));
LinearRing shell = GEOMETRY_FACTORY.createLinearRing( LinearRing shell = GEOMETRY_FACTORY.createLinearRing(
shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); shellCoordinates.toArray(new GeoPoint[shellCoordinates.size()]));
LinearRing[] holes = new LinearRing[1]; LinearRing[] holes = new LinearRing[1];
holes[0] = GEOMETRY_FACTORY.createLinearRing( holes[0] = GEOMETRY_FACTORY.createLinearRing(
holeCoordinates.toArray(new Coordinate[holeCoordinates.size()])); holeCoordinates.toArray(new GeoPoint[holeCoordinates.size()]));
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes); Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes);
assertGeometryEquals(jtsGeom(expected), polygonGeoJson); assertGeometryEquals(jtsGeom(expected), polygonGeoJson);
} }
@ -657,34 +663,34 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
.endArray() .endArray()
.endObject().string(); .endObject().string();
List<Coordinate> shellCoordinates = new ArrayList<>(); List<GeoPoint> shellCoordinates = new ArrayList<>();
shellCoordinates.add(new Coordinate(100, 0)); shellCoordinates.add(new GeoPoint(0, 100));
shellCoordinates.add(new Coordinate(101, 0)); shellCoordinates.add(new GeoPoint(0, 101));
shellCoordinates.add(new Coordinate(101, 1)); shellCoordinates.add(new GeoPoint(1, 101));
shellCoordinates.add(new Coordinate(100, 1)); shellCoordinates.add(new GeoPoint(1, 100));
shellCoordinates.add(new Coordinate(100, 0)); shellCoordinates.add(new GeoPoint(0, 100));
List<Coordinate> holeCoordinates = new ArrayList<>(); List<GeoPoint> holeCoordinates = new ArrayList<>();
holeCoordinates.add(new Coordinate(100.2, 0.2)); holeCoordinates.add(new GeoPoint(0.2, 100.2));
holeCoordinates.add(new Coordinate(100.8, 0.2)); holeCoordinates.add(new GeoPoint(0.2, 100.8));
holeCoordinates.add(new Coordinate(100.8, 0.8)); holeCoordinates.add(new GeoPoint(0.8, 100.8));
holeCoordinates.add(new Coordinate(100.2, 0.8)); holeCoordinates.add(new GeoPoint(0.8, 100.2));
holeCoordinates.add(new Coordinate(100.2, 0.2)); holeCoordinates.add(new GeoPoint(0.2, 100.2));
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new GeoPoint[shellCoordinates.size()]));
LinearRing[] holes = new LinearRing[1]; LinearRing[] holes = new LinearRing[1];
holes[0] = GEOMETRY_FACTORY.createLinearRing(holeCoordinates.toArray(new Coordinate[holeCoordinates.size()])); holes[0] = GEOMETRY_FACTORY.createLinearRing(holeCoordinates.toArray(new GeoPoint[holeCoordinates.size()]));
Polygon withHoles = GEOMETRY_FACTORY.createPolygon(shell, holes); Polygon withHoles = GEOMETRY_FACTORY.createPolygon(shell, holes);
shellCoordinates = new ArrayList<>(); shellCoordinates = new ArrayList<>();
shellCoordinates.add(new Coordinate(102, 3)); shellCoordinates.add(new GeoPoint(3, 102));
shellCoordinates.add(new Coordinate(103, 3)); shellCoordinates.add(new GeoPoint(3, 103));
shellCoordinates.add(new Coordinate(103, 2)); shellCoordinates.add(new GeoPoint(2, 103));
shellCoordinates.add(new Coordinate(102, 2)); shellCoordinates.add(new GeoPoint(2, 102));
shellCoordinates.add(new Coordinate(102, 3)); shellCoordinates.add(new GeoPoint(3, 102));
shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new GeoPoint[shellCoordinates.size()]));
Polygon withoutHoles = GEOMETRY_FACTORY.createPolygon(shell, null); Polygon withoutHoles = GEOMETRY_FACTORY.createPolygon(shell, null);
Shape expected = shapeCollection(withoutHoles, withHoles); Shape expected = shapeCollection(withoutHoles, withHoles);
@ -716,22 +722,22 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
.endObject().string(); .endObject().string();
shellCoordinates = new ArrayList<>(); shellCoordinates = new ArrayList<>();
shellCoordinates.add(new Coordinate(100, 1)); shellCoordinates.add(new GeoPoint(1, 100));
shellCoordinates.add(new Coordinate(101, 1)); shellCoordinates.add(new GeoPoint(1, 101));
shellCoordinates.add(new Coordinate(101, 0)); shellCoordinates.add(new GeoPoint(0, 101));
shellCoordinates.add(new Coordinate(100, 0)); shellCoordinates.add(new GeoPoint(0, 100));
shellCoordinates.add(new Coordinate(100, 1)); shellCoordinates.add(new GeoPoint(1, 100));
holeCoordinates = new ArrayList<>(); holeCoordinates = new ArrayList<>();
holeCoordinates.add(new Coordinate(100.2, 0.8)); holeCoordinates.add(new GeoPoint(0.8, 100.2));
holeCoordinates.add(new Coordinate(100.2, 0.2)); holeCoordinates.add(new GeoPoint(0.2, 100.2));
holeCoordinates.add(new Coordinate(100.8, 0.2)); holeCoordinates.add(new GeoPoint(0.2, 100.8));
holeCoordinates.add(new Coordinate(100.8, 0.8)); holeCoordinates.add(new GeoPoint(0.8, 100.8));
holeCoordinates.add(new Coordinate(100.2, 0.8)); holeCoordinates.add(new GeoPoint(0.8, 100.2));
shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new GeoPoint[shellCoordinates.size()]));
holes = new LinearRing[1]; holes = new LinearRing[1];
holes[0] = GEOMETRY_FACTORY.createLinearRing(holeCoordinates.toArray(new Coordinate[holeCoordinates.size()])); holes[0] = GEOMETRY_FACTORY.createLinearRing(holeCoordinates.toArray(new GeoPoint[holeCoordinates.size()]));
withHoles = GEOMETRY_FACTORY.createPolygon(shell, holes); withHoles = GEOMETRY_FACTORY.createPolygon(shell, holes);
assertGeometryEquals(jtsGeom(withHoles), multiPolygonGeoJson); assertGeometryEquals(jtsGeom(withHoles), multiPolygonGeoJson);
@ -757,12 +763,12 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
.string(); .string();
Shape[] expected = new Shape[2]; Shape[] expected = new Shape[2];
LineString expectedLineString = GEOMETRY_FACTORY.createLineString(new Coordinate[]{ LineString expectedLineString = GEOMETRY_FACTORY.createLineString(new GeoPoint[]{
new Coordinate(100, 0), new GeoPoint(0, 100),
new Coordinate(101, 1), new GeoPoint(1, 101),
}); });
expected[0] = jtsGeom(expectedLineString); expected[0] = jtsGeom(expectedLineString);
Point expectedPoint = GEOMETRY_FACTORY.createPoint(new Coordinate(102.0, 2.0)); Point expectedPoint = GEOMETRY_FACTORY.createPoint(new GeoPoint(2.0, 102.0));
expected[1] = new JtsPoint(expectedPoint, SPATIAL_CONTEXT); expected[1] = new JtsPoint(expectedPoint, SPATIAL_CONTEXT);
//equals returns true only if geometries are in the same order //equals returns true only if geometries are in the same order
@ -785,7 +791,7 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
.startObject("lala").field("type", "NotAPoint").endObject() .startObject("lala").field("type", "NotAPoint").endObject()
.endObject().string(); .endObject().string();
Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0)); Point expected = GEOMETRY_FACTORY.createPoint(new GeoPoint(0.0, 100.0));
assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson); assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson);
} }

View File

@ -24,7 +24,6 @@ import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Rectangle; import com.spatial4j.core.shape.Rectangle;
import com.spatial4j.core.shape.Shape; import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.impl.PointImpl; import com.spatial4j.core.shape.impl.PointImpl;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.Polygon;
import org.elasticsearch.common.geo.builders.PolygonBuilder; import org.elasticsearch.common.geo.builders.PolygonBuilder;
@ -64,38 +63,39 @@ public class ShapeBuilderTests extends ElasticsearchTestCase {
.point(-45, 30).toPolygon(); .point(-45, 30).toPolygon();
LineString exterior = polygon.getExteriorRing(); LineString exterior = polygon.getExteriorRing();
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30)); assertEquals(exterior.getCoordinateN(0), new GeoPoint(30, -45));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30)); assertEquals(exterior.getCoordinateN(1), new GeoPoint(30, 45));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30)); assertEquals(exterior.getCoordinateN(2), new GeoPoint(-30, 45));
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30)); assertEquals(exterior.getCoordinateN(3), new GeoPoint(-30, -45));
} }
@Test @Test
public void testNewPolygon_coordinate() { public void testNewPolygon_coordinate() {
Polygon polygon = ShapeBuilder.newPolygon() Polygon polygon = ShapeBuilder.newPolygon()
.point(new Coordinate(-45, 30)) .point(new GeoPoint(30, -45))
.point(new Coordinate(45, 30)) .point(new GeoPoint(30, 45))
.point(new Coordinate(45, -30)) .point(new GeoPoint(-30, 45))
.point(new Coordinate(-45, -30)) .point(new GeoPoint(-30, -45))
.point(new Coordinate(-45, 30)).toPolygon(); .point(new GeoPoint(30, -45)).toPolygon();
LineString exterior = polygon.getExteriorRing(); LineString exterior = polygon.getExteriorRing();
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30)); assertEquals(exterior.getCoordinateN(0), new GeoPoint(30, -45));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30)); assertEquals(exterior.getCoordinateN(1), new GeoPoint(30, 45));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30)); assertEquals(exterior.getCoordinateN(2), new GeoPoint(-30, 45));
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30)); assertEquals(exterior.getCoordinateN(3), new GeoPoint(-30, -45));
} }
@Test @Test
public void testNewPolygon_coordinates() { public void testNewPolygon_coordinates() {
Polygon polygon = ShapeBuilder.newPolygon() Polygon polygon = ShapeBuilder.newPolygon()
.points(new Coordinate(-45, 30), new Coordinate(45, 30), new Coordinate(45, -30), new Coordinate(-45, -30), new Coordinate(-45, 30)).toPolygon(); .points(new GeoPoint(30, -45), new GeoPoint(30, 45), new GeoPoint(-30, 45), new GeoPoint(-30, -45),
new GeoPoint(30, -45)).toPolygon();
LineString exterior = polygon.getExteriorRing(); LineString exterior = polygon.getExteriorRing();
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30)); assertEquals(exterior.getCoordinateN(0), new GeoPoint(30, -45));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30)); assertEquals(exterior.getCoordinateN(1), new GeoPoint(30, 45));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30)); assertEquals(exterior.getCoordinateN(2), new GeoPoint(-30, 45));
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30)); assertEquals(exterior.getCoordinateN(3), new GeoPoint(-30, -45));
} }
@Test @Test