Geometry.java
package com.surrealdb;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
/**
* A SurrealDB geometry value. Supports all GeoJSON-style types: {@code Point},
* {@code LineString}, {@code Polygon}, {@code MultiPoint},
* {@code MultiLineString}, {@code MultiPolygon}, and
* {@code GeometryCollection}.
* <p>
* Coordinates use {@link java.awt.geom.Point2D.Double} where {@code x} is the
* longitude and {@code y} is the latitude (GeoJSON ordering). Multi-coordinate
* types are exposed as nested {@link List}s of points.
* <p>
* Instances can be read from query results (via {@link Value#getGeometry()})
* and constructed with the static factory methods to be sent back to the
* database.
*/
public class Geometry extends Native {
Geometry(long ptr) {
super(ptr);
}
// ---- native: read ----
private static native boolean isPoint(long ptr);
private static native double[] getPoint(long ptr);
private static native String getType(long ptr);
private static native long getCoordinates(long ptr);
private static native long[] getCollection(long ptr);
// ---- native: write ----
private static native long newPoint(double x, double y);
private static native long newLineString(double[] coords);
private static native long newPolygon(double[][] rings);
private static native long newMultiPoint(double[] coords);
private static native long newMultiLineString(double[][] lines);
private static native long newMultiPolygon(double[][][] polygons);
private static native long newCollection(long[] geometryPtrs);
@Override
final native String toString(long ptr);
@Override
final native int hashCode(long ptr);
@Override
final native boolean equals(long ptr1, long ptr2);
@Override
final native void deleteInstance(long ptr);
// ---- type discrimination ----
/**
* Returns the geometry type: one of {@code "Point"}, {@code "LineString"},
* {@code "Polygon"}, {@code "MultiPoint"}, {@code "MultiLineString"},
* {@code "MultiPolygon"}, or {@code "GeometryCollection"}.
*/
public String getType() {
return getType(getPtr());
}
public boolean isPoint() {
return isPoint(getPtr());
}
public boolean isLineString() {
return "LineString".equals(getType());
}
public boolean isPolygon() {
return "Polygon".equals(getType());
}
public boolean isMultiPoint() {
return "MultiPoint".equals(getType());
}
public boolean isMultiLineString() {
return "MultiLineString".equals(getType());
}
public boolean isMultiPolygon() {
return "MultiPolygon".equals(getType());
}
public boolean isGeometryCollection() {
return "GeometryCollection".equals(getType());
}
// ---- readers ----
public Point2D.Double getPoint() {
final double[] coord = getPoint(getPtr());
return new Point2D.Double(coord[0], coord[1]);
}
public List<Point2D.Double> getLineString() {
return parseLine(coordinates());
}
public List<Point2D.Double> getMultiPoint() {
return parseLine(coordinates());
}
/**
* Returns the polygon's rings. The first ring is the exterior boundary; any
* subsequent rings are interior holes.
*/
public List<List<Point2D.Double>> getPolygon() {
return parseRings(coordinates());
}
public List<List<Point2D.Double>> getMultiLineString() {
return parseRings(coordinates());
}
public List<List<List<Point2D.Double>>> getMultiPolygon() {
final Array polygons = coordinates();
final List<List<List<Point2D.Double>>> result = new ArrayList<>(polygons.len());
for (int i = 0; i < polygons.len(); i++) {
result.add(parseRings(polygons.get(i).getArray()));
}
return result;
}
public List<Geometry> getGeometryCollection() {
final long[] ptrs = getCollection(getPtr());
final List<Geometry> result = new ArrayList<>(ptrs.length);
for (final long ptr : ptrs) {
result.add(new Geometry(ptr));
}
return result;
}
// ---- factories (x = longitude, y = latitude) ----
public static Geometry point(double x, double y) {
return new Geometry(newPoint(x, y));
}
public static Geometry point(Point2D.Double point) {
return new Geometry(newPoint(point.x, point.y));
}
public static Geometry lineString(List<Point2D.Double> points) {
return new Geometry(newLineString(flatten(points)));
}
public static Geometry multiPoint(List<Point2D.Double> points) {
return new Geometry(newMultiPoint(flatten(points)));
}
/**
* Builds a polygon. The first ring is the exterior boundary; any subsequent
* rings are interior holes.
*/
public static Geometry polygon(List<List<Point2D.Double>> rings) {
return new Geometry(newPolygon(flattenRings(rings)));
}
public static Geometry multiLineString(List<List<Point2D.Double>> lines) {
return new Geometry(newMultiLineString(flattenRings(lines)));
}
public static Geometry multiPolygon(List<List<List<Point2D.Double>>> polygons) {
final double[][][] data = new double[polygons.size()][][];
for (int i = 0; i < polygons.size(); i++) {
data[i] = flattenRings(polygons.get(i));
}
return new Geometry(newMultiPolygon(data));
}
public static Geometry geometryCollection(List<Geometry> geometries) {
final long[] ptrs = new long[geometries.size()];
for (int i = 0; i < geometries.size(); i++) {
ptrs[i] = geometries.get(i).getPtr();
}
return new Geometry(newCollection(ptrs));
}
// ---- helpers ----
/** The geometry's coordinates as a nested array of {@code [x, y]} pairs. */
private Array coordinates() {
return new Value(getCoordinates(getPtr())).getArray();
}
private static Point2D.Double parsePoint(Value coordinate) {
final Array xy = coordinate.getArray();
return new Point2D.Double(xy.get(0).getDouble(), xy.get(1).getDouble());
}
private static List<Point2D.Double> parseLine(Array line) {
final List<Point2D.Double> points = new ArrayList<>(line.len());
for (int i = 0; i < line.len(); i++) {
points.add(parsePoint(line.get(i)));
}
return points;
}
private static List<List<Point2D.Double>> parseRings(Array rings) {
final List<List<Point2D.Double>> result = new ArrayList<>(rings.len());
for (int i = 0; i < rings.len(); i++) {
result.add(parseLine(rings.get(i).getArray()));
}
return result;
}
private static double[] flatten(List<Point2D.Double> points) {
final double[] coords = new double[points.size() * 2];
for (int i = 0; i < points.size(); i++) {
coords[i * 2] = points.get(i).x;
coords[i * 2 + 1] = points.get(i).y;
}
return coords;
}
private static double[][] flattenRings(List<List<Point2D.Double>> rings) {
final double[][] data = new double[rings.size()][];
for (int i = 0; i < rings.size(); i++) {
data[i] = flatten(rings.get(i));
}
return data;
}
}