Skip to content

Commit

Permalink
Geo: deprecate ShapeBuilder in QueryBuilders
Browse files Browse the repository at this point in the history
Removes unnecessary now timeline decompositions from shape builders
and deprecates ShapeBuilders in QueryBuilder in favor of libs/geo
shapes.

Relates to elastic#40908
  • Loading branch information
imotov committed Jul 22, 2019
1 parent 3b7b025 commit 0355d0c
Show file tree
Hide file tree
Showing 13 changed files with 656 additions and 183 deletions.
18 changes: 9 additions & 9 deletions server/src/main/java/org/elasticsearch/common/geo/GeoJson.java
Original file line number Diff line number Diff line change
Expand Up @@ -382,17 +382,17 @@ public static String getGeoJsonName(Geometry geometry) {
return geometry.visit(new GeometryVisitor<>() {
@Override
public String visit(Circle circle) {
return "Circle";
return "circle";
}

@Override
public String visit(GeometryCollection<?> collection) {
return "GeometryCollection";
return "geometrycollection";
}

@Override
public String visit(Line line) {
return "LineString";
return "linestring";
}

@Override
Expand All @@ -402,32 +402,32 @@ public String visit(LinearRing ring) {

@Override
public String visit(MultiLine multiLine) {
return "MultiLineString";
return "multilinestring";
}

@Override
public String visit(MultiPoint multiPoint) {
return "MultiPoint";
return "multipoint";
}

@Override
public String visit(MultiPolygon multiPolygon) {
return "MultiPolygon";
return "multipolygon";
}

@Override
public String visit(Point point) {
return "Point";
return "point";
}

@Override
public String visit(Polygon polygon) {
return "Polygon";
return "polygon";
}

@Override
public String visit(Rectangle rectangle) {
return "Envelope";
return "envelope";
}
});
}
Expand Down
307 changes: 307 additions & 0 deletions server/src/main/java/org/elasticsearch/common/geo/GeometryIO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
/*
* 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.common.geo;

import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
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.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
* Utility class for binary serializtion/deserialization of libs/geo classes
*/
public final class GeometryIO {

public static void writeGeometry(StreamOutput out, Geometry geometry) throws IOException {
out.writeString(GeoJson.getGeoJsonName(geometry).toLowerCase(Locale.ROOT));
geometry.visit(new GeometryVisitor<Void, IOException>() {
@Override
public Void visit(Circle circle) throws IOException {
throw new UnsupportedOperationException("circle is not supported");
}

@Override
public Void visit(GeometryCollection<?> collection) throws IOException {
out.writeVInt(collection.size());
for (Geometry shape : collection) {
writeGeometry(out, shape);
}
return null;
}

@Override
public Void visit(Line line) throws IOException {
writeCoordinates(line);
return null;
}

@Override
public Void visit(LinearRing ring) {
throw new UnsupportedOperationException("linear ring is not supported");
}

@Override
public Void visit(MultiLine multiLine) throws IOException {
out.writeVInt(multiLine.size());
for (Line line : multiLine) {
visit(line);
}
return null;
}

@Override
public Void visit(MultiPoint multiPoint) throws IOException {
out.writeVInt(multiPoint.size());
for (int i = 0; i < multiPoint.size(); i++) {
Point point = multiPoint.get(i);
writeCoordinate(point.getLat(), point.getLon(), point.getAlt());
}
return null;
}

@Override
public Void visit(MultiPolygon multiPolygon) throws IOException {
out.writeBoolean(true); // Orientation for BWC with ShapeBuilder
out.writeVInt(multiPolygon.size());
for (int i = 0; i < multiPolygon.size(); i++) {
visit(multiPolygon.get(i));
}
return null;
}

@Override
public Void visit(Point point) throws IOException {
out.writeVInt(1); // Number of points For BWC with Shape Builder
writeCoordinate(point.getLat(), point.getLon(), point.getAlt());
return null;
}

@Override
public Void visit(Polygon polygon) throws IOException {
writeCoordinates(polygon.getPolygon());
out.writeBoolean(true); // Orientation for BWC with ShapeBuilder
out.writeVInt(polygon.getNumberOfHoles());
for (int i = 0; i < polygon.getNumberOfHoles(); i++) {
writeCoordinates(polygon.getHole(i));
}
return null;
}

@Override
public Void visit(Rectangle rectangle) throws IOException {
writeCoordinate(rectangle.getMaxLat(), rectangle.getMinLon(), rectangle.getMinAlt()); // top left
writeCoordinate(rectangle.getMinLat(), rectangle.getMaxLon(), rectangle.getMaxAlt()); // bottom right
return null;
}

private void writeCoordinate(double lat, double lon, double alt) throws IOException {
out.writeDouble(lon);
out.writeDouble(lat);
out.writeOptionalDouble(Double.isNaN(alt) ? null : alt);
}

private void writeCoordinates(Line line) throws IOException {
out.writeVInt(line.length());
for (int i = 0; i < line.length(); i++) {
writeCoordinate(line.getLat(i), line.getLon(i), line.getAlt(i));
}
}

});
}

public static Geometry readGeometry(StreamInput in) throws IOException {
String type = in.readString();
switch (type) {
case "geometrycollection":
return readGeometryCollection(in);
case "polygon":
return readPolygon(in);
case "point":
return readPoint(in);
case "linestring":
return readLine(in);
case "multilinestring":
return readMultiLine(in);
case "multipoint":
return readMultiPoint(in);
case "multipolygon":
return readMultiPolygon(in);
case "envelope":
return readRectangle(in);
default:
throw new UnsupportedOperationException("unsupported shape type " + type);
}
}

private static GeometryCollection<Geometry> readGeometryCollection(StreamInput in) throws IOException {
int size = in.readVInt();
List<Geometry> shapes = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
shapes.add(readGeometry(in));
}
return new GeometryCollection<>(shapes);
}

private static Polygon readPolygon(StreamInput in) throws IOException {
double[][] shellComponents = readLineComponents(in);
boolean orientation = in.readBoolean();
LinearRing shell = buildLinearRing(shellComponents, orientation);
int numberOfHoles = in.readVInt();
if (numberOfHoles > 0) {
List<LinearRing> holes = new ArrayList<>(numberOfHoles);
for (int i = 0; i < numberOfHoles; i++) {
holes.add(buildLinearRing(readLineComponents(in), orientation));
}
return new Polygon(shell, holes);
} else {
return new Polygon(shell);
}
}

private static double[][] readLineComponents(StreamInput in) throws IOException {
int len = in.readVInt();
double[] lat = new double[len];
double[] lon = new double[len];
double[] alt = new double[len];
for (int i = 0; i < len; i++) {
lon[i] = in.readDouble();
lat[i] = in.readDouble();
alt[i] = readAlt(in);
}
if (Double.isNaN(alt[0])) {
return new double[][]{lat, lon};
} else {
return new double[][]{lat, lon, alt};
}
}

private static void reverse(double[][] arr) {
for (double[] carr : arr) {
int len = carr.length;
for (int j = 0; j < len / 2; j++) {
double temp = carr[j];
carr[j] = carr[len - j - 1];
carr[len - j - 1] = temp;
}
}
}

private static LinearRing buildLinearRing(double[][] arr, boolean orientation) {
if (orientation == false) {
reverse(arr);
}
if (arr.length == 3) {
return new LinearRing(arr[0], arr[1], arr[2]);
} else {
return new LinearRing(arr[0], arr[1]);
}
}

private static Point readPoint(StreamInput in) throws IOException {
int size = in.readVInt(); // For BWC with Shape Builder
if (size != 1) {
throw new IOException("Unexpected point count " + size);
}
double lon = in.readDouble();
double lat = in.readDouble();
double alt = readAlt(in);
return new Point(lat, lon, alt);
}

private static Line readLine(StreamInput in) throws IOException {
double[][] coords = readLineComponents(in);
if (coords.length == 3) {
return new Line(coords[0], coords[1], coords[2]);
} else {
return new Line(coords[0], coords[1]);
}
}

private static MultiLine readMultiLine(StreamInput in) throws IOException {
int size = in.readVInt();
List<Line> lines = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
lines.add(readLine(in));
}
return new MultiLine(lines);
}

