提交 df4cc289 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add extended parameters for GEOMETRY data type

上级 e5098b56
...@@ -194,10 +194,12 @@ import org.h2.util.ParserUtil; ...@@ -194,10 +194,12 @@ import org.h2.util.ParserUtil;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.Utils; import org.h2.util.Utils;
import org.h2.util.geometry.EWKTUtils;
import org.h2.value.CompareMode; import org.h2.value.CompareMode;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo; import org.h2.value.ExtTypeInfo;
import org.h2.value.ExtTypeInfoEnum; import org.h2.value.ExtTypeInfoEnum;
import org.h2.value.ExtTypeInfoGeometry;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueBoolean; import org.h2.value.ValueBoolean;
import org.h2.value.ValueBytes; import org.h2.value.ValueBytes;
...@@ -5027,6 +5029,31 @@ public class Parser { ...@@ -5027,6 +5029,31 @@ public class Parser {
} }
original += extTypeInfo.toString(); original += extTypeInfo.toString();
} }
} else if (dataType.type == Value.GEOMETRY) {
if (extTypeInfo == null) {
if (readIf(OPEN_PAREN)) {
int type = 0;
if (currentTokenType == IDENTIFIER && !currentTokenQuoted) {
try {
type = EWKTUtils.parseGeometryType(currentToken);
read();
if (type / 1_000 == 0 && currentTokenType == IDENTIFIER && !currentTokenQuoted) {
type += EWKTUtils.parseDimensionSystem(currentToken) * 1_000;
read();
}
} catch (IllegalArgumentException ex) {
throw getSyntaxError();
}
}
Integer srid = null;
if (type == 0 || readIf(COMMA)) {
srid = readInt();
}
read(CLOSE_PAREN);
extTypeInfo = new ExtTypeInfoGeometry(type, srid);
original += extTypeInfo.toString();
}
}
} else if (readIf(OPEN_PAREN)) { } else if (readIf(OPEN_PAREN)) {
// Support for MySQL: INT(11), MEDIUMINT(8) and so on. // Support for MySQL: INT(11), MEDIUMINT(8) and so on.
// Just ignore the precision. // Just ignore the precision.
......
...@@ -199,15 +199,17 @@ public class CreateTable extends CommandWithColumns { ...@@ -199,15 +199,17 @@ public class CreateTable extends CommandWithColumns {
precision = scale; precision = scale;
} }
ExtTypeInfo extTypeInfo = null; ExtTypeInfo extTypeInfo = null;
if (dt.type == Value.ENUM) { int t = dt.type;
/** if (DataType.isExtInfoType(t)) {
if (expr instanceof ExpressionColumn) {
extTypeInfo = ((ExpressionColumn) expr).getColumn().getExtTypeInfo();
} else if (t == Value.ENUM) {
/*
* Only columns of tables may be enumerated. * Only columns of tables may be enumerated.
*/ */
if (!(expr instanceof ExpressionColumn)) {
throw DbException.get(ErrorCode.GENERAL_ERROR_1, throw DbException.get(ErrorCode.GENERAL_ERROR_1,
"Unable to resolve enumerators of expression"); "Unable to resolve enumerators of expression");
} }
extTypeInfo = ((ExpressionColumn) expr).getColumn().getExtTypeInfo();
} }
Column col = new Column(name, type, precision, scale, displaySize, extTypeInfo); Column col = new Column(name, type, precision, scale, displaySize, extTypeInfo);
addColumn(col); addColumn(col);
......
...@@ -402,7 +402,7 @@ public class Column { ...@@ -402,7 +402,7 @@ public class Column {
getCreateSQL(), s + " (" + value.getPrecision() + ")"); getCreateSQL(), s + " (" + value.getPrecision() + ")");
} }
} }
if (type == Value.ENUM && value != ValueNull.INSTANCE) { if (value != ValueNull.INSTANCE && DataType.isExtInfoType(type) && extTypeInfo != null) {
value = extTypeInfo.cast(value); value = extTypeInfo.cast(value);
} }
updateSequenceIfRequired(session, value); updateSequenceIfRequired(session, value);
...@@ -509,6 +509,11 @@ public class Column { ...@@ -509,6 +509,11 @@ public class Column {
case Value.DECIMAL: case Value.DECIMAL:
buff.append('(').append(precision).append(", ").append(scale).append(')'); buff.append('(').append(precision).append(", ").append(scale).append(')');
break; break;
case Value.GEOMETRY:
if (extTypeInfo == null) {
break;
}
//$FALL-THROUGH$
case Value.ENUM: case Value.ENUM:
buff.append(extTypeInfo.toString()); buff.append(extTypeInfo.toString());
break; break;
......
...@@ -38,6 +38,7 @@ import org.h2.util.ColumnNamer; ...@@ -38,6 +38,7 @@ import org.h2.util.ColumnNamer;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.Utils; import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo; import org.h2.value.ExtTypeInfo;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -196,7 +197,7 @@ public class TableView extends Table { ...@@ -196,7 +197,7 @@ public class TableView extends Table {
int scale = expr.getScale(); int scale = expr.getScale();
int displaySize = expr.getDisplaySize(); int displaySize = expr.getDisplaySize();
ExtTypeInfo extTypeInfo = null; ExtTypeInfo extTypeInfo = null;
if (type == Value.ENUM) { if (DataType.isExtInfoType(type)) {
if (expr instanceof ExpressionColumn) { if (expr instanceof ExpressionColumn) {
extTypeInfo = ((ExpressionColumn) expr).getColumn().getExtTypeInfo(); extTypeInfo = ((ExpressionColumn) expr).getColumn().getExtTypeInfo();
} }
......
...@@ -233,17 +233,17 @@ public final class EWKBUtils { ...@@ -233,17 +233,17 @@ public final class EWKBUtils {
/** /**
* Geometry type mask that indicates presence of dimension Z. * Geometry type mask that indicates presence of dimension Z.
*/ */
private static final int EWKB_Z = 0x8000_0000; public static final int EWKB_Z = 0x8000_0000;
/** /**
* Geometry type mask that indicates presence of dimension M. * Geometry type mask that indicates presence of dimension M.
*/ */
private static final int EWKB_M = 0x4000_0000; public static final int EWKB_M = 0x4000_0000;
/** /**
* Geometry type mask that indicates presence of SRID. * Geometry type mask that indicates presence of SRID.
*/ */
private static final int EWKB_SRID = 0x2000_0000; public static final int EWKB_SRID = 0x2000_0000;
/** /**
* Converts any supported EWKB to EWKB representation that is used by this * Converts any supported EWKB to EWKB representation that is used by this
...@@ -296,6 +296,32 @@ public final class EWKBUtils { ...@@ -296,6 +296,32 @@ public final class EWKBUtils {
} }
} }
/**
* Converts geometry type with flags to a dimension system.
*
* @param type
* geometry type with flags
* @return dimension system
*/
public static int type2dimensionSystem(int type) {
// PostGIS extensions
boolean useZ = (type & EWKB_Z) != 0;
boolean useM = (type & EWKB_M) != 0;
// OGC 06-103r4
type &= 0xffff;
switch (type / 1_000) {
case DIMENSION_SYSTEM_XYZ:
useZ = true;
break;
case DIMENSION_SYSTEM_XYZM:
useZ = true;
//$FALL-THROUGH$
case DIMENSION_SYSTEM_XYM:
useM = true;
}
return (useZ ? DIMENSION_SYSTEM_XYZ : 0) | (useM ? DIMENSION_SYSTEM_XYM : 0);
}
/** /**
* Parses a EWKB. * Parses a EWKB.
* *
......
...@@ -41,6 +41,30 @@ import org.h2.util.geometry.GeometryUtils.Target; ...@@ -41,6 +41,30 @@ import org.h2.util.geometry.GeometryUtils.Target;
*/ */
public final class EWKTUtils { public final class EWKTUtils {
/**
* 0-based type names of geometries, subtract 1 from type code to get index
* in this array.
*/
private static final String[] TYPES = { //
"POINT", //
"LINESTRING", //
"POLYGON", //
"MULTIPOINT", //
"MULTILINESTRING", //
"MULTIPOLYGON", //
"GEOMETRYCOLLECTION", //
};
/**
* Names of dimension systems.
*/
private static final String[] DIMENSION_SYTEMS = { //
"XY", //
"Z", //
"M", //
"ZM", //
};
/** /**
* Converter output target that writes a EWKT. * Converter output target that writes a EWKT.
*/ */
...@@ -328,7 +352,7 @@ public final class EWKTUtils { ...@@ -328,7 +352,7 @@ public final class EWKTUtils {
int o = offset; int o = offset;
skipWS(); skipWS();
int len = ewkt.length(); int len = ewkt.length();
if (offset > len - 2) { if (offset >= len) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
int result; int result;
...@@ -342,6 +366,9 @@ public final class EWKTUtils { ...@@ -342,6 +366,9 @@ public final class EWKTUtils {
case 'Z': case 'Z':
case 'z': case 'z':
offset++; offset++;
if (offset >= len) {
result = DIMENSION_SYSTEM_XYZ;
} else {
ch = ewkt.charAt(offset); ch = ewkt.charAt(offset);
if (ch == 'M' || ch == 'm') { if (ch == 'M' || ch == 'm') {
offset++; offset++;
...@@ -349,6 +376,7 @@ public final class EWKTUtils { ...@@ -349,6 +376,7 @@ public final class EWKTUtils {
} else { } else {
result = DIMENSION_SYSTEM_XYZ; result = DIMENSION_SYSTEM_XYZ;
} }
}
break; break;
default: default:
result = DIMENSION_SYSTEM_XY; result = DIMENSION_SYSTEM_XY;
...@@ -589,6 +617,71 @@ public final class EWKTUtils { ...@@ -589,6 +617,71 @@ public final class EWKTUtils {
parseEWKT(new EWKTSource(ewkt), target, 0, 0); parseEWKT(new EWKTSource(ewkt), target, 0, 0);
} }
/**
* Parses geometry type and dimension system from the given string.
*
* @param s
* string to parse
* @return geometry type and dimension system in OGC geometry code format
* (type + dimensionSystem * 1000)
* @throws IllegalArgumentException
* if input is not valid
*/
public static int parseGeometryType(String s) {
EWKTSource source = new EWKTSource(s);
int type = source.readType();
int dimensionSystem = 0;
if (source.hasData()) {
dimensionSystem = source.readDimensionSystem();
if (source.hasData()) {
throw new IllegalArgumentException();
}
}
return dimensionSystem * 1_000 + type;
}
/**
* Parses a dimension system from the given string.
*
* @param s
* string to parse
* @return dimension system, one of XYZ, XYM, or XYZM
* @see GeometryUtils#DIMENSION_SYSTEM_XYZ
* @see GeometryUtils#DIMENSION_SYSTEM_XYM
* @see GeometryUtils#DIMENSION_SYSTEM_XYZM
* @throws IllegalArgumentException
* if input is not valid
*/
public static int parseDimensionSystem(String s) {
EWKTSource source = new EWKTSource(s);
int dimensionSystem = source.readDimensionSystem();
if (source.hasData() || dimensionSystem == DIMENSION_SYSTEM_XY) {
throw new IllegalArgumentException();
}
return dimensionSystem;
}
/**
* Formats type and dimension system as a string.
*
* @param type
* OGC geometry code format (type + dimensionSystem * 1000)
* @return formatted string
* @throws IllegalArgumentException
* if type is not valid
*/
public static String formatGeometryTypeAndDimensionSystem(int type) {
int t = type % 1_000, d = type / 1_000;
if (t < POINT || t > GEOMETRY_COLLECTION || d < DIMENSION_SYSTEM_XY || d > DIMENSION_SYSTEM_XYZM) {
throw new IllegalArgumentException();
}
String result = TYPES[t - 1];
if (d != DIMENSION_SYSTEM_XY) {
result = result + ' ' + DIMENSION_SYTEMS[d];
}
return result;
}
/** /**
* Parses a EWKB. * Parses a EWKB.
* *
......
...@@ -362,37 +362,37 @@ public final class GeometryUtils { ...@@ -362,37 +362,37 @@ public final class GeometryUtils {
/** /**
* POINT geometry type. * POINT geometry type.
*/ */
static final int POINT = 1; public static final int POINT = 1;
/** /**
* LINESTRING geometry type. * LINESTRING geometry type.
*/ */
static final int LINE_STRING = 2; public static final int LINE_STRING = 2;
/** /**
* POLYGON geometry type. * POLYGON geometry type.
*/ */
static final int POLYGON = 3; public static final int POLYGON = 3;
/** /**
* MULTIPOINT geometry type. * MULTIPOINT geometry type.
*/ */
static final int MULTI_POINT = 4; public static final int MULTI_POINT = 4;
/** /**
* MULTILINESTRING geometry type. * MULTILINESTRING geometry type.
*/ */
static final int MULTI_LINE_STRING = 5; public static final int MULTI_LINE_STRING = 5;
/** /**
* MULTIPOLYGON geometry type. * MULTIPOLYGON geometry type.
*/ */
static final int MULTI_POLYGON = 6; public static final int MULTI_POLYGON = 6;
/** /**
* GEOMETRYCOLLECTION geometry type. * GEOMETRYCOLLECTION geometry type.
*/ */
static final int GEOMETRY_COLLECTION = 7; public static final int GEOMETRY_COLLECTION = 7;
/** /**
* Number of X coordinate. * Number of X coordinate.
......
...@@ -358,7 +358,7 @@ public class DataType { ...@@ -358,7 +358,7 @@ public class DataType {
104 104
); );
add(Value.GEOMETRY, Types.OTHER, add(Value.GEOMETRY, Types.OTHER,
createString(false), createGeometry(),
new String[]{"GEOMETRY"}, new String[]{"GEOMETRY"},
32 32
); );
...@@ -548,6 +548,17 @@ public class DataType { ...@@ -548,6 +548,17 @@ public class DataType {
return t; return t;
} }
private static DataType createGeometry() {
DataType dataType = new DataType();
dataType.prefix = "'";
dataType.suffix = "'";
dataType.params = "TYPE,SRID";
dataType.maxPrecision = Integer.MAX_VALUE;
dataType.defaultPrecision = Integer.MAX_VALUE;
dataType.defaultDisplaySize = Integer.MAX_VALUE;
return dataType;
}
/** /**
* Get the list of data types. * Get the list of data types.
* *
...@@ -1403,6 +1414,16 @@ public class DataType { ...@@ -1403,6 +1414,16 @@ public class DataType {
return type == Value.STRING || type == Value.STRING_FIXED || type == Value.STRING_IGNORECASE; return type == Value.STRING || type == Value.STRING_FIXED || type == Value.STRING_IGNORECASE;
} }
/**
* Check if the given type may have extended type information.
*
* @param type the value type
* @return true if the value type may have extended type information
*/
public static boolean isExtInfoType(int type) {
return type == Value.GEOMETRY || type == Value.ENUM;
}
/** /**
* Check if the given value type supports the add operation. * Check if the given value type supports the add operation.
* *
......
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.geometry.EWKTUtils;
/**
* Extended parameters of the GEOMETRY data type.
*/
public final class ExtTypeInfoGeometry extends ExtTypeInfo {
private final int type;
private final Integer srid;
private static String toString(int type, Integer srid) {
if (type == 0 && srid == null) {
return "";
}
StringBuilder builder = new StringBuilder();
builder.append('(');
if (type != 0) {
builder.append(EWKTUtils.formatGeometryTypeAndDimensionSystem(type));
}
if (srid != null) {
if (type != 0) {
builder.append(", ");
}
builder.append((int) srid);
}
return builder.append(')').toString();
}
/**
* Creates new instance of extended parameters of the GEOMETRY data type.
*
* @param type
* the type and dimension system of geometries, or 0 if not
* constrained
* @param srid
* the SRID of geometries, or {@code null} if not constrained
*/
public ExtTypeInfoGeometry(int type, Integer srid) {
this.type = type;
this.srid = srid;
}
@Override
public Value cast(Value value) {
ValueGeometry g = (ValueGeometry) value.convertTo(Value.GEOMETRY);
if (type != 0 && g.getTypeAndDimensionSystem() != type || srid != null && g.getSRID() != srid) {
throw DbException.get(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1,
toString(g.getTypeAndDimensionSystem(), g.getSRID()) + " <> " + toString());
}
return g;
}
@Override
public String toString() {
return toString(type, srid);
}
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
package org.h2.value; package org.h2.value;
import static org.h2.util.geometry.EWKBUtils.EWKB_SRID;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
...@@ -18,6 +19,7 @@ import org.h2.util.geometry.EWKBUtils; ...@@ -18,6 +19,7 @@ import org.h2.util.geometry.EWKBUtils;
import org.h2.util.geometry.EWKTUtils; import org.h2.util.geometry.EWKTUtils;
import org.h2.util.geometry.GeometryUtils; import org.h2.util.geometry.GeometryUtils;
import org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget; import org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget;
import org.h2.util.geometry.GeometryUtils.EnvelopeTarget;
import org.h2.util.geometry.JTSUtils; import org.h2.util.geometry.JTSUtils;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
...@@ -30,6 +32,8 @@ import org.locationtech.jts.geom.Geometry; ...@@ -30,6 +32,8 @@ import org.locationtech.jts.geom.Geometry;
*/ */
public class ValueGeometry extends Value { public class ValueGeometry extends Value {
private static final double[] UNKNOWN_ENVELOPE = new double[0];
/** /**
* As conversion from/to WKB cost a significant amount of CPU cycles, WKB * As conversion from/to WKB cost a significant amount of CPU cycles, WKB
* are kept in ValueGeometry instance. * are kept in ValueGeometry instance.
...@@ -43,9 +47,15 @@ public class ValueGeometry extends Value { ...@@ -43,9 +47,15 @@ public class ValueGeometry extends Value {
private final int hashCode; private final int hashCode;
/** /**
* Dimension system. -1 if not known yet. * Geometry type and dimension system in OGC geometry code format (type +
* dimensionSystem * 1000).
*/
private final int typeAndDimensionSystem;
/**
* Spatial reference system identifier.
*/ */
private int dimensionSystem; private final int srid;
/** /**
* The envelope of the value. Calculated only on request. * The envelope of the value. Calculated only on request.
...@@ -62,14 +72,18 @@ public class ValueGeometry extends Value { ...@@ -62,14 +72,18 @@ public class ValueGeometry extends Value {
* Create a new geometry object. * Create a new geometry object.
* *
* @param bytes the EWKB bytes * @param bytes the EWKB bytes
* @param dimensionSystem dimension system
* @param envelope the envelope * @param envelope the envelope
*/ */
private ValueGeometry(byte[] bytes, int dimensionSystem, double[] envelope) { private ValueGeometry(byte[] bytes, double[] envelope) {
if (bytes.length < 9 || bytes[0] != 0) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, StringUtils.convertBytesToHex(bytes));
}
this.bytes = bytes; this.bytes = bytes;
this.hashCode = Arrays.hashCode(bytes);
this.dimensionSystem = dimensionSystem;
this.envelope = envelope; this.envelope = envelope;
int t = Bits.readInt(bytes, 1);
srid = (t & EWKB_SRID) != 0 ? Bits.readInt(bytes, 5) : 0;
typeAndDimensionSystem = (t & 0xffff) % 1_000 + EWKBUtils.type2dimensionSystem(t) * 1_000;
hashCode = Arrays.hashCode(bytes);
} }
/** /**
...@@ -84,9 +98,8 @@ public class ValueGeometry extends Value { ...@@ -84,9 +98,8 @@ public class ValueGeometry extends Value {
EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget(); EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget();
Geometry g = (Geometry) o; Geometry g = (Geometry) o;
JTSUtils.parseGeometry(g, target); JTSUtils.parseGeometry(g, target);
int dimensionSystem = target.getDimensionSystem(); return (ValueGeometry) Value.cache(new ValueGeometry( //
return (ValueGeometry) Value.cache(new ValueGeometry(JTSUtils.geometry2ewkb(g, dimensionSystem), JTSUtils.geometry2ewkb(g, target.getDimensionSystem()), target.getEnvelope()));
dimensionSystem, target.getEnvelope()));
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, String.valueOf(o)); throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, String.valueOf(o));
} }
...@@ -102,9 +115,8 @@ public class ValueGeometry extends Value { ...@@ -102,9 +115,8 @@ public class ValueGeometry extends Value {
try { try {
EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget(); EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget();
EWKTUtils.parseEWKT(s, target); EWKTUtils.parseEWKT(s, target);
int dimensionSystem = target.getDimensionSystem(); return (ValueGeometry) Value.cache(new ValueGeometry( //
return (ValueGeometry) Value.cache(new ValueGeometry(EWKTUtils.ewkt2ewkb(s, dimensionSystem), EWKTUtils.ewkt2ewkb(s, target.getDimensionSystem()), target.getEnvelope()));
dimensionSystem, target.getEnvelope()));
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, s); throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, s);
} }
...@@ -129,7 +141,7 @@ public class ValueGeometry extends Value { ...@@ -129,7 +141,7 @@ public class ValueGeometry extends Value {
* @return the value * @return the value
*/ */
public static ValueGeometry get(byte[] bytes) { public static ValueGeometry get(byte[] bytes) {
return (ValueGeometry) Value.cache(new ValueGeometry(bytes, -1, null)); return (ValueGeometry) Value.cache(new ValueGeometry(bytes, UNKNOWN_ENVELOPE));
} }
/** /**
...@@ -142,9 +154,8 @@ public class ValueGeometry extends Value { ...@@ -142,9 +154,8 @@ public class ValueGeometry extends Value {
try { try {
EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget(); EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget();
EWKBUtils.parseEWKB(bytes, target); EWKBUtils.parseEWKB(bytes, target);
int dimensionSystem = target.getDimensionSystem(); return (ValueGeometry) Value.cache(new ValueGeometry( //
return (ValueGeometry) Value.cache(new ValueGeometry(EWKBUtils.ewkb2ewkb(bytes, dimensionSystem), EWKBUtils.ewkb2ewkb(bytes, target.getDimensionSystem()), target.getEnvelope()));
dimensionSystem, target.getEnvelope()));
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, StringUtils.convertBytesToHex(bytes)); throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, StringUtils.convertBytesToHex(bytes));
} }
...@@ -158,8 +169,7 @@ public class ValueGeometry extends Value { ...@@ -158,8 +169,7 @@ public class ValueGeometry extends Value {
*/ */
public static Value fromEnvelope(double[] envelope) { public static Value fromEnvelope(double[] envelope) {
return envelope != null return envelope != null
? Value.cache(new ValueGeometry(EWKBUtils.envelope2wkb(envelope), GeometryUtils.DIMENSION_SYSTEM_XY, ? Value.cache(new ValueGeometry(EWKBUtils.envelope2wkb(envelope), envelope))
envelope))
: ValueNull.INSTANCE; : ValueNull.INSTANCE;
} }
...@@ -180,13 +190,23 @@ public class ValueGeometry extends Value { ...@@ -180,13 +190,23 @@ public class ValueGeometry extends Value {
return ((Geometry) geometry).copy(); return ((Geometry) geometry).copy();
} }
private void calculateInfo() { /**
if (dimensionSystem < 0) { * Returns geometry type and dimension system in OGC geometry code format
EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget(); * (type + dimensionSystem * 1000).
EWKBUtils.parseEWKB(bytes, target); *
envelope = target.getEnvelope(); * @return geometry type and dimension system
dimensionSystem = target.getDimensionSystem(); */
public int getTypeAndDimensionSystem() {
return typeAndDimensionSystem;
} }
/**
* Returns geometry type.
*
* @return geometry type and dimension system
*/
public int getGeometryType() {
return typeAndDimensionSystem % 1_000;
} }
/** /**
...@@ -195,8 +215,16 @@ public class ValueGeometry extends Value { ...@@ -195,8 +215,16 @@ public class ValueGeometry extends Value {
* @return dimension system * @return dimension system
*/ */
public int getDimensionSystem() { public int getDimensionSystem() {
calculateInfo(); return typeAndDimensionSystem / 1_000;
return dimensionSystem; }
/**
* Return a spatial reference system identifier.
*
* @return spatial reference system identifier
*/
public int getSRID() {
return srid;
} }
/** /**
...@@ -205,7 +233,11 @@ public class ValueGeometry extends Value { ...@@ -205,7 +233,11 @@ public class ValueGeometry extends Value {
* @return envelope of this geometry * @return envelope of this geometry
*/ */
public double[] getEnvelopeNoCopy() { public double[] getEnvelopeNoCopy() {
calculateInfo(); if (envelope == UNKNOWN_ENVELOPE) {
EnvelopeTarget target = new EnvelopeTarget();
EWKBUtils.parseEWKB(bytes, target);
envelope = target.getEnvelope();
}
return envelope; return envelope;
} }
...@@ -319,7 +351,9 @@ public class ValueGeometry extends Value { ...@@ -319,7 +351,9 @@ public class ValueGeometry extends Value {
@Override @Override
public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) { public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (targetType == Value.JAVA_OBJECT) { if (targetType == Value.GEOMETRY) {
return extTypeInfo != null ? extTypeInfo.cast(this) : this;
} else if (targetType == Value.JAVA_OBJECT) {
return this; return this;
} }
return super.convertTo(targetType, precision, mode, column, null); return super.convertTo(targetType, precision, mode, column, null);
......
...@@ -8,6 +8,8 @@ package org.h2.test.unit; ...@@ -8,6 +8,8 @@ package org.h2.test.unit;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XY; import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XY;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYM; import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYM;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYZ; import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYZ;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYZM;
import static org.h2.util.geometry.GeometryUtils.GEOMETRY_COLLECTION;
import static org.h2.util.geometry.GeometryUtils.M; import static org.h2.util.geometry.GeometryUtils.M;
import static org.h2.util.geometry.GeometryUtils.MAX_X; 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.MAX_Y;
...@@ -33,6 +35,7 @@ import org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget; ...@@ -33,6 +35,7 @@ import org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget;
import org.h2.util.geometry.GeometryUtils.Target; import org.h2.util.geometry.GeometryUtils.Target;
import org.h2.util.geometry.JTSUtils; import org.h2.util.geometry.JTSUtils;
import org.h2.util.geometry.JTSUtils.GeometryTarget; import org.h2.util.geometry.JTSUtils.GeometryTarget;
import org.h2.value.ValueGeometry;
import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
...@@ -234,8 +237,11 @@ public class TestGeometryUtils extends TestBase { ...@@ -234,8 +237,11 @@ public class TestGeometryUtils extends TestBase {
testEnvelope(envelopeFromJTS, target.getEnvelope()); testEnvelope(envelopeFromJTS, target.getEnvelope());
// Test dimensions // Test dimensions
testDimensions(numOfDimensions > 2 ? GeometryUtils.DIMENSION_SYSTEM_XYZ : GeometryUtils.DIMENSION_SYSTEM_XY, int expectedDimensionSystem = numOfDimensions > 2 ? GeometryUtils.DIMENSION_SYSTEM_XYZ
wkbFromJTS); : GeometryUtils.DIMENSION_SYSTEM_XY;
testDimensions(expectedDimensionSystem, wkbFromJTS);
testValueGeometryProperties(wkbFromJTS);
} }
private void testEnvelope(Envelope envelopeFromJTS, double[] envelopeFromH2) { private void testEnvelope(Envelope envelopeFromJTS, double[] envelopeFromH2) {
...@@ -274,6 +280,7 @@ public class TestGeometryUtils extends TestBase { ...@@ -274,6 +280,7 @@ public class TestGeometryUtils extends TestBase {
testDimensionMCheckPoint(cs); testDimensionMCheckPoint(cs);
assertEquals(ewkb, JTSUtils.geometry2ewkb(p)); assertEquals(ewkb, JTSUtils.geometry2ewkb(p));
testDimensions(GeometryUtils.DIMENSION_SYSTEM_XYM, ewkb); testDimensions(GeometryUtils.DIMENSION_SYSTEM_XYM, ewkb);
testValueGeometryProperties(ewkb);
if (JTSUtils.M_IS_SUPPORTED) { if (JTSUtils.M_IS_SUPPORTED) {
p = (Point) new WKTReader().read("POINT M (1 2 3)"); p = (Point) new WKTReader().read("POINT M (1 2 3)");
...@@ -310,6 +317,7 @@ public class TestGeometryUtils extends TestBase { ...@@ -310,6 +317,7 @@ public class TestGeometryUtils extends TestBase {
testDimensionZMCheckPoint(cs); testDimensionZMCheckPoint(cs);
assertEquals(ewkb, JTSUtils.geometry2ewkb(p)); assertEquals(ewkb, JTSUtils.geometry2ewkb(p));
testDimensions(GeometryUtils.DIMENSION_SYSTEM_XYZM, ewkb); testDimensions(GeometryUtils.DIMENSION_SYSTEM_XYZM, ewkb);
testValueGeometryProperties(ewkb);
if (JTSUtils.M_IS_SUPPORTED) { if (JTSUtils.M_IS_SUPPORTED) {
p = (Point) new WKTReader().read("POINT ZM (1 2 3 4)"); p = (Point) new WKTReader().read("POINT ZM (1 2 3 4)");
...@@ -333,6 +341,32 @@ public class TestGeometryUtils extends TestBase { ...@@ -333,6 +341,32 @@ public class TestGeometryUtils extends TestBase {
assertEquals(4, cs.getOrdinate(0, M)); assertEquals(4, cs.getOrdinate(0, M));
} }
private void testValueGeometryProperties(byte[] ewkb) {
ValueGeometry vg = ValueGeometry.getFromEWKB(ewkb);
DimensionSystemTarget target = new DimensionSystemTarget();
EWKBUtils.parseEWKB(ewkb, target);
int dimensionSystem = target.getDimensionSystem();
assertEquals(dimensionSystem, vg.getDimensionSystem());
String formattedType = EWKTUtils.formatGeometryTypeAndDimensionSystem(vg.getTypeAndDimensionSystem());
assertTrue(EWKTUtils.ewkb2ewkt(ewkb).startsWith(formattedType));
switch (dimensionSystem) {
case DIMENSION_SYSTEM_XY:
assertTrue(formattedType.indexOf(' ') < 0);
break;
case DIMENSION_SYSTEM_XYZ:
assertTrue(formattedType.endsWith(" Z"));
break;
case DIMENSION_SYSTEM_XYM:
assertTrue(formattedType.endsWith(" M"));
break;
case DIMENSION_SYSTEM_XYZM:
assertTrue(formattedType.endsWith(" ZM"));
break;
}
assertEquals(vg.getTypeAndDimensionSystem(), vg.getGeometryType() + vg.getDimensionSystem() * 1_000);
assertEquals(0, vg.getSRID());
}
private void testFiniteOnly() { private void testFiniteOnly() {
for (int i = 0; i < NON_FINITE.length; i++) { for (int i = 0; i < NON_FINITE.length; i++) {
testFiniteOnly(NON_FINITE[i], new EWKBTarget(new ByteArrayOutputStream(), NON_FINITE_DIMENSIONS[i])); testFiniteOnly(NON_FINITE[i], new EWKBTarget(new ByteArrayOutputStream(), NON_FINITE_DIMENSIONS[i]));
...@@ -381,6 +415,9 @@ public class TestGeometryUtils extends TestBase { ...@@ -381,6 +415,9 @@ public class TestGeometryUtils extends TestBase {
assertEquals(10, gc.getSRID()); assertEquals(10, gc.getSRID());
assertEquals(10, gc.getGeometryN(0).getSRID()); assertEquals(10, gc.getGeometryN(0).getSRID());
assertEquals(ewkb, JTSUtils.geometry2ewkb(gc)); assertEquals(ewkb, JTSUtils.geometry2ewkb(gc));
ValueGeometry vg = ValueGeometry.getFromEWKB(ewkb);
assertEquals(10, vg.getSRID());
assertEquals(GEOMETRY_COLLECTION, vg.getTypeAndDimensionSystem());
assertEquals("SRID=-1;POINT EMPTY", EWKTUtils.ewkb2ewkt(EWKTUtils.ewkt2ewkb(" srid=-1 ; POINT EMPTY "))); assertEquals("SRID=-1;POINT EMPTY", EWKTUtils.ewkb2ewkt(EWKTUtils.ewkt2ewkb(" srid=-1 ; POINT EMPTY ")));
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论