[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:
parent
9ac6d78308
commit
06667c6aa8
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -38,6 +39,8 @@ public class GeoUtils {
|
||||||
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() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue