Skip to content

Commit

Permalink
Geo: deprecate ShapeBuilder in QueryBuilders (#44715)
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 #40908
  • Loading branch information
imotov committed Jul 24, 2019
1 parent b3d8b39 commit f9943a3
Show file tree
Hide file tree
Showing 13 changed files with 655 additions and 184 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<String, RuntimeException>() {
@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 f9943a3

Please sign in to comment.