mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-04-19 04:45:07 -04:00
Support explicit Z/M attributes using WKT geometry (#125896)
This commit is contained in:
parent
30b2a1f729
commit
856ee3a177
10 changed files with 271 additions and 1 deletions
5
docs/changelog/125896.yaml
Normal file
5
docs/changelog/125896.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
pr: 125896
|
||||
summary: Support explicit Z/M attributes using WKT geometry
|
||||
area: Geo
|
||||
type: enhancement
|
||||
issues: [123111]
|
|
@ -44,6 +44,8 @@ public class WellKnownText {
|
|||
public static final String RPAREN = ")";
|
||||
public static final String COMMA = ",";
|
||||
public static final String NAN = "NaN";
|
||||
public static final String Z = "Z";
|
||||
public static final String M = "M";
|
||||
public static final int MAX_NESTED_DEPTH = 1000;
|
||||
|
||||
private static final String NUMBER = "<NUMBER>";
|
||||
|
@ -440,7 +442,8 @@ public class WellKnownText {
|
|||
*/
|
||||
private static Geometry parseGeometry(StreamTokenizer stream, boolean coerce, int depth) throws IOException, ParseException {
|
||||
final String type = nextWord(stream).toLowerCase(Locale.ROOT);
|
||||
return switch (type) {
|
||||
final boolean isExplicitlySpecifiesZorM = isZOrMNext(stream);
|
||||
Geometry geometry = switch (type) {
|
||||
case "point" -> parsePoint(stream);
|
||||
case "multipoint" -> parseMultiPoint(stream);
|
||||
case "linestring" -> parseLine(stream);
|
||||
|
@ -453,6 +456,16 @@ public class WellKnownText {
|
|||
parseCircle(stream);
|
||||
default -> throw new IllegalArgumentException("Unknown geometry type: " + type);
|
||||
};
|
||||
checkZorMAttribute(isExplicitlySpecifiesZorM, geometry.hasZ());
|
||||
return geometry;
|
||||
}
|
||||
|
||||
private static void checkZorMAttribute(boolean isExplicitlySpecifiesZorM, boolean hasZ) {
|
||||
if (isExplicitlySpecifiesZorM && hasZ == false) {
|
||||
throw new IllegalArgumentException(
|
||||
"When specifying 'Z' or 'M', coordinates must include three values. Only two coordinates were provided"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static GeometryCollection<Geometry> parseGeometryCollection(StreamTokenizer stream, boolean coerce, int depth)
|
||||
|
@ -710,6 +723,21 @@ public class WellKnownText {
|
|||
return type == StreamTokenizer.TT_WORD;
|
||||
}
|
||||
|
||||
private static boolean isZOrMNext(StreamTokenizer stream) {
|
||||
String token;
|
||||
try {
|
||||
token = nextWord(stream);
|
||||
if (token.equals(Z) || token.equals(M)) {
|
||||
return true;
|
||||
}
|
||||
stream.pushBack();
|
||||
return false;
|
||||
} catch (ParseException | IOException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String nextEmptyOrOpen(StreamTokenizer stream) throws IOException, ParseException {
|
||||
final String next = nextWord(stream);
|
||||
if (next.equals(EMPTY) || next.equals(LPAREN)) {
|
||||
|
|
|
@ -21,6 +21,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
|
||||
abstract class BaseGeometryTestCase<T extends Geometry> extends AbstractWireTestCase<T> {
|
||||
|
||||
public static final String ZorMMustIncludeThreeValuesMsg =
|
||||
"When specifying 'Z' or 'M', coordinates must include three values. Only two coordinates were provided";
|
||||
|
||||
@Override
|
||||
protected final T createTestInstance() {
|
||||
boolean hasAlt = randomBoolean();
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.io.IOException;
|
|||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
|
@ -93,6 +94,35 @@ public class GeometryCollectionTests extends BaseGeometryTestCase<GeometryCollec
|
|||
return count;
|
||||
}
|
||||
|
||||
public void testParseGeometryCollectionZorMWithThreeCoordinates() throws IOException, ParseException {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
|
||||
GeometryCollection<Geometry> expected = new GeometryCollection<>(
|
||||
Arrays.asList(
|
||||
new Point(20.0, 10.0, 100.0),
|
||||
new Line(new double[] { 10.0, 20.0 }, new double[] { 5.0, 15.0 }, new double[] { 50.0, 150.0 })
|
||||
)
|
||||
);
|
||||
|
||||
String point = "(POINT Z (20.0 10.0 100.0)";
|
||||
String lineString = "LINESTRING M (10.0 5.0 50.0, 20.0 15.0 150.0)";
|
||||
assertEquals(expected, WellKnownText.fromWKT(validator, true, "GEOMETRYCOLLECTION Z " + point + ", " + lineString + ")"));
|
||||
|
||||
assertEquals(expected, WellKnownText.fromWKT(validator, true, "GEOMETRYCOLLECTION M " + point + ", " + lineString + ")"));
|
||||
}
|
||||
|
||||
public void testParseGeometryCollectionZorMWithTwoCoordinatesThrowsException() {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
List<String> gcWkt = List.of(
|
||||
"GEOMETRYCOLLECTION Z (POINT (20.0 10.0), LINESTRING (10.0 5.0, 20.0 15.0))",
|
||||
"GEOMETRYCOLLECTION M (POINT (20.0 10.0), LINESTRING (10.0 5.0, 20.0 15.0))"
|
||||
);
|
||||
for (String gc : gcWkt) {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> WellKnownText.fromWKT(validator, true, gc));
|
||||
assertEquals(ZorMMustIncludeThreeValuesMsg, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GeometryCollection<Geometry> mutateInstance(GeometryCollection<Geometry> instance) {
|
||||
return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.elasticsearch.geometry.utils.WellKnownText;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
|
||||
public class LineTests extends BaseGeometryTestCase<Line> {
|
||||
@Override
|
||||
|
@ -82,6 +83,25 @@ public class LineTests extends BaseGeometryTestCase<Line> {
|
|||
assertEquals("found Z value [6.0] but [ignore_z_value] parameter is [false]", ex.getMessage());
|
||||
}
|
||||
|
||||
public void testParseLineZorMWithThreeCoordinates() throws IOException, ParseException {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
|
||||
Line expectedZ = new Line(new double[] { 20.0, 30.0 }, new double[] { 10.0, 15.0 }, new double[] { 100.0, 200.0 });
|
||||
assertEquals(expectedZ, WellKnownText.fromWKT(validator, true, "LINESTRING Z (20.0 10.0 100.0, 30.0 15.0 200.0)"));
|
||||
|
||||
Line expectedM = new Line(new double[] { 20.0, 30.0 }, new double[] { 10.0, 15.0 }, new double[] { 100.0, 200.0 });
|
||||
assertEquals(expectedM, WellKnownText.fromWKT(validator, true, "LINESTRING M (20.0 10.0 100.0, 30.0 15.0 200.0)"));
|
||||
}
|
||||
|
||||
public void testParseLineZorMWithTwoCoordinatesThrowsException() {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
List<String> linesWkt = List.of("LINESTRING Z (20.0 10.0, 30.0 15.0)", "LINESTRING M (20.0 10.0, 30.0 15.0)");
|
||||
for (String line : linesWkt) {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> WellKnownText.fromWKT(validator, true, line));
|
||||
assertEquals(ZorMMustIncludeThreeValuesMsg, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Line mutateInstance(Line instance) {
|
||||
return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
|
||||
|
|
|
@ -64,6 +64,54 @@ public class MultiLineTests extends BaseGeometryTestCase<MultiLine> {
|
|||
);
|
||||
}
|
||||
|
||||
public void testParseMultiLineZorMWithThreeCoordinates() throws IOException, ParseException {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
MultiLine expectedZ = new MultiLine(
|
||||
List.of(
|
||||
new Line(new double[] { 20.0, 30.0 }, new double[] { 10.0, 15.0 }, new double[] { 100.0, 200.0 }),
|
||||
new Line(new double[] { 40.0, 50.0 }, new double[] { 20.0, 25.0 }, new double[] { 300.0, 400.0 })
|
||||
)
|
||||
);
|
||||
assertEquals(
|
||||
expectedZ,
|
||||
WellKnownText.fromWKT(
|
||||
validator,
|
||||
true,
|
||||
"MULTILINESTRING Z ((20.0 10.0 100.0, 30.0 15.0 200.0), (40.0 20.0 300.0, 50.0 25.0 400.0))"
|
||||
)
|
||||
);
|
||||
|
||||
MultiLine expectedM = new MultiLine(
|
||||
List.of(
|
||||
new Line(new double[] { 20.0, 30.0 }, new double[] { 10.0, 15.0 }, new double[] { 100.0, 200.0 }),
|
||||
new Line(new double[] { 40.0, 50.0 }, new double[] { 20.0, 25.0 }, new double[] { 300.0, 400.0 })
|
||||
)
|
||||
);
|
||||
assertEquals(
|
||||
expectedM,
|
||||
WellKnownText.fromWKT(
|
||||
validator,
|
||||
true,
|
||||
"MULTILINESTRING M ((20.0 10.0 100.0, 30.0 15.0 200.0), (40.0 20.0 300.0, 50.0 25.0 400.0))"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void testParseMultiLineZorMWithTwoCoordinatesThrowsException() {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
List<String> multiLinesWkt = List.of(
|
||||
"MULTILINESTRING Z ((20.0 10.0, 30.0 15.0), (40.0 20.0, 50.0 25.0))",
|
||||
"MULTILINESTRING M ((20.0 10.0, 30.0 15.0), (40.0 20.0, 50.0 25.0))"
|
||||
);
|
||||
for (String multiLine : multiLinesWkt) {
|
||||
IllegalArgumentException ex = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> WellKnownText.fromWKT(validator, true, multiLine)
|
||||
);
|
||||
assertEquals(ZorMMustIncludeThreeValuesMsg, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiLine mutateInstance(MultiLine instance) {
|
||||
return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
|
||||
|
|
|
@ -71,6 +71,27 @@ public class MultiPointTests extends BaseGeometryTestCase<MultiPoint> {
|
|||
StandardValidator.instance(true).validate(new MultiPoint(Collections.singletonList(new Point(2, 1, 3))));
|
||||
}
|
||||
|
||||
public void testParseMultiPointWithThreeCoordinates() throws IOException, ParseException {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
MultiPoint expectedZ = new MultiPoint(Arrays.asList(new Point(10, 20, 30), new Point(40, 50, 60)));
|
||||
MultiPoint expectedM = new MultiPoint(Arrays.asList(new Point(10, 20, 30), new Point(40, 50, 60)));
|
||||
|
||||
assertEquals(expectedZ, WellKnownText.fromWKT(validator, true, "MULTIPOINT Z (10 20 30, 40 50 60)"));
|
||||
assertEquals(expectedM, WellKnownText.fromWKT(validator, true, "MULTIPOINT M (10 20 30, 40 50 60)"));
|
||||
}
|
||||
|
||||
public void testParseMultiPointWithTwoCoordinatesThrowsException() {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
List<String> multiPointsWkt = List.of("MULTIPOINT Z (10 20, 40 50)", "MULTIPOINT M (10 20, 40 50)");
|
||||
for (String multiPoint : multiPointsWkt) {
|
||||
IllegalArgumentException ex = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> WellKnownText.fromWKT(validator, true, multiPoint)
|
||||
);
|
||||
assertEquals(ZorMMustIncludeThreeValuesMsg, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiPoint mutateInstance(MultiPoint instance) {
|
||||
return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
|
||||
|
|
|
@ -80,6 +80,48 @@ public class MultiPolygonTests extends BaseGeometryTestCase<MultiPolygon> {
|
|||
);
|
||||
}
|
||||
|
||||
public void testParseMultiPolygonZorMWithThreeCoordinates() throws IOException, ParseException {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
|
||||
MultiPolygon expected = new MultiPolygon(
|
||||
List.of(
|
||||
new Polygon(
|
||||
new LinearRing(
|
||||
new double[] { 20.0, 30.0, 40.0, 20.0 },
|
||||
new double[] { 10.0, 15.0, 10.0, 10.0 },
|
||||
new double[] { 100.0, 200.0, 300.0, 100.0 }
|
||||
)
|
||||
),
|
||||
new Polygon(
|
||||
new LinearRing(
|
||||
new double[] { 0.0, 10.0, 10.0, 0.0 },
|
||||
new double[] { 0.0, 0.0, 10.0, 0.0 },
|
||||
new double[] { 10.0, 20.0, 30.0, 10.0 }
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
String polygonA = "(20.0 10.0 100.0, 30.0 15.0 200.0, 40.0 10.0 300.0, 20.0 10.0 100.0)";
|
||||
String polygonB = "(0.0 0.0 10.0, 10.0 0.0 20.0, 10.0 10.0 30.0, 0.0 0.0 10.0)";
|
||||
assertEquals(expected, WellKnownText.fromWKT(validator, true, "MULTIPOLYGON Z ((" + polygonA + "), (" + polygonB + "))"));
|
||||
assertEquals(expected, WellKnownText.fromWKT(validator, true, "MULTIPOLYGON M ((" + polygonA + "), (" + polygonB + "))"));
|
||||
}
|
||||
|
||||
public void testParseMultiPolygonZorMWithTwoCoordinatesThrowsException() {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
List<String> multiPolygonsWkt = List.of(
|
||||
"MULTIPOLYGON Z (((20.0 10.0, 30.0 15.0, 40.0 10.0, 20.0 10.0)), ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 0.0)))",
|
||||
"MULTIPOLYGON M (((20.0 10.0, 30.0 15.0, 40.0 10.0, 20.0 10.0)), ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 0.0)))"
|
||||
);
|
||||
for (String multiPolygon : multiPolygonsWkt) {
|
||||
IllegalArgumentException ex = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> WellKnownText.fromWKT(validator, true, multiPolygon)
|
||||
);
|
||||
assertEquals(ZorMMustIncludeThreeValuesMsg, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiPolygon mutateInstance(MultiPolygon instance) {
|
||||
return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
|
||||
|
|
|
@ -17,8 +17,10 @@ import org.elasticsearch.geometry.utils.WellKnownText;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
|
||||
public class PointTests extends BaseGeometryTestCase<Point> {
|
||||
|
||||
@Override
|
||||
protected Point createTestInstance(boolean hasAlt) {
|
||||
return GeometryTestUtils.randomPoint(hasAlt);
|
||||
|
@ -58,6 +60,21 @@ public class PointTests extends BaseGeometryTestCase<Point> {
|
|||
assertEquals("found Z value [100.0] but [ignore_z_value] parameter is [false]", ex.getMessage());
|
||||
}
|
||||
|
||||
public void testParsePointZorMWithThreeCoordinates() 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)"));
|
||||
assertEquals(new Point(20, 10, 100), WellKnownText.fromWKT(validator, true, "POINT M (20.0 10.0 100.0)"));
|
||||
}
|
||||
|
||||
public void testParsePointZorMWithTwoCoordinatesThrowsException() {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
List<String> pointsWkt = List.of("POINT Z (20.0 10.0)", "POINT M (20.0 10.0)");
|
||||
for (String point : pointsWkt) {
|
||||
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> WellKnownText.fromWKT(validator, true, point));
|
||||
assertEquals(ZorMMustIncludeThreeValuesMsg, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Point mutateInstance(Point instance) {
|
||||
return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.elasticsearch.geometry.utils.WellKnownText;
|
|||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PolygonTests extends BaseGeometryTestCase<Polygon> {
|
||||
@Override
|
||||
|
@ -134,6 +135,61 @@ public class PolygonTests extends BaseGeometryTestCase<Polygon> {
|
|||
);
|
||||
}
|
||||
|
||||
public void testParsePolygonZorMWithThreeCoordinates() throws IOException, ParseException {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
|
||||
Polygon expectedZ = new Polygon(
|
||||
new LinearRing(
|
||||
new double[] { 20.0, 30.0, 40.0, 20.0 },
|
||||
new double[] { 10.0, 15.0, 10.0, 10.0 },
|
||||
new double[] { 100.0, 200.0, 300.0, 100.0 }
|
||||
)
|
||||
);
|
||||
assertEquals(
|
||||
expectedZ,
|
||||
WellKnownText.fromWKT(validator, true, "POLYGON Z ((20.0 10.0 100.0, 30.0 15.0 200.0, 40.0 10.0 300.0, 20.0 10.0 100.0))")
|
||||
);
|
||||
|
||||
Polygon expectedM = new Polygon(
|
||||
new LinearRing(
|
||||
new double[] { 20.0, 30.0, 40.0, 20.0 },
|
||||
new double[] { 10.0, 15.0, 10.0, 10.0 },
|
||||
new double[] { 100.0, 200.0, 300.0, 100.0 }
|
||||
)
|
||||
);
|
||||
assertEquals(
|
||||
expectedM,
|
||||
WellKnownText.fromWKT(validator, true, "POLYGON M ((20.0 10.0 100.0, 30.0 15.0 200.0, 40.0 10.0 300.0, 20.0 10.0 100.0))")
|
||||
);
|
||||
|
||||
Polygon expectedZAutoClose = new Polygon(
|
||||
new LinearRing(
|
||||
new double[] { 20.0, 30.0, 40.0, 20.0 },
|
||||
new double[] { 10.0, 15.0, 10.0, 10.0 },
|
||||
new double[] { 100.0, 200.0, 300.0, 100.0 }
|
||||
)
|
||||
);
|
||||
assertEquals(
|
||||
expectedZAutoClose,
|
||||
WellKnownText.fromWKT(validator, true, "POLYGON Z ((20.0 10.0 100.0, 30.0 15.0 200.0, 40.0 10.0 300.0))")
|
||||
);
|
||||
}
|
||||
|
||||
public void testParsePolygonZorMWithTwoCoordinatesThrowsException() {
|
||||
GeometryValidator validator = GeographyValidator.instance(true);
|
||||
List<String> polygonsWkt = List.of(
|
||||
"POLYGON Z ((20.0 10.0, 30.0 15.0, 40.0 10.0, 20.0 10.0))",
|
||||
"POLYGON M ((20.0 10.0, 30.0 15.0, 40.0 10.0, 20.0 10.0))"
|
||||
);
|
||||
for (String polygon : polygonsWkt) {
|
||||
IllegalArgumentException ex = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> WellKnownText.fromWKT(validator, true, polygon)
|
||||
);
|
||||
assertEquals(ZorMMustIncludeThreeValuesMsg, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Polygon mutateInstance(Polygon instance) {
|
||||
return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
|
||||
|
|
Loading…
Add table
Reference in a new issue