|
@@ -24,6 +24,8 @@ import org.elasticsearch.geometry.Rectangle;
|
|
|
import java.io.IOException;
|
|
|
import java.io.StreamTokenizer;
|
|
|
import java.io.StringReader;
|
|
|
+import java.nio.ByteBuffer;
|
|
|
+import java.nio.ByteOrder;
|
|
|
import java.text.ParseException;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Collections;
|
|
@@ -215,6 +217,196 @@ public class WellKnownText {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ public static String fromWKB(byte[] wkb, int offset, int length) {
|
|
|
+ final StringBuilder builder = new StringBuilder();
|
|
|
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(wkb, offset, length);
|
|
|
+ parseGeometry(byteBuffer, builder);
|
|
|
+ assert byteBuffer.remaining() == 0;
|
|
|
+ return builder.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseGeometry(ByteBuffer byteBuffer, StringBuilder sb) {
|
|
|
+ byteBuffer.order(byteBuffer.get() == 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
|
|
|
+ final int type = byteBuffer.getInt();
|
|
|
+ switch (type) {
|
|
|
+ case 1 -> parsePoint(byteBuffer, false, sb);
|
|
|
+ case 1001 -> parsePoint(byteBuffer, true, sb);
|
|
|
+ case 2 -> parseLine(byteBuffer, false, sb);
|
|
|
+ case 1002 -> parseLine(byteBuffer, true, sb);
|
|
|
+ case 3 -> parsePolygon(byteBuffer, false, sb);
|
|
|
+ case 1003 -> parsePolygon(byteBuffer, true, sb);
|
|
|
+ case 4 -> parseMultiPoint(byteBuffer, false, sb);
|
|
|
+ case 1004 -> parseMultiPoint(byteBuffer, true, sb);
|
|
|
+ case 5 -> parseMultiLine(byteBuffer, false, sb);
|
|
|
+ case 1005 -> parseMultiLine(byteBuffer, true, sb);
|
|
|
+ case 6 -> parseMultiPolygon(byteBuffer, false, sb);
|
|
|
+ case 1006 -> parseMultiPolygon(byteBuffer, true, sb);
|
|
|
+ case 7, 1007 -> parseGeometryCollection(byteBuffer, sb);
|
|
|
+ case 17 -> parseCircle(byteBuffer, false, sb);
|
|
|
+ case 1017 -> parseCircle(byteBuffer, true, sb);
|
|
|
+ case 18 -> parseBBox(byteBuffer, false, sb);
|
|
|
+ case 1018 -> parseBBox(byteBuffer, true, sb);
|
|
|
+ default -> throw new IllegalArgumentException("Unknown geometry type: " + type);
|
|
|
+ }
|
|
|
+ ;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void writeCoordinate(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ sb.append(byteBuffer.getDouble()).append(SPACE).append(byteBuffer.getDouble());
|
|
|
+ if (hasZ) {
|
|
|
+ sb.append(SPACE).append(byteBuffer.getDouble());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parsePoint(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ sb.append("POINT").append(SPACE);
|
|
|
+ sb.append(LPAREN);
|
|
|
+ writeCoordinate(byteBuffer, hasZ, sb);
|
|
|
+ sb.append(RPAREN);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseMultiPoint(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ sb.append("MULTIPOINT").append(SPACE);
|
|
|
+ final int numPoints = byteBuffer.getInt();
|
|
|
+ if (numPoints == 0) {
|
|
|
+ sb.append(EMPTY);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sb.append(LPAREN);
|
|
|
+ for (int i = 0; i < numPoints; i++) {
|
|
|
+ byteBuffer.order(byteBuffer.get() == 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
|
|
|
+ byteBuffer.getInt();
|
|
|
+ writeCoordinate(byteBuffer, hasZ, sb);
|
|
|
+ if (i != numPoints - 1) {
|
|
|
+ sb.append(COMMA);
|
|
|
+ sb.append(SPACE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sb.append(RPAREN);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseLine(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ sb.append("LINESTRING").append(SPACE);
|
|
|
+ parseLineString(byteBuffer, hasZ, sb);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseMultiLine(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ sb.append("MULTILINESTRING").append(SPACE);
|
|
|
+ final int numLines = byteBuffer.getInt();
|
|
|
+ if (numLines == 0) {
|
|
|
+ sb.append(EMPTY);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sb.append(LPAREN);
|
|
|
+ for (int i = 0; i < numLines; i++) {
|
|
|
+ byteBuffer.order(byteBuffer.get() == 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
|
|
|
+ byteBuffer.getInt();
|
|
|
+ parseLineString(byteBuffer, hasZ, sb);
|
|
|
+ if (i != numLines - 1) {
|
|
|
+ sb.append(COMMA);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sb.append(RPAREN);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parsePolygon(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ sb.append("POLYGON").append(SPACE);
|
|
|
+ parseRings(byteBuffer, hasZ, sb, byteBuffer.getInt());
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseRings(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb, int numRings) {
|
|
|
+ if (numRings == 0) {
|
|
|
+ sb.append(EMPTY);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sb.append(LPAREN);
|
|
|
+ parseLineString(byteBuffer, hasZ, sb);
|
|
|
+ for (int i = 1; i < numRings; i++) {
|
|
|
+ sb.append(COMMA);
|
|
|
+ sb.append(SPACE);
|
|
|
+ parseLineString(byteBuffer, hasZ, sb);
|
|
|
+ }
|
|
|
+ sb.append(RPAREN);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseMultiPolygon(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ sb.append("MULTIPOLYGON").append(SPACE);
|
|
|
+ final int numPolygons = byteBuffer.getInt();
|
|
|
+ if (numPolygons == 0) {
|
|
|
+ sb.append(EMPTY);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sb.append(LPAREN);
|
|
|
+ for (int i = 0; i < numPolygons; i++) {
|
|
|
+ byteBuffer.order(byteBuffer.get() == 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
|
|
|
+ byteBuffer.getInt();
|
|
|
+ parseRings(byteBuffer, hasZ, sb, byteBuffer.getInt());
|
|
|
+ if (i != numPolygons - 1) {
|
|
|
+ sb.append(COMMA);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sb.append(RPAREN);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseLineString(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ final int length = byteBuffer.getInt();
|
|
|
+ if (length == 0) {
|
|
|
+ sb.append(EMPTY);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sb.append(LPAREN);
|
|
|
+ for (int i = 0; i < length; i++) {
|
|
|
+ writeCoordinate(byteBuffer, hasZ, sb);
|
|
|
+ if (i != length - 1) {
|
|
|
+ sb.append(COMMA);
|
|
|
+ sb.append(SPACE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sb.append(RPAREN);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseGeometryCollection(ByteBuffer byteBuffer, StringBuilder sb) {
|
|
|
+ sb.append("GEOMETRYCOLLECTION").append(SPACE);
|
|
|
+ final int numGeometries = byteBuffer.getInt();
|
|
|
+ if (numGeometries == 0) {
|
|
|
+ sb.append(EMPTY);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sb.append(LPAREN);
|
|
|
+ for (int i = 0; i < numGeometries; i++) {
|
|
|
+ parseGeometry(byteBuffer, sb);
|
|
|
+ if (i != numGeometries - 1) {
|
|
|
+ sb.append(COMMA);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sb.append(RPAREN);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseCircle(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ sb.append("CIRCLE").append(SPACE);
|
|
|
+ sb.append(LPAREN);
|
|
|
+ sb.append(byteBuffer.getDouble()).append(SPACE).append(byteBuffer.getDouble());
|
|
|
+ final double r = byteBuffer.getDouble();
|
|
|
+ if (hasZ) {
|
|
|
+ sb.append(SPACE).append(byteBuffer.getDouble()).append(SPACE).append(r);
|
|
|
+ } else {
|
|
|
+ sb.append(SPACE).append(r);
|
|
|
+ }
|
|
|
+ sb.append(RPAREN);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void parseBBox(ByteBuffer byteBuffer, boolean hasZ, StringBuilder sb) {
|
|
|
+ sb.append("BBOX").append(SPACE);
|
|
|
+ sb.append(LPAREN);
|
|
|
+ sb.append(byteBuffer.getDouble()).append(COMMA).append(SPACE).append(byteBuffer.getDouble());
|
|
|
+ sb.append(COMMA).append(SPACE).append(byteBuffer.getDouble()).append(COMMA).append(SPACE).append(byteBuffer.getDouble());
|
|
|
+ if (hasZ) {
|
|
|
+ sb.append(COMMA).append(SPACE).append(byteBuffer.getDouble()).append(COMMA).append(SPACE).append(byteBuffer.getDouble());
|
|
|
+ }
|
|
|
+ sb.append(RPAREN);
|
|
|
+ }
|
|
|
+
|
|
|
public static Geometry fromWKT(GeometryValidator validator, boolean coerce, String wkt) throws IOException, ParseException {
|
|
|
StringReader reader = new StringReader(wkt);
|
|
|
try {
|