use of org.opensearch.geometry.Point in project OpenSearch by opensearch-project.
the class GeoPolygonDecomposer method holes.
private static Point[][] holes(Edge[] holes, int numHoles) {
if (numHoles == 0) {
return new Point[0][];
}
final Point[][] points = new Point[numHoles][];
for (int i = 0; i < numHoles; i++) {
double[] partitionPoint = new double[3];
// mark as visited by inverting the sign
int length = component(holes[i], -(i + 1), null, partitionPoint);
points[i] = coordinates(holes[i], new Point[length + 1], partitionPoint);
}
return points;
}
use of org.opensearch.geometry.Point in project OpenSearch by opensearch-project.
the class GeoPolygonDecomposer method buildPolygon.
private static Polygon buildPolygon(List<Point[]> polygon) {
List<LinearRing> holes;
Point[] shell = polygon.get(0);
if (polygon.size() > 1) {
holes = new ArrayList<>(polygon.size() - 1);
for (int i = 1; i < polygon.size(); ++i) {
Point[] coords = polygon.get(i);
// We do not have holes on the dateline as they get eliminated
// when breaking the polygon around it.
double[] x = new double[coords.length];
double[] y = new double[coords.length];
for (int c = 0; c < coords.length; ++c) {
x[c] = normalizeLon(coords[c].getX());
y[c] = normalizeLat(coords[c].getY());
}
holes.add(new LinearRing(x, y));
}
} else {
holes = Collections.emptyList();
}
double[] x = new double[shell.length];
double[] y = new double[shell.length];
for (int i = 0; i < shell.length; ++i) {
// Lucene Tessellator treats different +180 and -180 and we should keep the sign.
// normalizeLon method excludes -180.
x[i] = normalizeLonMinus180Inclusive(shell[i].getX());
y[i] = normalizeLat(shell[i].getY());
}
return new Polygon(new LinearRing(x, y), holes);
}
use of org.opensearch.geometry.Point in project OpenSearch by opensearch-project.
the class GeoPolygonDecomposer method component.
/**
* This method sets the component id of all edges in a ring to a given id and shifts the
* coordinates of this component according to the dateline
*
* @param edge An arbitrary edge of the component
* @param id id to apply to the component
* @param edges a list of edges to which all edges of the component will be added (could be <code>null</code>)
* @return number of edges that belong to this component
*/
private static int component(final Edge edge, final int id, final ArrayList<Edge> edges, double[] partitionPoint) {
// find a coordinate that is not part of the dateline
Edge any = edge;
while (any.coordinate.getX() == +DATELINE || any.coordinate.getX() == -DATELINE) {
if ((any = any.next) == edge) {
break;
}
}
double shiftOffset = any.coordinate.getX() > DATELINE ? DATELINE : (any.coordinate.getX() < -DATELINE ? -DATELINE : 0);
// run along the border of the component, collect the
// edges, shift them according to the dateline and
// update the component id
int length = 0, connectedComponents = 0;
// if there are two connected components, splitIndex keeps track of where to split the edge array
// start at 1 since the source coordinate is shared
int splitIndex = 1;
Edge current = edge;
Edge prev = edge;
// bookkeep the source and sink of each visited coordinate
HashMap<Point, Tuple<Edge, Edge>> visitedEdge = new HashMap<>();
do {
current.coordinate = shift(current.coordinate, shiftOffset);
current.component = id;
if (edges != null) {
// found a closed loop - we have two connected components so we need to slice into two distinct components
if (visitedEdge.containsKey(current.coordinate)) {
partitionPoint[0] = current.coordinate.getX();
partitionPoint[1] = current.coordinate.getY();
partitionPoint[2] = current.coordinate.getZ();
if (connectedComponents > 0 && current.next != edge) {
throw new InvalidShapeException("Shape contains more than one shared point");
}
// a negative id flags the edge as visited for the edges(...) method.
// since we're splitting connected components, we want the edges method to visit
// the newly separated component
final int visitID = -id;
Edge firstAppearance = visitedEdge.get(current.coordinate).v2();
// correct the graph pointers by correcting the 'next' pointer for both the
// first appearance and this appearance of the edge
Edge temp = firstAppearance.next;
firstAppearance.next = current.next;
current.next = temp;
current.component = visitID;
// a non-visited value (anything positive)
do {
prev.component = visitID;
prev = visitedEdge.get(prev.coordinate).v1();
++splitIndex;
} while (!current.coordinate.equals(prev.coordinate));
++connectedComponents;
} else {
visitedEdge.put(current.coordinate, new Tuple<Edge, Edge>(prev, current));
}
edges.add(current);
prev = current;
}
length++;
} while (connectedComponents == 0 && (current = current.next) != edge);
return (splitIndex != 1) ? length - splitIndex : length;
}
use of org.opensearch.geometry.Point in project OpenSearch by opensearch-project.
the class GeometryParser method parseGeometry.
/**
* Parses the value as a {@link Geometry}. The following types of values are supported:
* <p>
* Object: has to contain either lat and lon or geohash fields
* <p>
* String: expected to be in "latitude, longitude" format, a geohash or WKT
* <p>
* Array: two or more elements, the first element is longitude, the second is latitude, the rest is ignored if ignoreZValue is true
* <p>
* Json structure: valid geojson definition
*/
public Geometry parseGeometry(Object value) throws OpenSearchParseException {
if (value instanceof List) {
List<?> values = (List<?>) value;
if (values.size() == 2 && values.get(0) instanceof Number) {
GeoPoint point = GeoUtils.parseGeoPoint(values, ignoreZValue);
return new Point(point.lon(), point.lat());
} else {
List<Geometry> geometries = new ArrayList<>(values.size());
for (Object object : values) {
geometries.add(parseGeometry(object));
}
return new GeometryCollection<>(geometries);
}
}
try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, Collections.singletonMap("null_value", value), null)) {
// start object
parser.nextToken();
// field name
parser.nextToken();
// field value
parser.nextToken();
if (isPoint(value)) {
GeoPoint point = GeoUtils.parseGeoPoint(parser, new GeoPoint(), ignoreZValue);
return new Point(point.lon(), point.lat());
} else {
return parse(parser);
}
} catch (IOException | ParseException ex) {
throw new OpenSearchParseException("error parsing geometry ", ex);
}
}
use of org.opensearch.geometry.Point in project OpenSearch by opensearch-project.
the class GeoPolygonDecomposer method decomposePolygon.
/**
* Splits the specified polygon by datelines and adds them to the supplied polygon array
*/
public static void decomposePolygon(Polygon polygon, boolean orientation, List<Polygon> collector) {
if (polygon.isEmpty()) {
return;
}
// Last point is repeated
int numEdges = polygon.getPolygon().length() - 1;
for (int i = 0; i < polygon.getNumberOfHoles(); i++) {
numEdges += polygon.getHole(i).length() - 1;
validateHole(polygon.getPolygon(), polygon.getHole(i));
}
Edge[] edges = new Edge[numEdges];
Edge[] holeComponents = new Edge[polygon.getNumberOfHoles()];
final AtomicBoolean translated = new AtomicBoolean(false);
int offset = createEdges(0, orientation, polygon.getPolygon(), null, edges, 0, translated);
for (int i = 0; i < polygon.getNumberOfHoles(); i++) {
int length = createEdges(i + 1, orientation, polygon.getPolygon(), polygon.getHole(i), edges, offset, translated);
holeComponents[i] = edges[offset];
offset += length;
}
int numHoles = holeComponents.length;
numHoles = merge(edges, 0, intersections(+DATELINE, edges), holeComponents, numHoles);
numHoles = merge(edges, 0, intersections(-DATELINE, edges), holeComponents, numHoles);
compose(edges, holeComponents, numHoles, collector);
}
Aggregations