private static MultiPoint readMultiPoint(StreamInput in) throws IOException {
int size = in.readVInt();
List<Point> points = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
double lon = in.readDouble();
double lat = in.readDouble();
double alt = readAlt(in);
points.add(new Point(lat, lon, alt));
}
return new MultiPoint(points);
}


private static MultiPolygon readMultiPolygon(StreamInput in) throws IOException {
in.readBoolean(); // orientation for BWC
int size = in.readVInt();
List<Polygon> polygons = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
polygons.add(readPolygon(in));
}
return new MultiPolygon(polygons);
}

private static Rectangle readRectangle(StreamInput in) throws IOException {
// top left
double minLon = in.readDouble();
double maxLat = in.readDouble();
double minAlt = readAlt(in);

// bottom right
double maxLon = in.readDouble();
double minLat = in.readDouble();
double maxAlt = readAlt(in);

return new Rectangle(minLat, maxLat, minLon, maxLon, minAlt, maxAlt);
}

private static double readAlt(StreamInput in) throws IOException {
Double alt = in.readOptionalDouble();
if (alt == null) {
return Double.NaN;
} else {
return alt;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ private List<Line> decompose(double dateline, double[] lons, double[] lats) {

for (int i = 1; i < lons.length; i++) {
double t = intersection(lastLon, lons[i], dateline);
lastLon = lons[i];
if (Double.isNaN(t) == false) {
double[] partLons = Arrays.copyOfRange(lons, offset, i + 1);
double[] partLats = Arrays.copyOfRange(lats, offset, i + 1);
Expand Down Expand Up @@ -330,7 +331,7 @@ private void validateHole(LinearRing shell, LinearRing hole) {
exterior.add(new Point(shell.getLat(i), shell.getLon(i)));
}
for (int i = 0; i < hole.length(); i++) {
interior.remove(new Point(hole.getLat(i), hole.getLon(i)));
interior.add(new Point(hole.getLat(i), hole.getLon(i)));
}
exterior.retainAll(interior);
if (exterior.size() >= 2) {
Expand Down Expand Up @@ -645,7 +646,7 @@ private static Edge[] concat(int component, boolean direction, Point[] points, f
edges[edgeOffset + i - 1].next = edges[edgeOffset + i] = new Edge(nextPoint, null);
edges[edgeOffset + i - 1].component = component;
} else {
throw new InvalidShapeException("Provided shape has duplicate consecutive coordinates at: " + nextPoint);
throw new InvalidShapeException("Provided shape has duplicate consecutive coordinates at: (" + nextPoint + ")");
}
}

Expand Down
Loading

0 comments on commit 0355d0c

Please sign in to comment.