提交 3f58272d authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Use own utilities instead of JTS

上级 9ca18311
......@@ -14,18 +14,17 @@ import org.h2.index.Index;
import org.h2.mvstore.db.MVSpatialIndex;
import org.h2.table.Column;
import org.h2.table.TableFilter;
import org.h2.util.geometry.GeometryUtils;
import org.h2.value.Value;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueNull;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.GeometryFactory;
/**
* Data stored while calculating an aggregate.
*/
class AggregateDataEnvelope extends AggregateData {
private Envelope envelope;
private double[] envelope;
/**
* Get the index (if any) for the column specified in the geometry
......@@ -62,18 +61,15 @@ class AggregateDataEnvelope extends AggregateData {
if (v == ValueNull.INSTANCE) {
return;
}
if (envelope == null) {
envelope = new Envelope();
}
envelope.expandToInclude(((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy());
envelope = GeometryUtils.union(envelope, ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy());
}
@Override
Value getValue(Database database, int dataType, boolean distinct) {
if (envelope == null || envelope.isNull()) {
if (envelope == null) {
return ValueNull.INSTANCE;
}
return ValueGeometry.getFromGeometry(new GeometryFactory().toGeometry(envelope));
return ValueGeometry.get(GeometryUtils.envelope2wkb(envelope));
}
}
......@@ -5,6 +5,11 @@
*/
package org.h2.index;
import static org.h2.util.geometry.GeometryUtils.MAX_X;
import static org.h2.util.geometry.GeometryUtils.MAX_Y;
import static org.h2.util.geometry.GeometryUtils.MIN_X;
import static org.h2.util.geometry.GeometryUtils.MIN_Y;
import java.util.Iterator;
import org.h2.command.dml.AllColumnsForPlan;
import org.h2.engine.Session;
......@@ -23,7 +28,6 @@ import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueNull;
import org.locationtech.jts.geom.Envelope;
/**
* This is an index based on a MVR-TreeMap.
......@@ -133,10 +137,9 @@ public class SpatialTreeIndex extends BaseIndex implements SpatialIndex {
if (v == ValueNull.INSTANCE) {
return new SpatialKey(row.getKey());
}
Envelope env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy();
double[] env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy();
return new SpatialKey(row.getKey(),
(float) env.getMinX(), (float) env.getMaxX(),
(float) env.getMinY(), (float) env.getMaxY());
(float) env[MIN_X], (float) env[MAX_X], (float) env[MIN_Y], (float) env[MAX_Y]);
}
@Override
......
......@@ -5,6 +5,11 @@
*/
package org.h2.mvstore.db;
import static org.h2.util.geometry.GeometryUtils.MAX_X;
import static org.h2.util.geometry.GeometryUtils.MAX_Y;
import static org.h2.util.geometry.GeometryUtils.MIN_X;
import static org.h2.util.geometry.GeometryUtils.MIN_Y;
import java.util.Iterator;
import java.util.List;
import org.h2.api.ErrorCode;
......@@ -29,12 +34,11 @@ import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.IndexColumn;
import org.h2.table.TableFilter;
import org.h2.util.geometry.GeometryUtils;
import org.h2.value.Value;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.GeometryFactory;
/**
* This is an index based on a MVRTreeMap.
......@@ -264,8 +268,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
bmaxyf = maxyf;
}
}
return ValueGeometry.getFromGeometry(new GeometryFactory().toGeometry(
new Envelope(bminxf, bmaxxf, bminyf, bmaxyf)));
return ValueGeometry.get(GeometryUtils.envelope2wkb(new double[] {bminxf, bmaxxf, bminyf, bmaxyf}));
}
return ValueNull.INSTANCE;
}
......@@ -275,10 +278,10 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
if (v == ValueNull.INSTANCE) {
return new SpatialKey(row.getKey());
}
Envelope env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy();
double[] env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy();
return new SpatialKey(row.getKey(),
(float) env.getMinX(), (float) env.getMaxX(),
(float) env.getMinY(), (float) env.getMaxY());
(float) env[MIN_X], (float) env[MAX_X],
(float) env[MIN_Y], (float) env[MAX_Y]);
}
@Override
......@@ -467,10 +470,9 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
if (hasBounds) {
if ((minxf <= bminxf || maxxf >= bmaxxf || minyf <= bminyf || maxyf >= bmaxyf)
&& map.containsKey(key)) {
Envelope env = ((ValueGeometry) mvTable.getRow(session, key.getId()).getValue(columnId))
double[] env = ((ValueGeometry) mvTable.getRow(session, key.getId()).getValue(columnId))
.getEnvelopeNoCopy();
double minxd = env.getMinX(), maxxd = env.getMaxX(), minyd = env.getMinY(),
maxyd = env.getMaxY();
double minxd = env[MIN_X], maxxd = env[MAX_X], minyd = env[MIN_Y], maxyd = env[MAX_Y];
if (minxd < bminxd) {
bminxf = minxf;
bminxd = minxd;
......@@ -490,16 +492,16 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
}
} else if (map.containsKey(key)) {
hasBounds = true;
Envelope env = ((ValueGeometry) mvTable.getRow(session, key.getId()).getValue(columnId))
double[] env = ((ValueGeometry) mvTable.getRow(session, key.getId()).getValue(columnId))
.getEnvelopeNoCopy();
bminxf = minxf;
bminxd = env.getMinX();
bminxd = env[MIN_X];
bmaxxf = maxxf;
bmaxxd = env.getMaxX();
bmaxxd = env[MAX_X];
bminyf = minyf;
bminyd = env.getMinY();
bminyd = env[MIN_Y];
bmaxyf = maxyf;
bmaxyd = env.getMaxY();
bmaxyd = env[MAX_Y];
}
} else if (hasBounds) {
if (minxf <= bminxf || maxxf >= bmaxxf || minyf <= bminyf || maxyf >= bmaxyf) {
......@@ -512,8 +514,9 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
}
Value getBounds() {
return hasBounds ? ValueGeometry.getFromGeometry(new GeometryFactory().toGeometry(
new Envelope(bminxd, bmaxxd, bminyd, bmaxyd))) : ValueNull.INSTANCE;
return hasBounds ? ValueGeometry.get(
GeometryUtils.envelope2wkb(new double[] {bminxd, bmaxxd, bminyd, bmaxyd}))
: ValueNull.INSTANCE;
}
}
......
......@@ -550,22 +550,26 @@ public final class GeometryUtils {
} else if (envelope2 == null) {
return envelope1;
}
double minX1 = envelope1[MIN_X], maxX1 = envelope1[MAX_X], minY1 = envelope1[MIN_Y], maxY1 = envelope1[MAX_Y];
double minX2 = envelope2[MIN_X], maxX2 = envelope2[MAX_X], minY2 = envelope2[MIN_Y], maxY2 = envelope2[MAX_Y];
boolean modified = false;
if (minX1 > minX2) {
minX1 = minX2;
modified = true;
}
if (maxX1 < maxX2) {
maxX1 = maxX2;
modified = true;
}
if (minY1 > minY2) {
minY1 = minY2;
modified = true;
}
if (maxY1 < maxY2) {
maxY1 = maxY2;
modified = true;
}
return new double[] { minX1, maxX1, minY1, maxY1 };
return modified ? new double[] { minX1, maxX1, minY1, maxY1 } : envelope1;
}
/**
......
......@@ -898,7 +898,7 @@ public class DataType {
case Value.RESULT_SET:
return ResultSet.class.getName();
case Value.GEOMETRY:
return GEOMETRY_CLASS_NAME;
return GEOMETRY_CLASS != null ? GEOMETRY_CLASS_NAME : String.class.getName();
case Value.INTERVAL_YEAR:
case Value.INTERVAL_MONTH:
case Value.INTERVAL_DAY:
......
......@@ -13,17 +13,10 @@ import org.h2.message.DbException;
import org.h2.util.Bits;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.CoordinateSequenceFilter;
import org.locationtech.jts.geom.Envelope;
import org.h2.util.geometry.EWKTUtils;
import org.h2.util.geometry.GeometryUtils;
import org.h2.util.geometry.JTSUtils;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKBReader;
import org.locationtech.jts.io.WKBWriter;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
/**
* Implementation of the GEOMETRY data type.
......@@ -50,22 +43,20 @@ public class ValueGeometry extends Value {
* The value. Converted from WKB only on request as conversion from/to WKB
* cost a significant amount of CPU cycles.
*/
private Geometry geometry;
private Object geometry;
/**
* The envelope of the value. Calculated only on request.
*/
private Envelope envelope;
private double[] envelope;
/**
* Create a new geometry objects.
*
* @param bytes the bytes (always known)
* @param geometry the geometry object (may be null)
* @param bytes the EWKB bytes
*/
private ValueGeometry(byte[] bytes, Geometry geometry) {
private ValueGeometry(byte[] bytes) {
this.bytes = bytes;
this.geometry = geometry;
this.hashCode = Arrays.hashCode(bytes);
}
......@@ -77,29 +68,7 @@ public class ValueGeometry extends Value {
* @return the value
*/
public static ValueGeometry getFromGeometry(Object o) {
/*
* Do not pass untrusted source geometry object to a cache, use only its WKB
* representation. Geometries are not fully immutable.
*/
return get(convertToWKB((Geometry) o));
}
private static ValueGeometry get(Geometry g) {
byte[] bytes = convertToWKB(g);
return (ValueGeometry) Value.cache(new ValueGeometry(bytes, g));
}
private static byte[] convertToWKB(Geometry g) {
boolean includeSRID = g.getSRID() != 0;
int dimensionCount = getDimensionCount(g);
WKBWriter writer = new WKBWriter(dimensionCount, includeSRID);
return writer.write(g);
}
private static int getDimensionCount(Geometry geometry) {
ZVisitor finder = new ZVisitor();
geometry.apply(finder);
return finder.isFoundZ() ? 3 : 2;
return get(JTSUtils.geometry2ewkb((Geometry) o));
}
/**
......@@ -110,20 +79,8 @@ public class ValueGeometry extends Value {
*/
public static ValueGeometry get(String s) {
try {
int srid;
if (s.startsWith("SRID=")) {
int idx = s.indexOf(';', 5);
srid = Integer.parseInt(s.substring(5, idx));
s = s.substring(idx + 1);
} else {
srid = 0;
}
/*
* No-arg WKTReader() constructor instantiates a new GeometryFactory and a new
* PrecisionModel anyway, so special case for srid == 0 is not needed.
*/
return get(new WKTReader(new GeometryFactory(new PrecisionModel(), srid)).read(s));
} catch (ParseException | StringIndexOutOfBoundsException | NumberFormatException ex) {
return get(EWKTUtils.ewkt2ewkb(s));
} catch (RuntimeException ex) {
throw DbException.convert(ex);
}
}
......@@ -137,11 +94,7 @@ public class ValueGeometry extends Value {
*/
public static ValueGeometry get(String s, int srid) {
// This method is not used in H2, but preserved for H2GIS
try {
return get(new WKTReader(new GeometryFactory(new PrecisionModel(), srid)).read(s));
} catch (ParseException ex) {
throw DbException.convert(ex);
}
return get(srid == 0 ? s : "SRID=" + srid + ';' + s);
}
/**
......@@ -151,7 +104,7 @@ public class ValueGeometry extends Value {
* @return the value
*/
public static ValueGeometry get(byte[] bytes) {
return (ValueGeometry) Value.cache(new ValueGeometry(bytes, null));
return (ValueGeometry) Value.cache(new ValueGeometry(bytes));
}
/**
......@@ -160,25 +113,15 @@ public class ValueGeometry extends Value {
*
* @return a copy of the geometry object
*/
public Geometry getGeometry() {
Geometry geometry = getGeometryNoCopy();
Geometry copy = geometry.copy();
return copy;
}
public Geometry getGeometryNoCopy() {
public Object getGeometry() {
if (geometry == null) {
try {
/*
* No-arg WKBReader() constructor instantiates a new GeometryFactory and a new
* PrecisionModel anyway, so special case for srid == 0 is not needed.
*/
geometry = new WKBReader(new GeometryFactory(new PrecisionModel(), getSRID())).read(bytes);
} catch (ParseException ex) {
geometry = JTSUtils.ewkb2geometry(bytes);
} catch (RuntimeException ex) {
throw DbException.convert(ex);
}
}
return geometry;
return ((Geometry) geometry).copy();
}
/**
......@@ -215,9 +158,9 @@ public class ValueGeometry extends Value {
*
* @return envelope of this geometry
*/
public Envelope getEnvelopeNoCopy() {
public double[] getEnvelopeNoCopy() {
if (envelope == null) {
envelope = getGeometryNoCopy().getEnvelopeInternal();
envelope = GeometryUtils.getEnvelope(bytes);
}
return envelope;
}
......@@ -230,7 +173,7 @@ public class ValueGeometry extends Value {
* @return true if the two overlap
*/
public boolean intersectsBoundingBox(ValueGeometry r) {
return getEnvelopeNoCopy().intersects(r.getEnvelopeNoCopy());
return GeometryUtils.intersects(getEnvelopeNoCopy(), r.getEnvelopeNoCopy());
}
/**
......@@ -240,10 +183,7 @@ public class ValueGeometry extends Value {
* @return the union of this geometry envelope and another geometry envelope
*/
public Value getEnvelopeUnion(ValueGeometry r) {
GeometryFactory gf = new GeometryFactory();
Envelope mergedEnvelope = new Envelope(getEnvelopeNoCopy());
mergedEnvelope.expandToInclude(r.getEnvelopeNoCopy());
return get(gf.toGeometry(mergedEnvelope));
return get(GeometryUtils.envelope2wkb(GeometryUtils.union(getEnvelopeNoCopy(), r.getEnvelopeNoCopy())));
}
@Override
......@@ -253,13 +193,13 @@ public class ValueGeometry extends Value {
@Override
public String getSQL() {
// Using bytes is faster than converting EWKB to Geometry then EWKT.
// Using bytes is faster than converting to EWKT.
return "X'" + StringUtils.convertBytesToHex(getBytesNoCopy()) + "'::Geometry";
}
@Override
public int compareTypeSafe(Value v, CompareMode mode) {
return getGeometryNoCopy().compareTo(((ValueGeometry) v).getGeometryNoCopy());
return Bits.compareNotNullUnsigned(bytes, ((ValueGeometry) v).bytes);
}
@Override
......@@ -279,23 +219,25 @@ public class ValueGeometry extends Value {
@Override
public Object getObject() {
if (DataType.GEOMETRY_CLASS != null) {
return getGeometry();
}
return getEWKT();
}
@Override
public byte[] getBytes() {
return Utils.cloneByteArray(getEWKB());
return Utils.cloneByteArray(bytes);
}
@Override
public byte[] getBytesNoCopy() {
return getEWKB();
return bytes;
}
@Override
public void set(PreparedStatement prep, int parameterIndex)
throws SQLException {
prep.setObject(parameterIndex, getGeometryNoCopy());
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setBytes(parameterIndex, bytes);
}
@Override
......@@ -310,10 +252,7 @@ public class ValueGeometry extends Value {
@Override
public boolean equals(Object other) {
// The JTS library only does half-way support for 3D coordinates, so
// their equals method only checks the first two coordinates.
return other instanceof ValueGeometry &&
Arrays.equals(getEWKB(), ((ValueGeometry) other).getEWKB());
return other instanceof ValueGeometry && Arrays.equals(getEWKB(), ((ValueGeometry) other).getEWKB());
}
/**
......@@ -322,12 +261,7 @@ public class ValueGeometry extends Value {
* @return the extended well-known text
*/
public String getEWKT() {
String wkt = new WKTWriter(3).write(getGeometryNoCopy());
int srid = getSRID();
return srid == 0
? wkt
// "SRID=-2147483648;".length() == 17
: new StringBuilder(wkt.length() + 17).append("SRID=").append(srid).append(';').append(wkt).toString();
return EWKTUtils.ewkb2ewkt(bytes);
}
/**
......@@ -347,40 +281,4 @@ public class ValueGeometry extends Value {
return super.convertTo(targetType, precision, mode, column, null);
}
/**
* A visitor that checks if there is a Z coordinate.
*/
static class ZVisitor implements CoordinateSequenceFilter {
private boolean foundZ;
public boolean isFoundZ() {
return foundZ;
}
/**
* Performs an operation on a coordinate in a CoordinateSequence.
*
* @param coordinateSequence the object to which the filter is applied
* @param i the index of the coordinate to apply the filter to
*/
@Override
public void filter(CoordinateSequence coordinateSequence, int i) {
if (!Double.isNaN(coordinateSequence.getOrdinate(i, 2))) {
foundZ = true;
}
}
@Override
public boolean isDone() {
return foundZ;
}
@Override
public boolean isGeometryChanged() {
return false;
}
}
}
......@@ -612,12 +612,13 @@ public class TestSpatial extends TestDb {
ValueGeometry geom3d = ValueGeometry.get(ewkt);
assertEquals(ewkt, geom3d.getString());
ValueGeometry copy = ValueGeometry.get(geom3d.getBytes());
assertEquals(6, copy.getGeometry().getCoordinates()[0].z);
assertEquals(5, copy.getGeometry().getCoordinates()[1].z);
assertEquals(4, copy.getGeometry().getCoordinates()[2].z);
Geometry g = (Geometry) copy.getGeometry();
assertEquals(6, g.getCoordinates()[0].z);
assertEquals(5, g.getCoordinates()[1].z);
assertEquals(4, g.getCoordinates()[2].z);
// Test SRID
copy = ValueGeometry.get(geom3d.getBytes());
assertEquals(27572, copy.getGeometry().getSRID());
assertEquals(27572, g.getSRID());
Point point = new GeometryFactory().createPoint((new Coordinate(1.1d, 1.2d)));
// SRID 0
......@@ -696,7 +697,7 @@ public class TestSpatial extends TestDb {
assertFalse(valueGeometry.equals(valueGeometry2));
ValueGeometry valueGeometry3 = ValueGeometry.getFromGeometry(geometry);
assertEquals(valueGeometry, valueGeometry3);
assertEquals(geometry.getSRID(), valueGeometry3.getGeometry().getSRID());
assertEquals(geometry.getSRID(), ((Geometry) valueGeometry3.getGeometry()).getSRID());
// Check illegal geometry (no WKB representation)
try {
ValueGeometry.get("POINT EMPTY");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论