提交 573e605d authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add support of empty points to GEOMETRY data type

上级 000f7bb6
...@@ -66,9 +66,6 @@ class AggregateDataEnvelope extends AggregateData { ...@@ -66,9 +66,6 @@ class AggregateDataEnvelope extends AggregateData {
@Override @Override
Value getValue(Database database, int dataType, boolean distinct) { Value getValue(Database database, int dataType, boolean distinct) {
if (envelope == null) {
return ValueNull.INSTANCE;
}
return ValueGeometry.fromEnvelope(envelope); return ValueGeometry.fromEnvelope(envelope);
} }
......
...@@ -134,10 +134,11 @@ public class SpatialTreeIndex extends BaseIndex implements SpatialIndex { ...@@ -134,10 +134,11 @@ public class SpatialTreeIndex extends BaseIndex implements SpatialIndex {
return null; return null;
} }
Value v = row.getValue(columnIds[0]); Value v = row.getValue(columnIds[0]);
if (v == ValueNull.INSTANCE) { double[] env;
if (v == ValueNull.INSTANCE ||
(env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy()) == null) {
return new SpatialKey(row.getKey()); return new SpatialKey(row.getKey());
} }
double[] env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy();
return new SpatialKey(row.getKey(), return new SpatialKey(row.getKey(),
(float) env[MIN_X], (float) env[MAX_X], (float) env[MIN_Y], (float) env[MAX_Y]); (float) env[MIN_X], (float) env[MAX_X], (float) env[MIN_Y], (float) env[MAX_Y]);
} }
......
...@@ -274,10 +274,11 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex { ...@@ -274,10 +274,11 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
private SpatialKey getKey(SearchRow row) { private SpatialKey getKey(SearchRow row) {
Value v = row.getValue(columnIds[0]); Value v = row.getValue(columnIds[0]);
if (v == ValueNull.INSTANCE) { double[] env;
if (v == ValueNull.INSTANCE ||
(env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy()) == null) {
return new SpatialKey(row.getKey()); return new SpatialKey(row.getKey());
} }
double[] env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy();
return new SpatialKey(row.getKey(), return new SpatialKey(row.getKey(),
(float) env[MIN_X], (float) env[MAX_X], (float) env[MIN_X], (float) env[MAX_X],
(float) env[MIN_Y], (float) env[MAX_Y]); (float) env[MIN_Y], (float) env[MAX_Y]);
......
...@@ -44,6 +44,8 @@ public final class EWKTUtils { ...@@ -44,6 +44,8 @@ public final class EWKTUtils {
private int level; private int level;
private int type;
private boolean inMulti; private boolean inMulti;
/** /**
...@@ -104,6 +106,7 @@ public final class EWKTUtils { ...@@ -104,6 +106,7 @@ public final class EWKTUtils {
} }
private void writeHeader(int type, int srid) { private void writeHeader(int type, int srid) {
this.type = type;
// Never write SRID in inner objects // Never write SRID in inner objects
if (level == 0 && srid != 0) { if (level == 0 && srid != 0) {
output.append("SRID=").append(srid).append(';'); output.append("SRID=").append(srid).append(';');
...@@ -177,6 +180,10 @@ public final class EWKTUtils { ...@@ -177,6 +180,10 @@ public final class EWKTUtils {
@Override @Override
protected void addCoordinate(double x, double y, double z, double m, int index, int total) { protected void addCoordinate(double x, double y, double z, double m, int index, int total) {
if (type == POINT && Double.isNaN(x) && Double.isNaN(y) && Double.isNaN(z) && Double.isNaN(m)) {
output.append("EMPTY");
return;
}
if (index == 0) { if (index == 0) {
output.append('('); output.append('(');
} else { } else {
...@@ -572,13 +579,17 @@ public final class EWKTUtils { ...@@ -572,13 +579,17 @@ public final class EWKTUtils {
} }
switch (type) { switch (type) {
case "POINT": case "POINT":
if (parentType != 0 && parentType != MULTI_POINT && parentType != GEOMETRY_COLLECTION || empty) { if (parentType != 0 && parentType != MULTI_POINT && parentType != GEOMETRY_COLLECTION) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
empty = source.readEmpty(empty);
target.startPoint(source.srid); target.startPoint(source.srid);
source.read('('); if (empty) {
addCoordinate(source, target, useZ, useM, 0, 1); target.addCoordinate(Double.NaN, Double.NaN, Double.NaN, Double.NaN, 0, 1);
source.read(')'); } else {
addCoordinate(source, target, useZ, useM, 0, 1);
source.read(')');
}
break; break;
case "LINESTRING": { case "LINESTRING": {
if (parentType != 0 && parentType != MULTI_LINE_STRING && parentType != GEOMETRY_COLLECTION) { if (parentType != 0 && parentType != MULTI_LINE_STRING && parentType != GEOMETRY_COLLECTION) {
......
...@@ -191,7 +191,7 @@ public final class GeometryUtils { ...@@ -191,7 +191,7 @@ public final class GeometryUtils {
@Override @Override
protected void addCoordinate(double x, double y, double z, double m, int index, int total) { protected void addCoordinate(double x, double y, double z, double m, int index, int total) {
if (enabled) { if (enabled && !Double.isNaN(x) && !Double.isNaN(y)) {
if (!set) { if (!set) {
minX = maxX = x; minX = maxX = x;
minY = maxY = y; minY = maxY = y;
...@@ -318,7 +318,7 @@ public final class GeometryUtils { ...@@ -318,7 +318,7 @@ public final class GeometryUtils {
if (!hasM && !Double.isNaN(m)) { if (!hasM && !Double.isNaN(m)) {
hasM = true; hasM = true;
} }
if (enabled) { if (enabled && !Double.isNaN(x) && !Double.isNaN(y)) {
if (!set) { if (!set) {
minX = maxX = x; minX = maxX = x;
minY = maxY = y; minY = maxY = y;
...@@ -467,7 +467,7 @@ public final class GeometryUtils { ...@@ -467,7 +467,7 @@ public final class GeometryUtils {
* @return envelope, or null * @return envelope, or null
*/ */
public static double[] getEnvelope(byte[] ewkb) { public static double[] getEnvelope(byte[] ewkb) {
EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget(); EnvelopeTarget target = new EnvelopeTarget();
EWKBUtils.parseEWKB(ewkb, target); EWKBUtils.parseEWKB(ewkb, target);
return target.getEnvelope(); return target.getEnvelope();
} }
......
...@@ -149,6 +149,10 @@ public final class JTSUtils { ...@@ -149,6 +149,10 @@ public final class JTSUtils {
@Override @Override
protected void addCoordinate(double x, double y, double z, double m, int index, int total) { protected void addCoordinate(double x, double y, double z, double m, int index, int total) {
if (type == POINT && Double.isNaN(x) && Double.isNaN(y) && Double.isNaN(z) && Double.isNaN(m)) {
this.coordinates = createCoordinates(0);
return;
}
CoordinateSequence coordinates = innerOffset < 0 ? this.coordinates : innerCoordinates[innerOffset]; CoordinateSequence coordinates = innerOffset < 0 ? this.coordinates : innerCoordinates[innerOffset];
coordinates.setOrdinate(index, X, x); coordinates.setOrdinate(index, X, x);
coordinates.setOrdinate(index, Y, y); coordinates.setOrdinate(index, Y, y);
...@@ -287,7 +291,11 @@ public final class JTSUtils { ...@@ -287,7 +291,11 @@ public final class JTSUtils {
} }
target.startPoint(srid); target.startPoint(srid);
Point p = (Point) geometry; Point p = (Point) geometry;
addCoordinate(p.getCoordinateSequence(), target, 0, 1); if (p.isEmpty()) {
target.addCoordinate(Double.NaN, Double.NaN, Double.NaN, Double.NaN, 0, 1);
} else {
addCoordinate(p.getCoordinateSequence(), target, 0, 1);
}
} else if (geometry instanceof LineString) { } else if (geometry instanceof LineString) {
if (parentType != 0 && parentType != MULTI_LINE_STRING && parentType != GEOMETRY_COLLECTION) { if (parentType != 0 && parentType != MULTI_LINE_STRING && parentType != GEOMETRY_COLLECTION) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
......
...@@ -155,9 +155,9 @@ public class ValueGeometry extends Value { ...@@ -155,9 +155,9 @@ public class ValueGeometry extends Value {
* @param envelope envelope. May not be modified. * @param envelope envelope. May not be modified.
* @return the value * @return the value
*/ */
public static ValueGeometry fromEnvelope(double[] envelope) { public static Value fromEnvelope(double[] envelope) {
return (ValueGeometry) Value.cache(new ValueGeometry(GeometryUtils.envelope2wkb(envelope), return envelope != null ? Value.cache(new ValueGeometry(GeometryUtils.envelope2wkb(envelope),
GeometryUtils.DIMENSION_SYSTEM_XY, envelope)); GeometryUtils.DIMENSION_SYSTEM_XY, envelope)): ValueNull.INSTANCE;
} }
/** /**
......
...@@ -13,12 +13,10 @@ import java.sql.Statement; ...@@ -13,12 +13,10 @@ import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import java.util.Random; import java.util.Random;
import org.h2.api.Aggregate; import org.h2.api.Aggregate;
import org.h2.message.DbException;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.TestDb; import org.h2.test.TestDb;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
import org.h2.tools.SimpleRowSource; import org.h2.tools.SimpleRowSource;
import org.h2.util.StringUtils;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueGeometry; import org.h2.value.ValueGeometry;
...@@ -700,13 +698,6 @@ public class TestSpatial extends TestDb { ...@@ -700,13 +698,6 @@ public class TestSpatial extends TestDb {
ValueGeometry valueGeometry3 = ValueGeometry.getFromGeometry(geometry); ValueGeometry valueGeometry3 = ValueGeometry.getFromGeometry(geometry);
assertEquals(valueGeometry, valueGeometry3); assertEquals(valueGeometry, valueGeometry3);
assertEquals(geometry.getSRID(), ((Geometry) valueGeometry3.getGeometry()).getSRID()); assertEquals(geometry.getSRID(), ((Geometry) valueGeometry3.getGeometry()).getSRID());
// Check illegal geometry (no WKB representation)
try {
ValueGeometry.get("POINT EMPTY");
fail("expected exception");
} catch (IllegalArgumentException | DbException ex) {
// expected
}
} }
/** /**
......
...@@ -58,6 +58,7 @@ public class TestGeometryUtils extends TestBase { ...@@ -58,6 +58,7 @@ public class TestGeometryUtils extends TestBase {
testMultiLineString(); testMultiLineString();
testMultiPolygon(); testMultiPolygon();
testGeometryCollection(); testGeometryCollection();
testEmptyPoint();
testDimensionM(); testDimensionM();
testDimensionZM(); testDimensionZM();
testSRID(); testSRID();
...@@ -144,7 +145,8 @@ public class TestGeometryUtils extends TestBase { ...@@ -144,7 +145,8 @@ public class TestGeometryUtils extends TestBase {
// Test WKB->Geometry conversion // Test WKB->Geometry conversion
Geometry geometryFromH2 = JTSUtils.ewkb2geometry(wkbFromJTS); Geometry geometryFromH2 = JTSUtils.ewkb2geometry(wkbFromJTS);
// JTS has locale-specific bugs with NaNs, also such geometries are not fully valid // JTS has locale-specific bugs with NaNs, also such geometries are not
// fully valid
if (!wkt.contains("NaN")) { if (!wkt.contains("NaN")) {
assertEquals(jtsWkt.replaceAll(" Z", ""), new WKTWriter(numOfDimensions).write(geometryFromH2)); assertEquals(jtsWkt.replaceAll(" Z", ""), new WKTWriter(numOfDimensions).write(geometryFromH2));
} }
...@@ -171,18 +173,32 @@ public class TestGeometryUtils extends TestBase { ...@@ -171,18 +173,32 @@ public class TestGeometryUtils extends TestBase {
} else { } else {
double minX = envelopeFromJTS.getMinX(), maxX = envelopeFromJTS.getMaxX(); double minX = envelopeFromJTS.getMinX(), maxX = envelopeFromJTS.getMaxX();
double minY = envelopeFromJTS.getMinY(), maxY = envelopeFromJTS.getMaxY(); double minY = envelopeFromJTS.getMinY(), maxY = envelopeFromJTS.getMaxY();
assertEquals(minX, envelopeFromH2[0]); if (Double.isNaN(minX) || Double.isNaN(maxX) || Double.isNaN(minY) || Double.isNaN(maxY)) {
assertEquals(maxX, envelopeFromH2[1]); assertNull(envelopeFromH2);
assertEquals(minY, envelopeFromH2[2]); assertNull(GeometryUtils.envelope2wkb(envelopeFromH2));
assertEquals(maxY, envelopeFromH2[3]); } else {
// TODO determine what to do with NaNs in dimensions X and Y assertEquals(minX, envelopeFromH2[0]);
if (!Double.isNaN(minX) && !Double.isNaN(maxX) && !Double.isNaN(minY) && !Double.isNaN(maxY)) { assertEquals(maxX, envelopeFromH2[1]);
assertEquals(minY, envelopeFromH2[2]);
assertEquals(maxY, envelopeFromH2[3]);
assertEquals(new WKBWriter(2).write(new GeometryFactory().toGeometry(envelopeFromJTS)), assertEquals(new WKBWriter(2).write(new GeometryFactory().toGeometry(envelopeFromJTS)),
GeometryUtils.envelope2wkb(envelopeFromH2)); GeometryUtils.envelope2wkb(envelopeFromH2));
} }
} }
} }
private void testEmptyPoint() {
String ewkt = "POINT EMPTY";
byte[] ewkb = EWKTUtils.ewkt2ewkb(ewkt);
assertEquals(StringUtils.convertHexToBytes("00000000017ff80000000000007ff8000000000000"), ewkb);
assertEquals(ewkt, EWKTUtils.ewkb2ewkt(ewkb));
assertNull(GeometryUtils.getEnvelope(ewkb));
Point p = (Point) JTSUtils.ewkb2geometry(ewkb);
assertTrue(p.isEmpty());
assertEquals(ewkt, new WKTWriter().write(p));
assertEquals(ewkb, JTSUtils.geometry2ewkb(p));
}
private void testDimensionM() { private void testDimensionM() {
byte[] ewkb = EWKTUtils.ewkt2ewkb("POINT M (1 2 3)"); byte[] ewkb = EWKTUtils.ewkt2ewkb("POINT M (1 2 3)");
assertEquals("POINT M (1 2 3)", EWKTUtils.ewkb2ewkt(ewkb)); assertEquals("POINT M (1 2 3)", EWKTUtils.ewkb2ewkt(ewkb));
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论