Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libs/geo/src/main/java/org/elasticsearch/geometry/Point.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
public class Point implements Geometry {
public static final Point EMPTY = new Point();
public static final String Z = "Z";
public static final String M = "M";

private final double y;
private final double x;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,6 @@ private static void parseGeometry(ByteBuffer byteBuffer, StringBuilder 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) {
Expand Down Expand Up @@ -472,15 +471,23 @@ private static GeometryCollection<Geometry> parseGeometryCollection(StreamTokeni
}

private static Point parsePoint(StreamTokenizer stream) throws IOException, ParseException {
if (nextEmptyOrOpen(stream).equals(EMPTY)) {
if (isEmptyNext(stream)) {
return Point.EMPTY;
}
String token = nextZOrMOrOpen(stream);
boolean hasZOrM = token.equals(Point.Z) || token.equals(Point.M);
if (hasZOrM) {
nextOpener(stream);
}
double lon = nextNumber(stream);
double lat = nextNumber(stream);
Point pt;
if (isNumberNext(stream)) {
pt = new Point(lon, lat, nextNumber(stream));
} else {
if (hasZOrM) {
throw new ParseException("'POINT Z' or 'POINT M' must have three coordinates, but only two were found.", stream.lineno());
}
pt = new Point(lon, lat);
}
nextCloser(stream);
Expand Down Expand Up @@ -710,6 +717,14 @@ private static boolean isNumberNext(StreamTokenizer stream) throws IOException {
return type == StreamTokenizer.TT_WORD;
}

private static boolean isEmptyNext(StreamTokenizer stream) throws ParseException, IOException {
if (nextWord(stream).equals(EMPTY)) {
return true;
}
stream.pushBack();
return false;
}

private static String nextEmptyOrOpen(StreamTokenizer stream) throws IOException, ParseException {
final String next = nextWord(stream);
if (next.equals(EMPTY) || next.equals(LPAREN)) {
Expand All @@ -718,6 +733,17 @@ private static String nextEmptyOrOpen(StreamTokenizer stream) throws IOException
throw new ParseException("expected " + EMPTY + " or " + LPAREN + " but found: " + tokenString(stream), stream.lineno());
}

private static String nextZOrMOrOpen(StreamTokenizer stream) throws ParseException, IOException {
final String next = nextWord(stream);
if (next.equals(Point.Z) || next.equals(Point.M) || next.equals(LPAREN)) {
return next;
}
throw new ParseException(
"expected " + LPAREN + " or " + Point.Z + " or " + Point.M + " but found: " + tokenString(stream),
stream.lineno()
);
}

private static String nextCloser(StreamTokenizer stream) throws IOException, ParseException {
if (nextWord(stream).equals(RPAREN)) {
return RPAREN;
Expand Down
47 changes: 47 additions & 0 deletions libs/geo/src/test/java/org/elasticsearch/geometry/PointTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.io.IOException;
import java.text.ParseException;
import java.util.List;

public class PointTests extends BaseGeometryTestCase<Point> {
@Override
Expand Down Expand Up @@ -58,6 +59,52 @@ public void testWKTValidation() {
assertEquals("found Z value [100.0] but [ignore_z_value] parameter is [false]", ex.getMessage());
}

public void testParsePointZWithThreeCoordinates() throws IOException, ParseException {
GeometryValidator validator = GeographyValidator.instance(true);
assertEquals(new Point(20, 10, 100), WellKnownText.fromWKT(validator, true, "POINT Z (20.0 10.0 100.0)"));
}

public void testParsePointMWithThreeCoordinates() throws IOException, ParseException {
GeometryValidator validator = GeographyValidator.instance(true);
assertEquals(new Point(20, 10, 100), WellKnownText.fromWKT(validator, true, "POINT M (20.0 10.0 100.0)"));
}

public void testParsePointZWithTwoCoordinatesThrowsException() {
GeometryValidator validator = GeographyValidator.instance(true);
ParseException ex = expectThrows(ParseException.class, () -> WellKnownText.fromWKT(validator, true, "POINT Z (20.0 10.0)"));
assertEquals("'POINT Z' or 'POINT M' must have three coordinates, but only two were found.", ex.getMessage());
}

public void testParsePointMWithTwoCoordinatesThrowsException() {
GeometryValidator validator = StandardValidator.instance(true);
ParseException ex = expectThrows(ParseException.class, () -> WellKnownText.fromWKT(validator, true, "POINT M (20.0 10.0)"));
assertEquals("'POINT Z' or 'POINT M' must have three coordinates, but only two were found.", ex.getMessage());
}

public void testParsePointZMFormatNotSupported() {
GeometryValidator validator = StandardValidator.instance(true);
List<String> points = List.of("POINT ZM (20.0 10.0 100.0 200.0)", "POINT ZM (20.0 10.0 100.0)", "POINT ZM (20.0 10.0)");
for (String point : points) {
ParseException ex = expectThrows(ParseException.class, () -> WellKnownText.fromWKT(validator, true, point));
assertEquals("expected ( or Z or M but found: ZM", ex.getMessage());
}
}

public void testParsePointZWithEmpty() {
GeometryValidator validator = StandardValidator.instance(true);
ParseException ex = expectThrows(ParseException.class, () -> WellKnownText.fromWKT(validator, true, "POINT Z EMPTY"));
assertEquals("expected ( but found: EMPTY", ex.getMessage());
}

public void testParsePointZOrMWithTwoCoordinates() {
GeometryValidator validator = StandardValidator.instance(true);
List<String> points = List.of("POINT Z (20.0 10.0)", "POINT M (20.0 10.0)");
for (String point : points) {
ParseException ex = expectThrows(ParseException.class, () -> WellKnownText.fromWKT(validator, true, point));
assertEquals("'POINT Z' or 'POINT M' must have three coordinates, but only two were found.", ex.getMessage());
}
}

@Override
protected Point mutateInstance(Point instance) {
return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
Expand Down