Geo: Adds a set of no dependency geo classes for JDBC driver (#36477)
Adds a set of geo classes to represent geo data in the JDBC driver and to be used as an intermediate format to pass geo shapes for indexing and query generation in #35320. Relates to #35767 and #35320
This commit is contained in:
parent
0b396a0c5e
commit
6f91f06d86
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
apply plugin: 'elasticsearch.build'
|
||||
apply plugin: 'nebula.maven-base-publish'
|
||||
apply plugin: 'nebula.maven-scm'
|
||||
|
||||
dependencies {
|
||||
if (isEclipse == false || project.path == ":libs:geo-tests") {
|
||||
testCompile("org.elasticsearch.test:framework:${version}") {
|
||||
exclude group: 'org.elasticsearch', module: 'elasticsearch-geo'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
forbiddenApisMain {
|
||||
// geo does not depend on server
|
||||
// TODO: Need to decide how we want to handle for forbidden signatures with the changes to core
|
||||
replaceSignatureFiles 'jdk-signatures'
|
||||
}
|
||||
|
||||
if (isEclipse) {
|
||||
// in eclipse the project is under a fake root, we need to change around the source sets
|
||||
sourceSets {
|
||||
if (project.path == ":libs:geo") {
|
||||
main.java.srcDirs = ['java']
|
||||
main.resources.srcDirs = ['resources']
|
||||
} else {
|
||||
test.java.srcDirs = ['java']
|
||||
test.resources.srcDirs = ['resources']
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
/**
|
||||
* Circle geometry (not part of WKT standard, but used in elasticsearch)
|
||||
*/
|
||||
public class Circle implements Geometry {
|
||||
public static final Circle EMPTY = new Circle();
|
||||
private final double lat;
|
||||
private final double lon;
|
||||
private final double radiusMeters;
|
||||
|
||||
private Circle() {
|
||||
lat = 0;
|
||||
lon = 0;
|
||||
radiusMeters = -1;
|
||||
}
|
||||
|
||||
public Circle(final double lat, final double lon, final double radiusMeters) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
this.radiusMeters = radiusMeters;
|
||||
if (radiusMeters < 0 ) {
|
||||
throw new IllegalArgumentException("Circle radius [" + radiusMeters + "] cannot be negative");
|
||||
}
|
||||
GeometryUtils.checkLatitude(lat);
|
||||
GeometryUtils.checkLongitude(lon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.CIRCLE;
|
||||
}
|
||||
|
||||
public double getLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public double getLon() {
|
||||
return lon;
|
||||
}
|
||||
|
||||
public double getRadiusMeters() {
|
||||
return radiusMeters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Circle circle = (Circle) o;
|
||||
if (Double.compare(circle.lat, lat) != 0) return false;
|
||||
if (Double.compare(circle.lon, lon) != 0) return false;
|
||||
return (Double.compare(circle.radiusMeters, radiusMeters) == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(lat);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(lon);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(radiusMeters);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return radiusMeters < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "lat=" + lat + ", lon=" + lon + ", radius=" + radiusMeters;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
/**
|
||||
* Base class for all Geometry objects supported by elasticsearch
|
||||
*/
|
||||
public interface Geometry {
|
||||
|
||||
ShapeType type();
|
||||
|
||||
<T> T visit(GeometryVisitor<T> visitor);
|
||||
|
||||
boolean isEmpty();
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Collection of arbitrary geometry classes
|
||||
*/
|
||||
public class GeometryCollection<G extends Geometry> implements Geometry, Iterable<G> {
|
||||
public static final GeometryCollection<Geometry> EMPTY = new GeometryCollection<>();
|
||||
|
||||
private final List<G> shapes;
|
||||
|
||||
public GeometryCollection() {
|
||||
shapes = Collections.emptyList();
|
||||
}
|
||||
|
||||
public GeometryCollection(List<G> shapes) {
|
||||
if (shapes == null || shapes.isEmpty()) {
|
||||
throw new IllegalArgumentException("the list of shapes cannot be null or empty");
|
||||
}
|
||||
this.shapes = shapes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.GEOMETRYCOLLECTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return shapes.isEmpty();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return shapes.size();
|
||||
}
|
||||
|
||||
public G get(int i) {
|
||||
return shapes.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
GeometryCollection<?> that = (GeometryCollection<?>) o;
|
||||
return Objects.equals(shapes, that.shapes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(shapes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<G> iterator() {
|
||||
return shapes.iterator();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
/**
|
||||
* Geometry-related utility methods
|
||||
*/
|
||||
final class GeometryUtils {
|
||||
/**
|
||||
* Minimum longitude value.
|
||||
*/
|
||||
static final double MIN_LON_INCL = -180.0D;
|
||||
|
||||
/**
|
||||
* Maximum longitude value.
|
||||
*/
|
||||
static final double MAX_LON_INCL = 180.0D;
|
||||
|
||||
/**
|
||||
* Minimum latitude value.
|
||||
*/
|
||||
static final double MIN_LAT_INCL = -90.0D;
|
||||
|
||||
/**
|
||||
* Maximum latitude value.
|
||||
*/
|
||||
static final double MAX_LAT_INCL = 90.0D;
|
||||
|
||||
// No instance:
|
||||
private GeometryUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* validates latitude value is within standard +/-90 coordinate bounds
|
||||
*/
|
||||
static void checkLatitude(double latitude) {
|
||||
if (Double.isNaN(latitude) || latitude < MIN_LAT_INCL || latitude > MAX_LAT_INCL) {
|
||||
throw new IllegalArgumentException(
|
||||
"invalid latitude " + latitude + "; must be between " + MIN_LAT_INCL + " and " + MAX_LAT_INCL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validates longitude value is within standard +/-180 coordinate bounds
|
||||
*/
|
||||
static void checkLongitude(double longitude) {
|
||||
if (Double.isNaN(longitude) || longitude < MIN_LON_INCL || longitude > MAX_LON_INCL) {
|
||||
throw new IllegalArgumentException(
|
||||
"invalid longitude " + longitude + "; must be between " + MIN_LON_INCL + " and " + MAX_LON_INCL);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
/**
|
||||
* Support class for creating Geometry Visitors.
|
||||
* <p>
|
||||
* This is an implementation of the Visitor pattern. The basic idea is to simplify adding new operations on Geometries, without
|
||||
* constantly modifying and adding new functionality to the Geometry hierarchy and keeping it as lightweight as possible.
|
||||
* <p>
|
||||
* It is a more object-oriented alternative to structures like this:
|
||||
* <pre>
|
||||
* if (obj instanceof This) {
|
||||
* doThis((This) obj);
|
||||
* } elseif (obj instanceof That) {
|
||||
* doThat((That) obj);
|
||||
* ...
|
||||
* } else {
|
||||
* throw new IllegalArgumentException("Unknown object " + obj);
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* The Visitor Pattern replaces this structure with Interface inheritance making it easier to identify all places that are using this
|
||||
* structure, and making a shape a compile-time failure instead of runtime.
|
||||
* <p>
|
||||
* See {@link org.elasticsearch.geo.utils.WellKnownText#toWKT(Geometry, StringBuilder)} for an example of how this interface is used.
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Visitor_pattern">Visitor Pattern</a>
|
||||
*/
|
||||
public interface GeometryVisitor<T> {
|
||||
|
||||
T visit(Circle circle);
|
||||
|
||||
T visit(GeometryCollection<?> collection);
|
||||
|
||||
T visit(Line line);
|
||||
|
||||
T visit(LinearRing ring);
|
||||
|
||||
T visit(MultiLine multiLine);
|
||||
|
||||
T visit(MultiPoint multiPoint);
|
||||
|
||||
T visit(MultiPolygon multiPolygon);
|
||||
|
||||
T visit(Point point);
|
||||
|
||||
T visit(Polygon polygon);
|
||||
|
||||
T visit(Rectangle rectangle);
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Represents a Line on the earth's surface in lat/lon decimal degrees.
|
||||
*/
|
||||
public class Line implements Geometry {
|
||||
public static final Line EMPTY = new Line();
|
||||
private final double[] lats;
|
||||
private final double[] lons;
|
||||
|
||||
protected Line() {
|
||||
lats = new double[0];
|
||||
lons = new double[0];
|
||||
}
|
||||
|
||||
public Line(double[] lats, double[] lons) {
|
||||
this.lats = lats;
|
||||
this.lons = lons;
|
||||
if (lats == null) {
|
||||
throw new IllegalArgumentException("lats must not be null");
|
||||
}
|
||||
if (lons == null) {
|
||||
throw new IllegalArgumentException("lons must not be null");
|
||||
}
|
||||
if (lats.length != lons.length) {
|
||||
throw new IllegalArgumentException("lats and lons must be equal length");
|
||||
}
|
||||
if (lats.length < 2) {
|
||||
throw new IllegalArgumentException("at least two points in the line is required");
|
||||
}
|
||||
for (int i = 0; i < lats.length; i++) {
|
||||
GeometryUtils.checkLatitude(lats[i]);
|
||||
GeometryUtils.checkLongitude(lons[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return lats.length;
|
||||
}
|
||||
|
||||
public double getLat(int i) {
|
||||
return lats[i];
|
||||
}
|
||||
|
||||
public double getLon(int i) {
|
||||
return lons[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.LINESTRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return lats.length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Line line = (Line) o;
|
||||
return Arrays.equals(lats, line.lats) &&
|
||||
Arrays.equals(lons, line.lons);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(lats);
|
||||
result = 31 * result + Arrays.hashCode(lons);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "lats=" + Arrays.toString(lats) +
|
||||
", lons=" + Arrays.toString(lons);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
/**
|
||||
* Represents a closed line on the earth's surface in lat/lon decimal degrees.
|
||||
* <p>
|
||||
* Cannot be serialized by WKT directly but used as a part of polygon
|
||||
*/
|
||||
public class LinearRing extends Line {
|
||||
public static final LinearRing EMPTY = new LinearRing();
|
||||
|
||||
private LinearRing() {
|
||||
}
|
||||
|
||||
public LinearRing(double[] lats, double[] lons) {
|
||||
super(lats, lons);
|
||||
if (lats.length < 2) {
|
||||
throw new IllegalArgumentException("linear ring cannot contain less than 2 points, found " + lats.length);
|
||||
}
|
||||
if (lats[0] != lats[lats.length - 1] || lons[0] != lons[lons.length - 1]) {
|
||||
throw new IllegalArgumentException("first and last points of the linear ring must be the same (it must close itself): lats[0]="
|
||||
+ lats[0] + " lats[" + (lats.length - 1) + "]=" + lats[lats.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.LINEARRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a MultiLine geometry object on the earth's surface.
|
||||
*/
|
||||
public class MultiLine extends GeometryCollection<Line> {
|
||||
public static final MultiLine EMPTY = new MultiLine();
|
||||
|
||||
private MultiLine() {
|
||||
}
|
||||
|
||||
public MultiLine(List<Line> lines) {
|
||||
super(lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.MULTILINESTRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a MultiPoint object on the earth's surface in decimal degrees.
|
||||
*/
|
||||
public class MultiPoint extends GeometryCollection<Point> {
|
||||
public static final MultiPoint EMPTY = new MultiPoint();
|
||||
|
||||
private MultiPoint() {
|
||||
}
|
||||
|
||||
public MultiPoint(List<Point> points) {
|
||||
super(points);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.MULTIPOINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Collection of polygons
|
||||
*/
|
||||
public class MultiPolygon extends GeometryCollection<Polygon> {
|
||||
public static final MultiPolygon EMPTY = new MultiPolygon();
|
||||
|
||||
private MultiPolygon() {
|
||||
}
|
||||
|
||||
public MultiPolygon(List<Polygon> polygons) {
|
||||
super(polygons);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.MULTIPOLYGON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
/**
|
||||
* Represents a Point on the earth's surface in decimal degrees.
|
||||
*/
|
||||
public class Point implements Geometry {
|
||||
public static final Point EMPTY = new Point();
|
||||
|
||||
private final double lat;
|
||||
private final double lon;
|
||||
private final boolean empty;
|
||||
|
||||
private Point() {
|
||||
lat = 0;
|
||||
lon = 0;
|
||||
empty = true;
|
||||
}
|
||||
|
||||
public Point(double lat, double lon) {
|
||||
GeometryUtils.checkLatitude(lat);
|
||||
GeometryUtils.checkLongitude(lon);
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
this.empty = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.POINT;
|
||||
}
|
||||
|
||||
public double lat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public double lon() {
|
||||
return lon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Point point = (Point) o;
|
||||
if (point.empty != empty) return false;
|
||||
if (Double.compare(point.lat, lat) != 0) return false;
|
||||
return Double.compare(point.lon, lon) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(lat);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(lon);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return empty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "lat=" + lat + ", lon=" + lon;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a closed polygon on the earth's surface with optional holes
|
||||
*/
|
||||
public final class Polygon implements Geometry {
|
||||
public static final Polygon EMPTY = new Polygon();
|
||||
private final LinearRing polygon;
|
||||
private final List<LinearRing> holes;
|
||||
|
||||
private Polygon() {
|
||||
polygon = LinearRing.EMPTY;
|
||||
holes = Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Polygon from the supplied latitude/longitude array, and optionally any holes.
|
||||
*/
|
||||
public Polygon(LinearRing polygon, List<LinearRing> holes) {
|
||||
this.polygon = polygon;
|
||||
this.holes = holes;
|
||||
if (holes == null) {
|
||||
throw new IllegalArgumentException("holes must not be null");
|
||||
}
|
||||
checkRing(polygon);
|
||||
for (LinearRing hole : holes) {
|
||||
checkRing(hole);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Polygon from the supplied latitude/longitude array, and optionally any holes.
|
||||
*/
|
||||
public Polygon(LinearRing polygon) {
|
||||
this(polygon, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.POLYGON;
|
||||
}
|
||||
|
||||
private void checkRing(LinearRing ring) {
|
||||
if (ring.length() < 4) {
|
||||
throw new IllegalArgumentException("at least 4 polygon points required");
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumberOfHoles() {
|
||||
return holes.size();
|
||||
}
|
||||
|
||||
public LinearRing getPolygon() {
|
||||
return polygon;
|
||||
}
|
||||
|
||||
public LinearRing getHole(int i) {
|
||||
if (i >= holes.size()) {
|
||||
throw new IllegalArgumentException("Index " + i + " is outside the bounds of the " + holes.size() + " polygon holes");
|
||||
}
|
||||
return holes.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return polygon.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("polygon=").append(polygon);
|
||||
if (holes.size() > 0) {
|
||||
sb.append(", holes=");
|
||||
sb.append(holes);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Polygon polygon1 = (Polygon) o;
|
||||
return Objects.equals(polygon, polygon1.polygon) &&
|
||||
Objects.equals(holes, polygon1.holes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(polygon, holes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
/**
|
||||
* Represents a lat/lon rectangle in decimal degrees.
|
||||
*/
|
||||
public class Rectangle implements Geometry {
|
||||
public static final Rectangle EMPTY = new Rectangle();
|
||||
/**
|
||||
* maximum longitude value (in degrees)
|
||||
*/
|
||||
private final double minLat;
|
||||
/**
|
||||
* minimum longitude value (in degrees)
|
||||
*/
|
||||
private final double minLon;
|
||||
/**
|
||||
* maximum latitude value (in degrees)
|
||||
*/
|
||||
private final double maxLat;
|
||||
/**
|
||||
* minimum latitude value (in degrees)
|
||||
*/
|
||||
private final double maxLon;
|
||||
|
||||
private final boolean empty;
|
||||
|
||||
private Rectangle() {
|
||||
minLat = 0;
|
||||
minLon = 0;
|
||||
maxLat = 0;
|
||||
maxLon = 0;
|
||||
empty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a bounding box by first validating the provided latitude and longitude coordinates
|
||||
*/
|
||||
public Rectangle(double minLat, double maxLat, double minLon, double maxLon) {
|
||||
GeometryUtils.checkLatitude(minLat);
|
||||
GeometryUtils.checkLatitude(maxLat);
|
||||
GeometryUtils.checkLongitude(minLon);
|
||||
GeometryUtils.checkLongitude(maxLon);
|
||||
this.minLon = minLon;
|
||||
this.maxLon = maxLon;
|
||||
this.minLat = minLat;
|
||||
this.maxLat = maxLat;
|
||||
empty = false;
|
||||
if (maxLat < minLat) {
|
||||
throw new IllegalArgumentException("max lat cannot be less than min lat");
|
||||
}
|
||||
}
|
||||
|
||||
public double getWidth() {
|
||||
if (crossesDateline()) {
|
||||
return GeometryUtils.MAX_LON_INCL - minLon + maxLon - GeometryUtils.MIN_LON_INCL;
|
||||
}
|
||||
return maxLon - minLon;
|
||||
}
|
||||
|
||||
public double getHeight() {
|
||||
return maxLat - minLat;
|
||||
}
|
||||
|
||||
public double getMinLat() {
|
||||
return minLat;
|
||||
}
|
||||
|
||||
public double getMinLon() {
|
||||
return minLon;
|
||||
}
|
||||
|
||||
public double getMaxLat() {
|
||||
return maxLat;
|
||||
}
|
||||
|
||||
public double getMaxLon() {
|
||||
return maxLon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType type() {
|
||||
return ShapeType.ENVELOPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("Rectangle(lat=");
|
||||
b.append(minLat);
|
||||
b.append(" TO ");
|
||||
b.append(maxLat);
|
||||
b.append(" lon=");
|
||||
b.append(minLon);
|
||||
b.append(" TO ");
|
||||
b.append(maxLon);
|
||||
if (maxLon < minLon) {
|
||||
b.append(" [crosses dateline!]");
|
||||
}
|
||||
b.append(")");
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this bounding box crosses the dateline
|
||||
*/
|
||||
public boolean crossesDateline() {
|
||||
return maxLon < minLon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Rectangle rectangle = (Rectangle) o;
|
||||
|
||||
if (Double.compare(rectangle.minLat, minLat) != 0) return false;
|
||||
if (Double.compare(rectangle.minLon, minLon) != 0) return false;
|
||||
if (Double.compare(rectangle.maxLat, maxLat) != 0) return false;
|
||||
return Double.compare(rectangle.maxLon, maxLon) == 0;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(minLat);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(minLon);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(maxLat);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(maxLon);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T visit(GeometryVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return empty;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
/**
|
||||
* Shape types supported by elasticsearch
|
||||
*/
|
||||
public enum ShapeType {
|
||||
POINT,
|
||||
MULTIPOINT,
|
||||
LINESTRING,
|
||||
MULTILINESTRING,
|
||||
POLYGON,
|
||||
MULTIPOLYGON,
|
||||
GEOMETRYCOLLECTION,
|
||||
LINEARRING, // not serialized by itself in WKT or WKB
|
||||
ENVELOPE, // not part of the actual WKB spec
|
||||
CIRCLE; // not part of the actual WKB spec
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Common Geo classes
|
||||
*/
|
||||
package org.elasticsearch.geo;
|
|
@ -0,0 +1,560 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.utils;
|
||||
|
||||
import org.elasticsearch.geo.geometry.Circle;
|
||||
import org.elasticsearch.geo.geometry.Geometry;
|
||||
import org.elasticsearch.geo.geometry.GeometryCollection;
|
||||
import org.elasticsearch.geo.geometry.GeometryVisitor;
|
||||
import org.elasticsearch.geo.geometry.Line;
|
||||
import org.elasticsearch.geo.geometry.LinearRing;
|
||||
import org.elasticsearch.geo.geometry.MultiLine;
|
||||
import org.elasticsearch.geo.geometry.MultiPoint;
|
||||
import org.elasticsearch.geo.geometry.MultiPolygon;
|
||||
import org.elasticsearch.geo.geometry.Point;
|
||||
import org.elasticsearch.geo.geometry.Polygon;
|
||||
import org.elasticsearch.geo.geometry.Rectangle;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StreamTokenizer;
|
||||
import java.io.StringReader;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utility class for converting to and from WKT
|
||||
*/
|
||||
public class WellKnownText {
|
||||
public static final String EMPTY = "EMPTY";
|
||||
public static final String SPACE = " ";
|
||||
public static final String LPAREN = "(";
|
||||
public static final String RPAREN = ")";
|
||||
public static final String COMMA = ",";
|
||||
public static final String NAN = "NaN";
|
||||
|
||||
private static final String NUMBER = "<NUMBER>";
|
||||
private static final String EOF = "END-OF-STREAM";
|
||||
private static final String EOL = "END-OF-LINE";
|
||||
|
||||
public static String toWKT(Geometry geometry) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
toWKT(geometry, builder);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static void toWKT(Geometry geometry, StringBuilder sb) {
|
||||
sb.append(getWKTName(geometry));
|
||||
sb.append(SPACE);
|
||||
if (geometry.isEmpty()) {
|
||||
sb.append(EMPTY);
|
||||
} else {
|
||||
geometry.visit(new GeometryVisitor<Void>() {
|
||||
@Override
|
||||
public Void visit(Circle circle) {
|
||||
sb.append(LPAREN);
|
||||
visitPoint(circle.getLon(), circle.getLat());
|
||||
sb.append(SPACE);
|
||||
sb.append(circle.getRadiusMeters());
|
||||
sb.append(RPAREN);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(GeometryCollection<?> collection) {
|
||||
if (collection.size() == 0) {
|
||||
sb.append(EMPTY);
|
||||
} else {
|
||||
sb.append(LPAREN);
|
||||
toWKT(collection.get(0), sb);
|
||||
for (int i = 1; i < collection.size(); ++i) {
|
||||
sb.append(COMMA);
|
||||
toWKT(collection.get(i), sb);
|
||||
}
|
||||
sb.append(RPAREN);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(Line line) {
|
||||
sb.append(LPAREN);
|
||||
visitPoint(line.getLon(0), line.getLat(0));
|
||||
for (int i = 1; i < line.length(); ++i) {
|
||||
sb.append(COMMA);
|
||||
sb.append(SPACE);
|
||||
visitPoint(line.getLon(i), line.getLat(i));
|
||||
}
|
||||
sb.append(RPAREN);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(LinearRing ring) {
|
||||
throw new IllegalArgumentException("Linear ring is not supported by WKT");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(MultiLine multiLine) {
|
||||
visitCollection(multiLine);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(MultiPoint multiPoint) {
|
||||
// walk through coordinates:
|
||||
sb.append(LPAREN);
|
||||
visitPoint(multiPoint.get(0).lon(), multiPoint.get(0).lat());
|
||||
for (int i = 1; i < multiPoint.size(); ++i) {
|
||||
sb.append(COMMA);
|
||||
sb.append(SPACE);
|
||||
Point point = multiPoint.get(i);
|
||||
visitPoint(point.lon(), point.lat());
|
||||
}
|
||||
sb.append(RPAREN);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(MultiPolygon multiPolygon) {
|
||||
visitCollection(multiPolygon);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(Point point) {
|
||||
if (point.isEmpty()) {
|
||||
sb.append(EMPTY);
|
||||
} else {
|
||||
sb.append(LPAREN);
|
||||
visitPoint(point.lon(), point.lat());
|
||||
sb.append(RPAREN);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void visitPoint(double lon, double lat) {
|
||||
sb.append(lon).append(SPACE).append(lat);
|
||||
}
|
||||
|
||||
private void visitCollection(GeometryCollection<?> collection) {
|
||||
if (collection.size() == 0) {
|
||||
sb.append(EMPTY);
|
||||
} else {
|
||||
sb.append(LPAREN);
|
||||
collection.get(0).visit(this);
|
||||
for (int i = 1; i < collection.size(); ++i) {
|
||||
sb.append(COMMA);
|
||||
collection.get(i).visit(this);
|
||||
}
|
||||
sb.append(RPAREN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(Polygon polygon) {
|
||||
sb.append(LPAREN);
|
||||
visit((Line) polygon.getPolygon());
|
||||
int numberOfHoles = polygon.getNumberOfHoles();
|
||||
for (int i = 0; i < numberOfHoles; ++i) {
|
||||
sb.append(", ");
|
||||
visit((Line) polygon.getHole(i));
|
||||
}
|
||||
sb.append(RPAREN);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(Rectangle rectangle) {
|
||||
sb.append(LPAREN);
|
||||
// minX, maxX, maxY, minY
|
||||
sb.append(rectangle.getMinLon());
|
||||
sb.append(COMMA);
|
||||
sb.append(SPACE);
|
||||
sb.append(rectangle.getMaxLon());
|
||||
sb.append(COMMA);
|
||||
sb.append(SPACE);
|
||||
sb.append(rectangle.getMaxLat());
|
||||
sb.append(COMMA);
|
||||
sb.append(SPACE);
|
||||
sb.append(rectangle.getMinLat());
|
||||
sb.append(RPAREN);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static Geometry fromWKT(String wkt) throws IOException, ParseException {
|
||||
StringReader reader = new StringReader(wkt);
|
||||
try {
|
||||
// setup the tokenizer; configured to read words w/o numbers
|
||||
StreamTokenizer tokenizer = new StreamTokenizer(reader);
|
||||
tokenizer.resetSyntax();
|
||||
tokenizer.wordChars('a', 'z');
|
||||
tokenizer.wordChars('A', 'Z');
|
||||
tokenizer.wordChars(128 + 32, 255);
|
||||
tokenizer.wordChars('0', '9');
|
||||
tokenizer.wordChars('-', '-');
|
||||
tokenizer.wordChars('+', '+');
|
||||
tokenizer.wordChars('.', '.');
|
||||
tokenizer.whitespaceChars(' ', ' ');
|
||||
tokenizer.whitespaceChars('\t', '\t');
|
||||
tokenizer.whitespaceChars('\r', '\r');
|
||||
tokenizer.whitespaceChars('\n', '\n');
|
||||
tokenizer.commentChar('#');
|
||||
return parseGeometry(tokenizer);
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parse geometry from the stream tokenizer
|
||||
*/
|
||||
private static Geometry parseGeometry(StreamTokenizer stream) throws IOException, ParseException {
|
||||
final String type = nextWord(stream).toLowerCase(Locale.ROOT);
|
||||
switch (type) {
|
||||
case "point":
|
||||
return parsePoint(stream);
|
||||
case "multipoint":
|
||||
return parseMultiPoint(stream);
|
||||
case "linestring":
|
||||
return parseLine(stream);
|
||||
case "multilinestring":
|
||||
return parseMultiLine(stream);
|
||||
case "polygon":
|
||||
return parsePolygon(stream);
|
||||
case "multipolygon":
|
||||
return parseMultiPolygon(stream);
|
||||
case "bbox":
|
||||
return parseBBox(stream);
|
||||
case "geometrycollection":
|
||||
return parseGeometryCollection(stream);
|
||||
case "circle": // Not part of the standard, but we need it for internal serialization
|
||||
return parseCircle(stream);
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown geometry type: " + type);
|
||||
}
|
||||
|
||||
private static GeometryCollection<Geometry> parseGeometryCollection(StreamTokenizer stream) throws IOException, ParseException {
|
||||
if (nextEmptyOrOpen(stream).equals(EMPTY)) {
|
||||
return GeometryCollection.EMPTY;
|
||||
}
|
||||
List<Geometry> shapes = new ArrayList<>();
|
||||
shapes.add(parseGeometry(stream));
|
||||
while (nextCloserOrComma(stream).equals(COMMA)) {
|
||||
shapes.add(parseGeometry(stream));
|
||||
}
|
||||
return new GeometryCollection<>(shapes);
|
||||
}
|
||||
|
||||
private static Point parsePoint(StreamTokenizer stream) throws IOException, ParseException {
|
||||
if (nextEmptyOrOpen(stream).equals(EMPTY)) {
|
||||
return Point.EMPTY;
|
||||
}
|
||||
double lon = nextNumber(stream);
|
||||
double lat = nextNumber(stream);
|
||||
Point pt = new Point(lat, lon);
|
||||
if (isNumberNext(stream) == true) {
|
||||
nextNumber(stream);
|
||||
}
|
||||
nextCloser(stream);
|
||||
return pt;
|
||||
}
|
||||
|
||||
private static void parseCoordinates(StreamTokenizer stream, ArrayList<Double> lats, ArrayList<Double> lons)
|
||||
throws IOException, ParseException {
|
||||
parseCoordinate(stream, lats, lons);
|
||||
while (nextCloserOrComma(stream).equals(COMMA)) {
|
||||
parseCoordinate(stream, lats, lons);
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseCoordinate(StreamTokenizer stream, ArrayList<Double> lats, ArrayList<Double> lons)
|
||||
throws IOException, ParseException {
|
||||
lons.add(nextNumber(stream));
|
||||
lats.add(nextNumber(stream));
|
||||
if (isNumberNext(stream)) {
|
||||
nextNumber(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private static MultiPoint parseMultiPoint(StreamTokenizer stream) throws IOException, ParseException {
|
||||
String token = nextEmptyOrOpen(stream);
|
||||
if (token.equals(EMPTY)) {
|
||||
return MultiPoint.EMPTY;
|
||||
}
|
||||
ArrayList<Double> lats = new ArrayList<>();
|
||||
ArrayList<Double> lons = new ArrayList<>();
|
||||
ArrayList<Point> points = new ArrayList<>();
|
||||
parseCoordinates(stream, lats, lons);
|
||||
for (int i = 0; i < lats.size(); i++) {
|
||||
points.add(new Point(lats.get(i), lons.get(i)));
|
||||
}
|
||||
return new MultiPoint(Collections.unmodifiableList(points));
|
||||
}
|
||||
|
||||
private static Line parseLine(StreamTokenizer stream) throws IOException, ParseException {
|
||||
String token = nextEmptyOrOpen(stream);
|
||||
if (token.equals(EMPTY)) {
|
||||
return Line.EMPTY;
|
||||
}
|
||||
ArrayList<Double> lats = new ArrayList<>();
|
||||
ArrayList<Double> lons = new ArrayList<>();
|
||||
parseCoordinates(stream, lats, lons);
|
||||
return new Line(lats.stream().mapToDouble(i -> i).toArray(), lons.stream().mapToDouble(i -> i).toArray());
|
||||
}
|
||||
|
||||
private static MultiLine parseMultiLine(StreamTokenizer stream) throws IOException, ParseException {
|
||||
String token = nextEmptyOrOpen(stream);
|
||||
if (token.equals(EMPTY)) {
|
||||
return MultiLine.EMPTY;
|
||||
}
|
||||
ArrayList<Line> lines = new ArrayList<>();
|
||||
lines.add(parseLine(stream));
|
||||
while (nextCloserOrComma(stream).equals(COMMA)) {
|
||||
lines.add(parseLine(stream));
|
||||
}
|
||||
return new MultiLine(Collections.unmodifiableList(lines));
|
||||
}
|
||||
|
||||
private static LinearRing parsePolygonHole(StreamTokenizer stream) throws IOException, ParseException {
|
||||
nextOpener(stream);
|
||||
ArrayList<Double> lats = new ArrayList<>();
|
||||
ArrayList<Double> lons = new ArrayList<>();
|
||||
parseCoordinates(stream, lats, lons);
|
||||
return new LinearRing(lats.stream().mapToDouble(i -> i).toArray(), lons.stream().mapToDouble(i -> i).toArray());
|
||||
}
|
||||
|
||||
private static Polygon parsePolygon(StreamTokenizer stream) throws IOException, ParseException {
|
||||
if (nextEmptyOrOpen(stream).equals(EMPTY)) {
|
||||
return Polygon.EMPTY;
|
||||
}
|
||||
nextOpener(stream);
|
||||
ArrayList<Double> lats = new ArrayList<>();
|
||||
ArrayList<Double> lons = new ArrayList<>();
|
||||
parseCoordinates(stream, lats, lons);
|
||||
ArrayList<LinearRing> holes = new ArrayList<>();
|
||||
while (nextCloserOrComma(stream).equals(COMMA)) {
|
||||
holes.add(parsePolygonHole(stream));
|
||||
}
|
||||
if (holes.isEmpty()) {
|
||||
return new Polygon(new LinearRing(lats.stream().mapToDouble(i -> i).toArray(), lons.stream().mapToDouble(i -> i).toArray()));
|
||||
} else {
|
||||
return new Polygon(
|
||||
new LinearRing(lats.stream().mapToDouble(i -> i).toArray(), lons.stream().mapToDouble(i -> i).toArray()),
|
||||
Collections.unmodifiableList(holes));
|
||||
}
|
||||
}
|
||||
|
||||
private static MultiPolygon parseMultiPolygon(StreamTokenizer stream) throws IOException, ParseException {
|
||||
String token = nextEmptyOrOpen(stream);
|
||||
if (token.equals(EMPTY)) {
|
||||
return MultiPolygon.EMPTY;
|
||||
}
|
||||
ArrayList<Polygon> polygons = new ArrayList<>();
|
||||
polygons.add(parsePolygon(stream));
|
||||
while (nextCloserOrComma(stream).equals(COMMA)) {
|
||||
polygons.add(parsePolygon(stream));
|
||||
}
|
||||
return new MultiPolygon(Collections.unmodifiableList(polygons));
|
||||
}
|
||||
|
||||
private static Rectangle parseBBox(StreamTokenizer stream) throws IOException, ParseException {
|
||||
if (nextEmptyOrOpen(stream).equals(EMPTY)) {
|
||||
return Rectangle.EMPTY;
|
||||
}
|
||||
double minLon = nextNumber(stream);
|
||||
nextComma(stream);
|
||||
double maxLon = nextNumber(stream);
|
||||
nextComma(stream);
|
||||
double maxLat = nextNumber(stream);
|
||||
nextComma(stream);
|
||||
double minLat = nextNumber(stream);
|
||||
nextCloser(stream);
|
||||
return new Rectangle(minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
|
||||
|
||||
private static Circle parseCircle(StreamTokenizer stream) throws IOException, ParseException {
|
||||
if (nextEmptyOrOpen(stream).equals(EMPTY)) {
|
||||
return Circle.EMPTY;
|
||||
}
|
||||
double lon = nextNumber(stream);
|
||||
double lat = nextNumber(stream);
|
||||
double radius = nextNumber(stream);
|
||||
Circle circle = new Circle(lat, lon, radius);
|
||||
if (isNumberNext(stream) == true) {
|
||||
nextNumber(stream);
|
||||
}
|
||||
nextCloser(stream);
|
||||
return circle;
|
||||
}
|
||||
|
||||
/**
|
||||
* next word in the stream
|
||||
*/
|
||||
private static String nextWord(StreamTokenizer stream) throws ParseException, IOException {
|
||||
switch (stream.nextToken()) {
|
||||
case StreamTokenizer.TT_WORD:
|
||||
final String word = stream.sval;
|
||||
return word.equalsIgnoreCase(EMPTY) ? EMPTY : word;
|
||||
case '(':
|
||||
return LPAREN;
|
||||
case ')':
|
||||
return RPAREN;
|
||||
case ',':
|
||||
return COMMA;
|
||||
}
|
||||
throw new ParseException("expected word but found: " + tokenString(stream), stream.lineno());
|
||||
}
|
||||
|
||||
private static double nextNumber(StreamTokenizer stream) throws IOException, ParseException {
|
||||
if (stream.nextToken() == StreamTokenizer.TT_WORD) {
|
||||
if (stream.sval.equalsIgnoreCase(NAN)) {
|
||||
return Double.NaN;
|
||||
} else {
|
||||
try {
|
||||
return Double.parseDouble(stream.sval);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParseException("invalid number found: " + stream.sval, stream.lineno());
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ParseException("expected number but found: " + tokenString(stream), stream.lineno());
|
||||
}
|
||||
|
||||
private static String tokenString(StreamTokenizer stream) {
|
||||
switch (stream.ttype) {
|
||||
case StreamTokenizer.TT_WORD:
|
||||
return stream.sval;
|
||||
case StreamTokenizer.TT_EOF:
|
||||
return EOF;
|
||||
case StreamTokenizer.TT_EOL:
|
||||
return EOL;
|
||||
case StreamTokenizer.TT_NUMBER:
|
||||
return NUMBER;
|
||||
}
|
||||
return "'" + (char) stream.ttype + "'";
|
||||
}
|
||||
|
||||
private static boolean isNumberNext(StreamTokenizer stream) throws IOException {
|
||||
final int type = stream.nextToken();
|
||||
stream.pushBack();
|
||||
return type == StreamTokenizer.TT_WORD;
|
||||
}
|
||||
|
||||
private static String nextEmptyOrOpen(StreamTokenizer stream) throws IOException, ParseException {
|
||||
final String next = nextWord(stream);
|
||||
if (next.equals(EMPTY) || next.equals(LPAREN)) {
|
||||
return next;
|
||||
}
|
||||
throw new ParseException("expected " + EMPTY + " or " + LPAREN
|
||||
+ " but found: " + tokenString(stream), stream.lineno());
|
||||
}
|
||||
|
||||
private static String nextCloser(StreamTokenizer stream) throws IOException, ParseException {
|
||||
if (nextWord(stream).equals(RPAREN)) {
|
||||
return RPAREN;
|
||||
}
|
||||
throw new ParseException("expected " + RPAREN + " but found: " + tokenString(stream), stream.lineno());
|
||||
}
|
||||
|
||||
private static String nextComma(StreamTokenizer stream) throws IOException, ParseException {
|
||||
if (nextWord(stream).equals(COMMA) == true) {
|
||||
return COMMA;
|
||||
}
|
||||
throw new ParseException("expected " + COMMA + " but found: " + tokenString(stream), stream.lineno());
|
||||
}
|
||||
|
||||
private static String nextOpener(StreamTokenizer stream) throws IOException, ParseException {
|
||||
if (nextWord(stream).equals(LPAREN)) {
|
||||
return LPAREN;
|
||||
}
|
||||
throw new ParseException("expected " + LPAREN + " but found: " + tokenString(stream), stream.lineno());
|
||||
}
|
||||
|
||||
private static String nextCloserOrComma(StreamTokenizer stream) throws IOException, ParseException {
|
||||
String token = nextWord(stream);
|
||||
if (token.equals(COMMA) || token.equals(RPAREN)) {
|
||||
return token;
|
||||
}
|
||||
throw new ParseException("expected " + COMMA + " or " + RPAREN
|
||||
+ " but found: " + tokenString(stream), stream.lineno());
|
||||
}
|
||||
|
||||
public static String getWKTName(Geometry geometry) {
|
||||
return geometry.visit(new GeometryVisitor<String>() {
|
||||
@Override
|
||||
public String visit(Circle circle) {
|
||||
return "circle";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visit(GeometryCollection<?> collection) {
|
||||
return "geometrycollection";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visit(Line line) {
|
||||
return "linestring";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visit(LinearRing ring) {
|
||||
throw new UnsupportedOperationException("line ring cannot be serialized using WKT");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visit(MultiLine multiLine) {
|
||||
return "multilinestring";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visit(MultiPoint multiPoint) {
|
||||
return "multipoint";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visit(MultiPolygon multiPolygon) {
|
||||
return "multipolygon";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visit(Point point) {
|
||||
return "point";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visit(Polygon polygon) {
|
||||
return "polygon";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visit(Rectangle rectangle) {
|
||||
return "bbox";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
import org.elasticsearch.test.AbstractWireTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
abstract class BaseGeometryTestCase<T extends Geometry> extends AbstractWireTestCase<T> {
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<T> instanceReader() {
|
||||
throw new IllegalStateException("shouldn't be called in this test");
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected T copyInstance(T instance, Version version) throws IOException {
|
||||
String text = WellKnownText.toWKT(instance);
|
||||
try {
|
||||
return (T) WellKnownText.fromWKT(text);
|
||||
} catch (ParseException e) {
|
||||
throw new ElasticsearchException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void testVisitor() {
|
||||
testVisitor(createTestInstance());
|
||||
}
|
||||
|
||||
public static void testVisitor(Geometry geom) {
|
||||
AtomicBoolean called = new AtomicBoolean(false);
|
||||
Object result = geom.visit(new GeometryVisitor<Object>() {
|
||||
private Object verify(Geometry geometry, String expectedClass) {
|
||||
assertFalse("Visitor should be called only once", called.getAndSet(true));
|
||||
assertSame(geom, geometry);
|
||||
assertEquals(geometry.getClass().getName(), "org.elasticsearch.geo.geometry." + expectedClass);
|
||||
return "result";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(Circle circle) {
|
||||
return verify(circle, "Circle");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(GeometryCollection<?> collection) {
|
||||
return verify(collection, "GeometryCollection"); }
|
||||
|
||||
@Override
|
||||
public Object visit(Line line) {
|
||||
return verify(line, "Line");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(LinearRing ring) {
|
||||
return verify(ring, "LinearRing");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(MultiLine multiLine) {
|
||||
return verify(multiLine, "MultiLine");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(MultiPoint multiPoint) {
|
||||
return verify(multiPoint, "MultiPoint");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(MultiPolygon multiPolygon) {
|
||||
return verify(multiPolygon, "MultiPolygon");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(Point point) {
|
||||
return verify(point, "Point");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(Polygon polygon) {
|
||||
return verify(polygon, "Polygon");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(Rectangle rectangle) {
|
||||
return verify(rectangle, "Rectangle");
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue("visitor wasn't called", called.get());
|
||||
assertEquals("result", result);
|
||||
}
|
||||
|
||||
public static double randomLat() {
|
||||
return randomDoubleBetween(-90, 90, true);
|
||||
}
|
||||
|
||||
public static double randomLon() {
|
||||
return randomDoubleBetween(-180, 180, true);
|
||||
}
|
||||
|
||||
public static Circle randomCircle() {
|
||||
return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDoubleBetween(0, 100, false));
|
||||
}
|
||||
|
||||
public static Line randomLine() {
|
||||
int size = randomIntBetween(2, 10);
|
||||
double[] lats = new double[size];
|
||||
double[] lons = new double[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
lats[i] = randomLat();
|
||||
lons[i] = randomLon();
|
||||
}
|
||||
return new Line(lats, lons);
|
||||
}
|
||||
|
||||
public static Point randomPoint() {
|
||||
return new Point(randomLat(), randomLon());
|
||||
}
|
||||
|
||||
public static LinearRing randomLinearRing() {
|
||||
int size = randomIntBetween(3, 10);
|
||||
double[] lats = new double[size + 1];
|
||||
double[] lons = new double[size + 1];
|
||||
for (int i = 0; i < size; i++) {
|
||||
lats[i] = randomLat();
|
||||
lons[i] = randomLon();
|
||||
}
|
||||
lats[size] = lats[0];
|
||||
lons[size] = lons[0];
|
||||
return new LinearRing(lats, lons);
|
||||
}
|
||||
|
||||
public static Polygon randomPolygon() {
|
||||
int size = randomIntBetween(0, 10);
|
||||
List<LinearRing> holes = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
holes.add(randomLinearRing());
|
||||
}
|
||||
if (holes.size() > 0) {
|
||||
return new Polygon(randomLinearRing(), holes);
|
||||
} else {
|
||||
return new Polygon(randomLinearRing());
|
||||
}
|
||||
}
|
||||
|
||||
public static Rectangle randomRectangle() {
|
||||
double lat1 = randomLat();
|
||||
double lat2 = randomLat();
|
||||
double minLon = randomLon();
|
||||
double maxLon = randomLon();
|
||||
return new Rectangle(Math.min(lat1, lat2), Math.max(lat1, lat2), minLon, maxLon);
|
||||
}
|
||||
|
||||
public static GeometryCollection<Geometry> randomGeometryCollection() {
|
||||
return randomGeometryCollection(0);
|
||||
}
|
||||
|
||||
private static GeometryCollection<Geometry> randomGeometryCollection(int level) {
|
||||
int size = randomIntBetween(1, 10);
|
||||
List<Geometry> shapes = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
@SuppressWarnings("unchecked") Supplier<Geometry> geometry = randomFrom(
|
||||
BaseGeometryTestCase::randomCircle,
|
||||
BaseGeometryTestCase::randomLine,
|
||||
BaseGeometryTestCase::randomPoint,
|
||||
BaseGeometryTestCase::randomPolygon,
|
||||
BaseGeometryTestCase::randomRectangle,
|
||||
level < 3 ? () -> randomGeometryCollection(level + 1) : BaseGeometryTestCase::randomPoint // don't build too deep
|
||||
);
|
||||
shapes.add(geometry.get());
|
||||
}
|
||||
return new GeometryCollection<>(shapes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
public class CircleTests extends BaseGeometryTestCase<Circle> {
|
||||
@Override
|
||||
protected Circle createTestInstance() {
|
||||
return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDoubleBetween(0, 100, false));
|
||||
}
|
||||
|
||||
public void testBasicSerialization() throws IOException, ParseException {
|
||||
assertEquals("circle (20.0 10.0 15.0)", WellKnownText.toWKT(new Circle(10, 20, 15)));
|
||||
assertEquals(new Circle(10, 20, 15), WellKnownText.fromWKT("circle (20.0 10.0 15.0)"));
|
||||
|
||||
assertEquals("circle EMPTY", WellKnownText.toWKT(Circle.EMPTY));
|
||||
assertEquals(Circle.EMPTY, WellKnownText.fromWKT("circle EMPTY)"));
|
||||
}
|
||||
|
||||
public void testInitValidation() {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> new Circle(10, 20, -1));
|
||||
assertEquals("Circle radius [-1.0] cannot be negative", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new Circle(100, 20, 1));
|
||||
assertEquals("invalid latitude 100.0; must be between -90.0 and 90.0", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new Circle(10, 200, 1));
|
||||
assertEquals("invalid longitude 200.0; must be between -180.0 and 180.0", ex.getMessage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
public class GeometryCollectionTests extends BaseGeometryTestCase<GeometryCollection<Geometry>> {
|
||||
@Override
|
||||
protected GeometryCollection<Geometry> createTestInstance() {
|
||||
return randomGeometryCollection();
|
||||
}
|
||||
|
||||
public void testBasicSerialization() throws IOException, ParseException {
|
||||
assertEquals("geometrycollection (point (20.0 10.0),point EMPTY)",
|
||||
WellKnownText.toWKT(new GeometryCollection<Geometry>(Arrays.asList(new Point(10, 20), Point.EMPTY))));
|
||||
|
||||
assertEquals(new GeometryCollection<Geometry>(Arrays.asList(new Point(10, 20), Point.EMPTY)),
|
||||
WellKnownText.fromWKT("geometrycollection (point (20.0 10.0),point EMPTY)"));
|
||||
|
||||
assertEquals("geometrycollection EMPTY", WellKnownText.toWKT(GeometryCollection.EMPTY));
|
||||
assertEquals(GeometryCollection.EMPTY, WellKnownText.fromWKT("geometrycollection EMPTY)"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public void testInitValidation() {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> new GeometryCollection<>(Collections.emptyList()));
|
||||
assertEquals("the list of shapes cannot be null or empty", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new GeometryCollection<>(null));
|
||||
assertEquals("the list of shapes cannot be null or empty", ex.getMessage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
public class LineTests extends BaseGeometryTestCase<Line> {
|
||||
@Override
|
||||
protected Line createTestInstance() {
|
||||
return randomLine();
|
||||
}
|
||||
|
||||
public void testBasicSerialization() throws IOException, ParseException {
|
||||
assertEquals("linestring (3.0 1.0, 4.0 2.0)", WellKnownText.toWKT(new Line(new double[]{1, 2}, new double[]{3, 4})));
|
||||
assertEquals(new Line(new double[]{1, 2}, new double[]{3, 4}), WellKnownText.fromWKT("linestring (3 1, 4 2)"));
|
||||
|
||||
assertEquals("linestring EMPTY", WellKnownText.toWKT(Line.EMPTY));
|
||||
assertEquals(Line.EMPTY, WellKnownText.fromWKT("linestring EMPTY)"));
|
||||
}
|
||||
|
||||
public void testInitValidation() {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> new Line(new double[]{1}, new double[]{3}));
|
||||
assertEquals("at least two points in the line is required", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new Line(new double[]{1, 2, 3, 1}, new double[]{3, 4, 500, 3}));
|
||||
assertEquals("invalid longitude 500.0; must be between -180.0 and 180.0", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new Line(new double[]{1, 100, 3, 1}, new double[]{3, 4, 5, 3}));
|
||||
assertEquals("invalid latitude 100.0; must be between -90.0 and 90.0", ex.getMessage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
public class LinearRingTests extends ESTestCase {
|
||||
|
||||
public void testBasicSerialization() {
|
||||
UnsupportedOperationException ex = expectThrows(UnsupportedOperationException.class,
|
||||
() -> WellKnownText.toWKT(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3})));
|
||||
assertEquals("line ring cannot be serialized using WKT", ex.getMessage());
|
||||
}
|
||||
|
||||
public void testInitValidation() {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class,
|
||||
() -> new LinearRing(new double[]{1, 2, 3}, new double[]{3, 4, 5}));
|
||||
assertEquals("first and last points of the linear ring must be the same (it must close itself): lats[0]=1.0 lats[2]=3.0",
|
||||
ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new LinearRing(new double[]{1}, new double[]{3}));
|
||||
assertEquals("at least two points in the line is required", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 500, 3}));
|
||||
assertEquals("invalid longitude 500.0; must be between -180.0 and 180.0", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new LinearRing(new double[]{1, 100, 3, 1}, new double[]{3, 4, 5, 3}));
|
||||
assertEquals("invalid latitude 100.0; must be between -90.0 and 90.0", ex.getMessage());
|
||||
}
|
||||
|
||||
public void testVisitor() {
|
||||
BaseGeometryTestCase.testVisitor(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3}));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MultiLineTests extends BaseGeometryTestCase<MultiLine> {
|
||||
|
||||
@Override
|
||||
protected MultiLine createTestInstance() {
|
||||
int size = randomIntBetween(1, 10);
|
||||
List<Line> arr = new ArrayList<Line>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
arr.add(randomLine());
|
||||
}
|
||||
return new MultiLine(arr);
|
||||
}
|
||||
|
||||
public void testBasicSerialization() throws IOException, ParseException {
|
||||
assertEquals("multilinestring ((3.0 1.0, 4.0 2.0))", WellKnownText.toWKT(
|
||||
new MultiLine(Collections.singletonList(new Line(new double[]{1, 2}, new double[]{3, 4})))));
|
||||
assertEquals(new MultiLine(Collections.singletonList(new Line(new double[]{1, 2}, new double[]{3, 4}))),
|
||||
WellKnownText.fromWKT("multilinestring ((3 1, 4 2))"));
|
||||
|
||||
assertEquals("multilinestring EMPTY", WellKnownText.toWKT(MultiLine.EMPTY));
|
||||
assertEquals(MultiLine.EMPTY, WellKnownText.fromWKT("multilinestring EMPTY)"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MultiPointTests extends BaseGeometryTestCase<MultiPoint> {
|
||||
|
||||
@Override
|
||||
protected MultiPoint createTestInstance() {
|
||||
int size = randomIntBetween(1, 10);
|
||||
List<Point> arr = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
arr.add(randomPoint());
|
||||
}
|
||||
return new MultiPoint(arr);
|
||||
}
|
||||
|
||||
public void testBasicSerialization() throws IOException, ParseException {
|
||||
assertEquals("multipoint (2.0 1.0)", WellKnownText.toWKT(
|
||||
new MultiPoint(Collections.singletonList(new Point(1, 2)))));
|
||||
assertEquals(new MultiPoint(Collections.singletonList(new Point(1 ,2))),
|
||||
WellKnownText.fromWKT("multipoint (2 1)"));
|
||||
|
||||
assertEquals("multipoint EMPTY", WellKnownText.toWKT(MultiPoint.EMPTY));
|
||||
assertEquals(MultiPoint.EMPTY, WellKnownText.fromWKT("multipoint EMPTY)"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MultiPolygonTests extends BaseGeometryTestCase<MultiPolygon> {
|
||||
|
||||
@Override
|
||||
protected MultiPolygon createTestInstance() {
|
||||
int size = randomIntBetween(1, 10);
|
||||
List<Polygon> arr = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
arr.add(randomPolygon());
|
||||
}
|
||||
return new MultiPolygon(arr);
|
||||
}
|
||||
|
||||
public void testBasicSerialization() throws IOException, ParseException {
|
||||
assertEquals("multipolygon (((3.0 1.0, 4.0 2.0, 5.0 3.0, 3.0 1.0)))",
|
||||
WellKnownText.toWKT(new MultiPolygon(Collections.singletonList(
|
||||
new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3}))))));
|
||||
assertEquals(new MultiPolygon(Collections.singletonList(
|
||||
new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3})))),
|
||||
WellKnownText.fromWKT("multipolygon (((3.0 1.0, 4.0 2.0, 5.0 3.0, 3.0 1.0)))"));
|
||||
|
||||
assertEquals("multipolygon EMPTY", WellKnownText.toWKT(MultiPolygon.EMPTY));
|
||||
assertEquals(MultiPolygon.EMPTY, WellKnownText.fromWKT("multipolygon EMPTY)"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
public class PointTests extends BaseGeometryTestCase<Point> {
|
||||
@Override
|
||||
protected Point createTestInstance() {
|
||||
return randomPoint();
|
||||
}
|
||||
|
||||
public void testBasicSerialization() throws IOException, ParseException {
|
||||
assertEquals("point (20.0 10.0)", WellKnownText.toWKT(new Point(10, 20)));
|
||||
assertEquals(new Point(10, 20), WellKnownText.fromWKT("point (20.0 10.0)"));
|
||||
|
||||
assertEquals("point EMPTY", WellKnownText.toWKT(Point.EMPTY));
|
||||
assertEquals(Point.EMPTY, WellKnownText.fromWKT("point EMPTY)"));
|
||||
}
|
||||
|
||||
public void testInitValidation() {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> new Point(100, 10));
|
||||
assertEquals("invalid latitude 100.0; must be between -90.0 and 90.0", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new Point(10, 500));
|
||||
assertEquals("invalid longitude 500.0; must be between -180.0 and 180.0", ex.getMessage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
public class PolygonTests extends BaseGeometryTestCase<Polygon> {
|
||||
@Override
|
||||
protected Polygon createTestInstance() {
|
||||
return randomPolygon();
|
||||
}
|
||||
|
||||
public void testBasicSerialization() throws IOException, ParseException {
|
||||
assertEquals("polygon ((3.0 1.0, 4.0 2.0, 5.0 3.0, 3.0 1.0))",
|
||||
WellKnownText.toWKT(new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3}))));
|
||||
assertEquals(new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3})),
|
||||
WellKnownText.fromWKT("polygon ((3 1, 4 2, 5 3, 3 1))"));
|
||||
|
||||
assertEquals("polygon EMPTY", WellKnownText.toWKT(Polygon.EMPTY));
|
||||
assertEquals(Polygon.EMPTY, WellKnownText.fromWKT("polygon EMPTY)"));
|
||||
}
|
||||
|
||||
public void testInitValidation() {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class,
|
||||
() -> new Polygon(new LinearRing(new double[]{1, 2, 1}, new double[]{3, 4, 3})));
|
||||
assertEquals("at least 4 polygon points required", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class,
|
||||
() -> new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3}), null));
|
||||
assertEquals("holes must not be null", ex.getMessage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.geo.geometry;
|
||||
|
||||
import org.elasticsearch.geo.utils.WellKnownText;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
public class RectangleTests extends BaseGeometryTestCase<Rectangle> {
|
||||
@Override
|
||||
protected Rectangle createTestInstance() {
|
||||
return randomRectangle();
|
||||
}
|
||||
|
||||
public void testBasicSerialization() throws IOException, ParseException {
|
||||
assertEquals("bbox (10.0, 20.0, 40.0, 30.0)", WellKnownText.toWKT(new Rectangle(30, 40, 10, 20)));
|
||||
assertEquals(new Rectangle(30, 40, 10, 20), WellKnownText.fromWKT("bbox (10.0, 20.0, 40.0, 30.0)"));
|
||||
|
||||
assertEquals("bbox EMPTY", WellKnownText.toWKT(Rectangle.EMPTY));
|
||||
assertEquals(Rectangle.EMPTY, WellKnownText.fromWKT("bbox EMPTY)"));
|
||||
}
|
||||
|
||||
public void testInitValidation() {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> new Rectangle(100, 1, 2, 3));
|
||||
assertEquals("invalid latitude 100.0; must be between -90.0 and 90.0", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new Rectangle(1, 2, 200, 3));
|
||||
assertEquals("invalid longitude 200.0; must be between -180.0 and 180.0", ex.getMessage());
|
||||
|
||||
ex = expectThrows(IllegalArgumentException.class, () -> new Rectangle(2, 1, 2, 3));
|
||||
assertEquals("max lat cannot be less than min lat", ex.getMessage());
|
||||
}
|
||||
}
|
|
@ -95,6 +95,7 @@ if (isEclipse) {
|
|||
projects << 'libs:x-content-tests'
|
||||
projects << 'libs:secure-sm-tests'
|
||||
projects << 'libs:grok-tests'
|
||||
projects << 'libs:geo-tests'
|
||||
}
|
||||
|
||||
include projects.toArray(new String[0])
|
||||
|
@ -130,7 +131,10 @@ if (isEclipse) {
|
|||
project(":libs:grok").buildFileName = 'eclipse-build.gradle'
|
||||
project(":libs:grok-tests").projectDir = new File(rootProject.projectDir, 'libs/grok/src/test')
|
||||
project(":libs:grok-tests").buildFileName = 'eclipse-build.gradle'
|
||||
}
|
||||
project(":libs:geo").projectDir = new File(rootProject.projectDir, 'libs/geo/src/main')
|
||||
project(":libs:geo").buildFileName = 'eclipse-build.gradle'
|
||||
project(":libs:geo-tests").projectDir = new File(rootProject.projectDir, 'libs/geo/src/test')
|
||||
project(":libs:geo-tests").buildFileName = 'eclipse-build.gradle'}
|
||||
|
||||
// look for extra plugins for elasticsearch
|
||||
File extraProjects = new File(rootProject.projectDir.parentFile, "${dirName}-extra")
|
||||
|
@ -141,3 +145,4 @@ if (extraProjects.exists()) {
|
|||
}
|
||||
|
||||
project(":libs:cli").name = 'elasticsearch-cli'
|
||||
project(":libs:geo").name = 'elasticsearch-geo'
|
||||
|
|
Loading…
Reference in New Issue