Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
ba63ffc6
Unverified
提交
ba63ffc6
authored
9月 12, 2018
作者:
Evgenij Ryazanov
提交者:
GitHub
9月 12, 2018
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #1428 from katzyn/geometry
Add support for M and ZM dimensions to GEOMETRY data type
上级
9f62cc3b
c47184ef
隐藏空白字符变更
内嵌
并排
正在显示
22 个修改的文件
包含
3372 行增加
和
252 行删除
+3372
-252
help.csv
h2/src/docsrc/help/help.csv
+7
-2
advanced.html
h2/src/docsrc/html/advanced.html
+1
-13
Bits.class
h2/src/java9/precompiled/org/h2/util/Bits.class
+0
-0
Bits.java
h2/src/java9/src/org/h2/util/Bits.java
+159
-14
SysProperties.java
h2/src/main/org/h2/engine/SysProperties.java
+12
-0
AggregateDataEnvelope.java
...in/org/h2/expression/aggregate/AggregateDataEnvelope.java
+4
-11
SpatialTreeIndex.java
h2/src/main/org/h2/index/SpatialTreeIndex.java
+9
-5
MVSpatialIndex.java
h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java
+20
-18
Bits.java
h2/src/main/org/h2/util/Bits.java
+123
-3
EWKBUtils.java
h2/src/main/org/h2/util/geometry/EWKBUtils.java
+502
-0
EWKTUtils.java
h2/src/main/org/h2/util/geometry/EWKTUtils.java
+830
-0
GeometryUtils.java
h2/src/main/org/h2/util/geometry/GeometryUtils.java
+552
-0
JTSUtils.java
h2/src/main/org/h2/util/geometry/JTSUtils.java
+482
-0
package.html
h2/src/main/org/h2/util/geometry/package.html
+14
-0
DataType.java
h2/src/main/org/h2/value/DataType.java
+1
-1
Value.java
h2/src/main/org/h2/value/Value.java
+1
-1
ValueGeometry.java
h2/src/main/org/h2/value/ValueGeometry.java
+107
-165
TestAll.java
h2/src/test/org/h2/test/TestAll.java
+2
-0
TestSpatial.java
h2/src/test/org/h2/test/db/TestSpatial.java
+6
-12
TestGeometryUtils.java
h2/src/test/org/h2/test/unit/TestGeometryUtils.java
+490
-0
TestUtils.java
h2/src/test/org/h2/test/unit/TestUtils.java
+47
-7
dictionary.txt
h2/src/tools/org/h2/build/doc/dictionary.txt
+3
-0
没有找到文件。
h2/src/docsrc/help/help.csv
浏览文件 @
ba63ffc6
...
...
@@ -3038,9 +3038,14 @@ ENUM('clubs', 'diamonds', 'hearts', 'spades')
"Data Types","GEOMETRY Type","
GEOMETRY
","
A spatial geometry type, based on the ""org.locationtech.jts"" library.
A spatial geometry type.
Mapped to ""org.locationtech.jts.geom.Geometry"" if JTS library is in classpath.
May be represented in textual format using the WKT (well-known text) or EWKT (extended well-known text) format.
Values are stored internally in EWKB (extended well-known binary) format.
Only a subset of EWKB and EWKT features is supported.
Supported objects are POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, and GEOMETRYCOLLECTION.
Supported dimension systems are XY (2D), XYZ, XYM, and XYZM.
SRID (spatial reference system identifier) is supported.
Use a quoted string containing a WKT/EWKT formatted string or ""PreparedStatement.setObject()"" to store values,
and ""ResultSet.getObject(..)"" or ""ResultSet.getString(..)"" to retrieve the values.
...
...
@@ -3437,7 +3442,7 @@ MODE(X ORDER BY X)
MODE() WITHIN GROUP(ORDER BY X)
"
"Functions (Aggregate)","
ST_EXTENT
","
"Functions (Aggregate)","
ENVELOPE
","
ENVELOPE( value ) [ FILTER ( WHERE expression ) ]
","
Returns the minimum bounding box that encloses all specified GEOMETRY values.
...
...
h2/src/docsrc/html/advanced.html
浏览文件 @
ba63ffc6
...
...
@@ -1415,19 +1415,7 @@ that means the probability is about 0.000'000'000'06.
<h2
id=
"spatial_features"
>
Spatial Features
</h2>
<p>
H2 supports the geometry data type and spatial indexes if
the
<a
href=
"https://projects.eclipse.org/projects/locationtech.jts"
>
JTS Topology Suite
</a>
is in the classpath.
To run the H2 Console tool with the JTS tool, you need to download the
<a
href=
"https://search.maven.org/remotecontent?filepath=org/locationtech/jts/jts-core/1.15.0/jts-core-1.15.0.jar"
>
JTS-CORE 1.15.0 jar file
</a>
and place it in the h2 bin directory. Then edit the
<code>
h2.sh
</code>
file as follows:
</p>
<pre>
#!/bin/sh
dir=$(dirname "$0")
java -cp "$dir/h2.jar:jts-core-1.15.0.jar:$H2DRIVERS:$CLASSPATH" org.h2.tools.Console "$@"
</pre>
<p>
H2 supports the geometry data type and spatial indexes.
Here is an example SQL script to create a table with a spatial column and index:
</p>
<pre>
...
...
h2/src/java9/precompiled/org/h2/util/Bits.class
浏览文件 @
ba63ffc6
No preview for this file type
h2/src/java9/src/org/h2/util/Bits.java
浏览文件 @
ba63ffc6
...
...
@@ -18,16 +18,45 @@ import java.util.UUID;
public
final
class
Bits
{
/**
* VarHandle giving access to elements of a byte[] array viewed as if it
were a
* int[] array on big-endian system.
* VarHandle giving access to elements of a byte[] array viewed as if it
*
were a
int[] array on big-endian system.
*/
private
static
final
VarHandle
INT_VH
=
MethodHandles
.
byteArrayViewVarHandle
(
int
[].
class
,
ByteOrder
.
BIG_ENDIAN
);
private
static
final
VarHandle
INT_VH
_BE
=
MethodHandles
.
byteArrayViewVarHandle
(
int
[].
class
,
ByteOrder
.
BIG_ENDIAN
);
/**
* VarHandle giving access to elements of a byte[] array viewed as if it
were a
*
long[] array on big
-endian system.
* VarHandle giving access to elements of a byte[] array viewed as if it
*
were a int[] array on little
-endian system.
*/
private
static
final
VarHandle
LONG_VH
=
MethodHandles
.
byteArrayViewVarHandle
(
long
[].
class
,
ByteOrder
.
BIG_ENDIAN
);
private
static
final
VarHandle
INT_VH_LE
=
MethodHandles
.
byteArrayViewVarHandle
(
int
[].
class
,
ByteOrder
.
LITTLE_ENDIAN
);
/**
* VarHandle giving access to elements of a byte[] array viewed as if it
* were a long[] array on big-endian system.
*/
private
static
final
VarHandle
LONG_VH_BE
=
MethodHandles
.
byteArrayViewVarHandle
(
long
[].
class
,
ByteOrder
.
BIG_ENDIAN
);
/**
* VarHandle giving access to elements of a byte[] array viewed as if it
* were a long[] array on little-endian system.
*/
private
static
final
VarHandle
LONG_VH_LE
=
MethodHandles
.
byteArrayViewVarHandle
(
long
[].
class
,
ByteOrder
.
LITTLE_ENDIAN
);
/**
* VarHandle giving access to elements of a byte[] array viewed as if it
* were a double[] array on big-endian system.
*/
private
static
final
VarHandle
DOUBLE_VH_BE
=
MethodHandles
.
byteArrayViewVarHandle
(
double
[].
class
,
ByteOrder
.
BIG_ENDIAN
);
/**
* VarHandle giving access to elements of a byte[] array viewed as if it
* were a double[] array on little-endian system.
*/
private
static
final
VarHandle
DOUBLE_VH_LE
=
MethodHandles
.
byteArrayViewVarHandle
(
double
[].
class
,
ByteOrder
.
LITTLE_ENDIAN
);
/**
* Compare the contents of two char arrays. If the content or length of the
...
...
@@ -96,12 +125,26 @@ public final class Bits {
* @return the value
*/
public
static
int
readInt
(
byte
[]
buff
,
int
pos
)
{
return
(
int
)
INT_VH
.
get
(
buff
,
pos
);
return
(
int
)
INT_VH
_BE
.
get
(
buff
,
pos
);
}
/**
* Reads a long value from the byte array at the given position in big-endian
* order.
* Reads a int value from the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @return the value
*/
public
static
int
readIntLE
(
byte
[]
buff
,
int
pos
)
{
return
(
int
)
INT_VH_LE
.
get
(
buff
,
pos
);
}
/**
* Reads a long value from the byte array at the given position in
* big-endian order.
*
* @param buff
* the byte array
...
...
@@ -110,7 +153,49 @@ public final class Bits {
* @return the value
*/
public
static
long
readLong
(
byte
[]
buff
,
int
pos
)
{
return
(
long
)
LONG_VH
.
get
(
buff
,
pos
);
return
(
long
)
LONG_VH_BE
.
get
(
buff
,
pos
);
}
/**
* Reads a long value from the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @return the value
*/
public
static
long
readLongLE
(
byte
[]
buff
,
int
pos
)
{
return
(
long
)
LONG_VH_LE
.
get
(
buff
,
pos
);
}
/**
* Reads a double value from the byte array at the given position in
* big-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @return the value
*/
public
static
double
readDouble
(
byte
[]
buff
,
int
pos
)
{
return
(
double
)
DOUBLE_VH_BE
.
get
(
buff
,
pos
);
}
/**
* Reads a double value from the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @return the value
*/
public
static
double
readDoubleLE
(
byte
[]
buff
,
int
pos
)
{
return
(
double
)
DOUBLE_VH_LE
.
get
(
buff
,
pos
);
}
/**
...
...
@@ -124,8 +209,8 @@ public final class Bits {
*/
public
static
byte
[]
uuidToBytes
(
long
msb
,
long
lsb
)
{
byte
[]
buff
=
new
byte
[
16
];
LONG_VH
.
set
(
buff
,
0
,
msb
);
LONG_VH
.
set
(
buff
,
8
,
lsb
);
LONG_VH
_BE
.
set
(
buff
,
0
,
msb
);
LONG_VH
_BE
.
set
(
buff
,
8
,
lsb
);
return
buff
;
}
...
...
@@ -152,7 +237,22 @@ public final class Bits {
* the value to write
*/
public
static
void
writeInt
(
byte
[]
buff
,
int
pos
,
int
x
)
{
INT_VH
.
set
(
buff
,
pos
,
x
);
INT_VH_BE
.
set
(
buff
,
pos
,
x
);
}
/**
* Writes a int value to the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @param x
* the value to write
*/
public
static
void
writeIntLE
(
byte
[]
buff
,
int
pos
,
int
x
)
{
INT_VH_LE
.
set
(
buff
,
pos
,
x
);
}
/**
...
...
@@ -167,7 +267,52 @@ public final class Bits {
* the value to write
*/
public
static
void
writeLong
(
byte
[]
buff
,
int
pos
,
long
x
)
{
LONG_VH
.
set
(
buff
,
pos
,
x
);
LONG_VH_BE
.
set
(
buff
,
pos
,
x
);
}
/**
* Writes a long value to the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @param x
* the value to write
*/
public
static
void
writeLongLE
(
byte
[]
buff
,
int
pos
,
long
x
)
{
LONG_VH_LE
.
set
(
buff
,
pos
,
x
);
}
/**
* Writes a double value to the byte array at the given position in
* big-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @param x
* the value to write
*/
public
static
void
writeDouble
(
byte
[]
buff
,
int
pos
,
double
x
)
{
DOUBLE_VH_BE
.
set
(
buff
,
pos
,
x
);
}
/**
* Writes a double value to the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @param x
* the value to write
*/
public
static
void
writeDoubleLE
(
byte
[]
buff
,
int
pos
,
double
x
)
{
DOUBLE_VH_LE
.
set
(
buff
,
pos
,
x
);
}
private
Bits
()
{
...
...
h2/src/main/org/h2/engine/SysProperties.java
浏览文件 @
ba63ffc6
...
...
@@ -565,6 +565,18 @@ public class SysProperties {
public
static
final
String
AUTH_CONFIG_FILE
=
Utils
.
getProperty
(
"h2.authConfigFile"
,
null
);
/**
* System property {@code h2.mixedGeometries}, {@code false} by default.
* <p>
* If {@code true} illegal geometries with mixed XY/XYZ dimensionality like
* {@code 'LINESTRING (1 2, 3 4 5)'} are accepted.
* </p>
* <p>
* If {@code false} such geometries are rejected with data conversion error.
* </p>
*/
public
static
final
boolean
MIXED_GEOMETRIES
=
Utils
.
getProperty
(
"h2.mixedGeometries"
,
false
);
private
static
final
String
H2_BASE_DIR
=
"h2.baseDir"
;
private
SysProperties
()
{
...
...
h2/src/main/org/h2/expression/aggregate/AggregateDataEnvelope.java
浏览文件 @
ba63ffc6
...
...
@@ -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,12 @@ 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
())
{
return
ValueNull
.
INSTANCE
;
}
return
ValueGeometry
.
getFromGeometry
(
new
GeometryFactory
().
toGeometry
(
envelope
));
return
ValueGeometry
.
fromEnvelope
(
envelope
);
}
}
h2/src/main/org/h2/index/SpatialTreeIndex.java
浏览文件 @
ba63ffc6
...
...
@@ -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.
...
...
@@ -130,13 +134,13 @@ public class SpatialTreeIndex extends BaseIndex implements SpatialIndex {
return
null
;
}
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
());
}
Envelope
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
...
...
h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java
浏览文件 @
ba63ffc6
...
...
@@ -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
;
...
...
@@ -33,8 +38,6 @@ 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,21 +267,21 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
bmaxyf
=
maxyf
;
}
}
return
ValueGeometry
.
getFromGeometry
(
new
GeometryFactory
().
toGeometry
(
new
Envelope
(
bminxf
,
bmaxxf
,
bminyf
,
bmaxyf
)));
return
ValueGeometry
.
fromEnvelope
(
new
double
[]
{
bminxf
,
bmaxxf
,
bminyf
,
bmaxyf
});
}
return
ValueNull
.
INSTANCE
;
}
private
SpatialKey
getKey
(
SearchRow
row
)
{
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
());
}
Envelope
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,8 @@ 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
.
fromEnvelope
(
new
double
[]
{
bminxd
,
bmaxxd
,
bminyd
,
bmaxyd
})
:
ValueNull
.
INSTANCE
;
}
}
...
...
h2/src/main/org/h2/util/Bits.java
浏览文件 @
ba63ffc6
...
...
@@ -124,8 +124,22 @@ public final class Bits {
}
/**
* Reads a long value from the byte array at the given position in big-endian
* order.
* Reads a int value from the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @return the value
*/
public
static
int
readIntLE
(
byte
[]
buff
,
int
pos
)
{
return
(
buff
[
pos
++]
&
0xff
)
+
((
buff
[
pos
++]
&
0xff
)
<<
8
)
+
((
buff
[
pos
++]
&
0xff
)
<<
16
)
+
(
buff
[
pos
]
<<
24
);
}
/**
* Reads a long value from the byte array at the given position in
* big-endian order.
*
* @param buff
* the byte array
...
...
@@ -134,7 +148,49 @@ public final class Bits {
* @return the value
*/
public
static
long
readLong
(
byte
[]
buff
,
int
pos
)
{
return
(((
long
)
readInt
(
buff
,
pos
))
<<
32
)
+
(
readInt
(
buff
,
pos
+
4
)
&
0xffffffff
L
);
return
(((
long
)
readInt
(
buff
,
pos
))
<<
32
)
+
(
readInt
(
buff
,
pos
+
4
)
&
0xffff_ffff
L
);
}
/**
* Reads a long value from the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @return the value
*/
public
static
long
readLongLE
(
byte
[]
buff
,
int
pos
)
{
return
(
readIntLE
(
buff
,
pos
)
&
0xffff_ffff
L
)
+
(((
long
)
readIntLE
(
buff
,
pos
+
4
))
<<
32
);
}
/**
* Reads a double value from the byte array at the given position in
* big-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @return the value
*/
public
static
double
readDouble
(
byte
[]
buff
,
int
pos
)
{
return
Double
.
longBitsToDouble
(
readLong
(
buff
,
pos
));
}
/**
* Reads a double value from the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @return the value
*/
public
static
double
readDoubleLE
(
byte
[]
buff
,
int
pos
)
{
return
Double
.
longBitsToDouble
(
readLongLE
(
buff
,
pos
));
}
/**
...
...
@@ -184,6 +240,24 @@ public final class Bits {
buff
[
pos
]
=
(
byte
)
x
;
}
/**
* Writes a int value to the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @param x
* the value to write
*/
public
static
void
writeIntLE
(
byte
[]
buff
,
int
pos
,
int
x
)
{
buff
[
pos
++]
=
(
byte
)
x
;
buff
[
pos
++]
=
(
byte
)
(
x
>>
8
);
buff
[
pos
++]
=
(
byte
)
(
x
>>
16
);
buff
[
pos
]
=
(
byte
)
(
x
>>
24
);
}
/**
* Writes a long value to the byte array at the given position in big-endian
* order.
...
...
@@ -200,6 +274,52 @@ public final class Bits {
writeInt
(
buff
,
pos
+
4
,
(
int
)
x
);
}
/**
* Writes a long value to the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @param x
* the value to write
*/
public
static
void
writeLongLE
(
byte
[]
buff
,
int
pos
,
long
x
)
{
writeIntLE
(
buff
,
pos
,
(
int
)
x
);
writeIntLE
(
buff
,
pos
+
4
,
(
int
)
(
x
>>
32
));
}
/**
* Writes a double value to the byte array at the given position in
* big-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @param x
* the value to write
*/
public
static
void
writeDouble
(
byte
[]
buff
,
int
pos
,
double
x
)
{
writeLong
(
buff
,
pos
,
Double
.
doubleToRawLongBits
(
x
));
}
/**
* Writes a double value to the byte array at the given position in
* little-endian order.
*
* @param buff
* the byte array
* @param pos
* the position
* @param x
* the value to write
*/
public
static
void
writeDoubleLE
(
byte
[]
buff
,
int
pos
,
double
x
)
{
writeLongLE
(
buff
,
pos
,
Double
.
doubleToRawLongBits
(
x
));
}
private
Bits
()
{
}
}
h2/src/main/org/h2/util/geometry/EWKBUtils.java
0 → 100644
浏览文件 @
ba63ffc6
/*
* 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
.
util
.
geometry
;
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_XYZM
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
GEOMETRY_COLLECTION
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
LINE_STRING
;
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
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MULTI_LINE_STRING
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MULTI_POINT
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MULTI_POLYGON
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
POINT
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
POLYGON
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
checkFinite
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
toCanonicalDouble
;
import
java.io.ByteArrayOutputStream
;
import
org.h2.engine.SysProperties
;
import
org.h2.util.Bits
;
import
org.h2.util.StringUtils
;
import
org.h2.util.geometry.GeometryUtils.DimensionSystemTarget
;
import
org.h2.util.geometry.GeometryUtils.Target
;
/**
* EWKB format support for GEOMETRY data type.
*
* <p>
* This class provides limited support of EWKB. EWKB is based on Well-known
* Binary Representation (WKB) from OGC 06-103r4 and includes additional PostGIS
* extensions. This class can read dimension system marks in both OGC WKB and
* EWKB formats, but always writes them in EWKB format. SRID support from EWKB
* is implemented. As an addition POINT EMPTY is stored with NaN values as
* specified in <a href="http://www.geopackage.org/spec/">OGC 12-128r15</a>.
* </p>
*/
public
final
class
EWKBUtils
{
/**
* Converter output target that writes a EWKB.
*/
public
static
final
class
EWKBTarget
extends
Target
{
private
final
ByteArrayOutputStream
output
;
private
final
int
dimensionSystem
;
private
final
byte
[]
buf
=
new
byte
[
8
];
private
int
type
;
private
int
srid
;
/**
* Creates a new EWKB output target.
*
* @param output
* output stream
* @param dimensionSystem
* dimension system to use
*/
public
EWKBTarget
(
ByteArrayOutputStream
output
,
int
dimensionSystem
)
{
this
.
output
=
output
;
this
.
dimensionSystem
=
dimensionSystem
;
}
@Override
protected
void
init
(
int
srid
)
{
this
.
srid
=
srid
;
}
@Override
protected
void
startPoint
()
{
writeHeader
(
POINT
);
}
@Override
protected
void
startLineString
(
int
numPoints
)
{
writeHeader
(
LINE_STRING
);
writeInt
(
numPoints
);
}
@Override
protected
void
startPolygon
(
int
numInner
,
int
numPoints
)
{
writeHeader
(
POLYGON
);
writeInt
(
numInner
+
1
);
writeInt
(
numPoints
);
}
@Override
protected
void
startPolygonInner
(
int
numInner
)
{
writeInt
(
numInner
);
}
@Override
protected
void
startCollection
(
int
type
,
int
numItems
)
{
writeHeader
(
type
);
writeInt
(
numItems
);
}
private
void
writeHeader
(
int
type
)
{
this
.
type
=
type
;
switch
(
dimensionSystem
)
{
case
DIMENSION_SYSTEM_XYZ:
type
|=
EWKB_Z
;
break
;
case
DIMENSION_SYSTEM_XYZM:
type
|=
EWKB_Z
;
//$FALL-THROUGH$
case
DIMENSION_SYSTEM_XYM:
type
|=
EWKB_M
;
}
if
(
srid
!=
0
)
{
type
|=
EWKB_SRID
;
}
output
.
write
(
0
);
writeInt
(
type
);
if
(
srid
!=
0
)
{
writeInt
(
srid
);
// Never write SRID in inner objects
srid
=
0
;
}
}
@Override
protected
Target
startCollectionItem
(
int
index
,
int
total
)
{
return
this
;
}
@Override
protected
void
addCoordinate
(
double
x
,
double
y
,
double
z
,
double
m
,
int
index
,
int
total
)
{
boolean
check
=
type
!=
POINT
||
!
Double
.
isNaN
(
x
)
||
!
Double
.
isNaN
(
y
)
||
!
Double
.
isNaN
(
z
)
||
!
Double
.
isNaN
(
m
);
if
(
check
)
{
checkFinite
(
x
);
checkFinite
(
y
);
}
writeDouble
(
x
);
writeDouble
(
y
);
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYZ
)
!=
0
)
{
writeDouble
(!
SysProperties
.
MIXED_GEOMETRIES
&&
check
?
checkFinite
(
z
)
:
z
);
}
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
)
{
writeDouble
(
check
?
checkFinite
(
m
)
:
m
);
}
}
private
void
writeInt
(
int
v
)
{
Bits
.
writeInt
(
buf
,
0
,
v
);
output
.
write
(
buf
,
0
,
4
);
}
private
void
writeDouble
(
double
v
)
{
v
=
toCanonicalDouble
(
v
);
Bits
.
writeDouble
(
buf
,
0
,
v
);
output
.
write
(
buf
,
0
,
8
);
}
}
/**
* Helper source object for EWKB reading.
*/
private
static
final
class
EWKBSource
{
private
final
byte
[]
ewkb
;
private
int
offset
;
/**
* Whether current byte order is big-endian.
*/
boolean
bigEndian
;
/**
* Creates new instance of EWKB source.
*
* @param ewkb
* EWKB
*/
EWKBSource
(
byte
[]
ewkb
)
{
this
.
ewkb
=
ewkb
;
}
/**
* Reads one byte.
*
* @return next byte
*/
byte
readByte
()
{
return
ewkb
[
offset
++];
}
/**
* Reads a 32-bit integer using current byte order.
*
* @return next 32-bit integer
*/
int
readInt
()
{
int
result
=
bigEndian
?
Bits
.
readInt
(
ewkb
,
offset
)
:
Bits
.
readIntLE
(
ewkb
,
offset
);
offset
+=
4
;
return
result
;
}
/**
* Reads a 64-bit floating point using current byte order.
*
* @return next 64-bit floating point
*/
double
readCoordinate
()
{
double
v
=
bigEndian
?
Bits
.
readDouble
(
ewkb
,
offset
)
:
Bits
.
readDoubleLE
(
ewkb
,
offset
);
offset
+=
8
;
return
toCanonicalDouble
(
v
);
}
@Override
public
String
toString
()
{
String
s
=
StringUtils
.
convertBytesToHex
(
ewkb
);
int
idx
=
offset
*
2
;
return
new
StringBuilder
(
s
.
length
()
+
3
).
append
(
s
,
0
,
idx
).
append
(
"<*>"
).
append
(
s
,
idx
,
s
.
length
())
.
toString
();
}
}
/**
* Geometry type mask that indicates presence of dimension Z.
*/
private
static
final
int
EWKB_Z
=
0x8000_0000
;
/**
* Geometry type mask that indicates presence of dimension M.
*/
private
static
final
int
EWKB_M
=
0x4000_0000
;
/**
* Geometry type mask that indicates presence of SRID.
*/
private
static
final
int
EWKB_SRID
=
0x2000_0000
;
/**
* Converts any supported EWKB to EWKB representation that is used by this
* class. Reduces dimension system to minimal possible and uses EWKB flags
* for dimension system indication. May also perform other changes.
*
* @param ewkb
* source EWKB
* @return canonical EWKB, may be the same as the source
*/
public
static
byte
[]
ewkb2ewkb
(
byte
[]
ewkb
)
{
// Determine dimension system first
DimensionSystemTarget
dimensionTarget
=
new
DimensionSystemTarget
();
parseEWKB
(
ewkb
,
dimensionTarget
);
// Write an EWKB
return
ewkb2ewkb
(
ewkb
,
dimensionTarget
.
getDimensionSystem
());
}
/**
* Converts any supported EWKB to EWKB representation that is used by this
* class. Reduces dimension system to minimal possible and uses EWKB flags
* for dimension system indication. May also perform other changes.
*
* @param ewkb
* source EWKB
* @param dimension
* dimension system
* @return canonical EWKB, may be the same as the source
*/
public
static
byte
[]
ewkb2ewkb
(
byte
[]
ewkb
,
int
dimensionSystem
)
{
ByteArrayOutputStream
output
=
new
ByteArrayOutputStream
();
EWKBTarget
target
=
new
EWKBTarget
(
output
,
dimensionSystem
);
parseEWKB
(
ewkb
,
target
);
return
output
.
toByteArray
();
}
/**
* Parses a EWKB.
*
* @param ewkb
* EWKB representation
* @param target
* output target
*/
public
static
void
parseEWKB
(
byte
[]
ewkb
,
Target
target
)
{
try
{
parseEWKB
(
new
EWKBSource
(
ewkb
),
target
,
0
);
}
catch
(
ArrayIndexOutOfBoundsException
e
)
{
throw
new
IllegalArgumentException
();
}
}
/**
* Parses a EWKB.
*
* @param source
* EWKB source
* @param target
* output target
* @param parentType
* type of parent geometry collection, or 0 for the root geometry
*/
private
static
void
parseEWKB
(
EWKBSource
source
,
Target
target
,
int
parentType
)
{
// Read byte order of a next geometry
switch
(
source
.
readByte
())
{
case
0
:
source
.
bigEndian
=
true
;
break
;
case
1
:
source
.
bigEndian
=
false
;
break
;
default
:
throw
new
IllegalArgumentException
();
}
// Type contains type of a geometry and additional flags
int
type
=
source
.
readInt
();
// PostGIS extensions
boolean
useZ
=
(
type
&
EWKB_Z
)
!=
0
;
boolean
useM
=
(
type
&
EWKB_M
)
!=
0
;
int
srid
=
(
type
&
EWKB_SRID
)
!=
0
?
source
.
readInt
()
:
0
;
// Use only top-level SRID
if
(
parentType
==
0
)
{
target
.
init
(
srid
);
}
// 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
;
}
type
%=
1_000
;
switch
(
type
)
{
case
POINT:
if
(
parentType
!=
0
&&
parentType
!=
MULTI_POINT
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPoint
();
addCoordinate
(
source
,
target
,
useZ
,
useM
,
0
,
1
);
break
;
case
LINE_STRING:
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_LINE_STRING
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
int
numPoints
=
source
.
readInt
();
if
(
numPoints
<
0
||
numPoints
==
1
)
{
throw
new
IllegalArgumentException
();
}
target
.
startLineString
(
numPoints
);
for
(
int
i
=
0
;
i
<
numPoints
;
i
++)
{
addCoordinate
(
source
,
target
,
useZ
,
useM
,
i
,
numPoints
);
}
break
;
}
case
POLYGON:
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_POLYGON
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
int
numInner
=
source
.
readInt
()
-
1
;
if
(
numInner
<
0
)
{
throw
new
IllegalArgumentException
();
}
int
size
=
source
.
readInt
();
// Size may be 0 (EMPTY) or 4+
if
(
size
<
0
||
size
>=
1
&&
size
<=
3
)
{
throw
new
IllegalArgumentException
();
}
if
(
size
==
0
&&
numInner
>
0
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPolygon
(
numInner
,
size
);
if
(
size
>
0
)
{
addRing
(
source
,
target
,
useZ
,
useM
,
size
);
for
(
int
i
=
0
;
i
<
numInner
;
i
++)
{
size
=
source
.
readInt
();
// Size may be 0 (EMPTY) or 4+
if
(
size
<
0
||
size
>=
1
&&
size
<=
3
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPolygonInner
(
size
);
addRing
(
source
,
target
,
useZ
,
useM
,
size
);
}
target
.
endNonEmptyPolygon
();
}
break
;
}
case
MULTI_POINT:
case
MULTI_LINE_STRING:
case
MULTI_POLYGON:
case
GEOMETRY_COLLECTION:
{
if
(
parentType
!=
0
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
int
numItems
=
source
.
readInt
();
if
(
numItems
<
0
)
{
throw
new
IllegalArgumentException
();
}
target
.
startCollection
(
type
,
numItems
);
for
(
int
i
=
0
;
i
<
numItems
;
i
++)
{
Target
innerTarget
=
target
.
startCollectionItem
(
i
,
numItems
);
parseEWKB
(
source
,
innerTarget
,
type
);
target
.
endCollectionItem
(
innerTarget
,
i
,
numItems
);
}
target
.
endCollection
(
type
);
break
;
}
default
:
throw
new
IllegalArgumentException
();
}
}
private
static
void
addRing
(
EWKBSource
source
,
Target
target
,
boolean
useZ
,
boolean
useM
,
int
size
)
{
// 0 or 4+ are valid
if
(
size
>=
4
)
{
double
startX
=
source
.
readCoordinate
(),
startY
=
source
.
readCoordinate
();
target
.
addCoordinate
(
startX
,
startY
,
//
useZ
?
source
.
readCoordinate
()
:
Double
.
NaN
,
useM
?
source
.
readCoordinate
()
:
Double
.
NaN
,
//
0
,
size
);
for
(
int
i
=
1
;
i
<
size
-
1
;
i
++)
{
addCoordinate
(
source
,
target
,
useZ
,
useM
,
i
,
size
);
}
double
endX
=
source
.
readCoordinate
(),
endY
=
source
.
readCoordinate
();
/*
* TODO OGC 06-103r4 determines points as equal if they have the
* same X and Y coordinates. Should we check Z and M here too?
*/
if
(
startX
!=
endX
||
startY
!=
endY
)
{
throw
new
IllegalArgumentException
();
}
target
.
addCoordinate
(
endX
,
endY
,
//
useZ
?
source
.
readCoordinate
()
:
Double
.
NaN
,
useM
?
source
.
readCoordinate
()
:
Double
.
NaN
,
//
size
-
1
,
size
);
}
}
private
static
void
addCoordinate
(
EWKBSource
source
,
Target
target
,
boolean
useZ
,
boolean
useM
,
int
index
,
int
total
)
{
target
.
addCoordinate
(
source
.
readCoordinate
(),
source
.
readCoordinate
(),
useZ
?
source
.
readCoordinate
()
:
Double
.
NaN
,
useM
?
source
.
readCoordinate
()
:
Double
.
NaN
,
//
index
,
total
);
}
/**
* Converts an envelope to a WKB.
*
* @param envelope
* envelope, or null
* @return WKB, or null
*/
public
static
byte
[]
envelope2wkb
(
double
[]
envelope
)
{
if
(
envelope
==
null
)
{
return
null
;
}
byte
[]
result
;
double
minX
=
envelope
[
MIN_X
],
maxX
=
envelope
[
MAX_X
],
minY
=
envelope
[
MIN_Y
],
maxY
=
envelope
[
MAX_Y
];
if
(
minX
==
maxX
&&
minY
==
maxY
)
{
result
=
new
byte
[
21
];
result
[
4
]
=
POINT
;
Bits
.
writeDouble
(
result
,
5
,
minX
);
Bits
.
writeDouble
(
result
,
13
,
minY
);
}
else
if
(
minX
==
maxX
||
minY
==
maxY
)
{
result
=
new
byte
[
41
];
result
[
4
]
=
LINE_STRING
;
result
[
8
]
=
2
;
Bits
.
writeDouble
(
result
,
9
,
minX
);
Bits
.
writeDouble
(
result
,
17
,
minY
);
Bits
.
writeDouble
(
result
,
25
,
maxX
);
Bits
.
writeDouble
(
result
,
33
,
maxY
);
}
else
{
result
=
new
byte
[
93
];
result
[
4
]
=
POLYGON
;
result
[
8
]
=
1
;
result
[
12
]
=
5
;
Bits
.
writeDouble
(
result
,
13
,
minX
);
Bits
.
writeDouble
(
result
,
21
,
minY
);
Bits
.
writeDouble
(
result
,
29
,
minX
);
Bits
.
writeDouble
(
result
,
37
,
maxY
);
Bits
.
writeDouble
(
result
,
45
,
maxX
);
Bits
.
writeDouble
(
result
,
53
,
maxY
);
Bits
.
writeDouble
(
result
,
61
,
maxX
);
Bits
.
writeDouble
(
result
,
69
,
minY
);
Bits
.
writeDouble
(
result
,
77
,
minX
);
Bits
.
writeDouble
(
result
,
85
,
minY
);
}
return
result
;
}
private
EWKBUtils
()
{
}
}
h2/src/main/org/h2/util/geometry/EWKTUtils.java
0 → 100644
浏览文件 @
ba63ffc6
/*
* 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
.
util
.
geometry
;
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_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
.
LINE_STRING
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
M
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MULTI_LINE_STRING
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MULTI_POINT
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MULTI_POLYGON
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
POINT
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
POLYGON
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
X
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
Y
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
Z
;
import
java.io.ByteArrayOutputStream
;
import
java.util.ArrayList
;
import
org.h2.engine.SysProperties
;
import
org.h2.util.geometry.EWKBUtils.EWKBTarget
;
import
org.h2.util.geometry.GeometryUtils.DimensionSystemTarget
;
import
org.h2.util.geometry.GeometryUtils.Target
;
/**
* EWKT format support for GEOMETRY data type.
*
* <p>
* This class provides limited support of EWKT. EWKT is based on Well-known Text
* Representation (WKT) from OGC 06-103r4 and includes additional <a href=
* "https://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT">PostGIS
* extensions</a>. SRID support from EWKT is implemented.
* </p>
*/
public
final
class
EWKTUtils
{
/**
* Converter output target that writes a EWKT.
*/
public
static
final
class
EWKTTarget
extends
Target
{
private
final
StringBuilder
output
;
private
final
int
dimensionSystem
;
private
int
type
;
private
boolean
inMulti
;
/**
* Creates a new EWKT output target.
*
* @param output
* output stream
* @param dimensionSystem
* dimension system to use
*/
public
EWKTTarget
(
StringBuilder
output
,
int
dimensionSystem
)
{
this
.
output
=
output
;
this
.
dimensionSystem
=
dimensionSystem
;
}
@Override
protected
void
init
(
int
srid
)
{
if
(
srid
!=
0
)
{
output
.
append
(
"SRID="
).
append
(
srid
).
append
(
';'
);
}
}
@Override
protected
void
startPoint
()
{
writeHeader
(
POINT
);
}
@Override
protected
void
startLineString
(
int
numPoints
)
{
writeHeader
(
LINE_STRING
);
if
(
numPoints
==
0
)
{
output
.
append
(
"EMPTY"
);
}
}
@Override
protected
void
startPolygon
(
int
numInner
,
int
numPoints
)
{
writeHeader
(
POLYGON
);
if
(
numPoints
==
0
)
{
output
.
append
(
"EMPTY"
);
}
else
{
output
.
append
(
'('
);
}
}
@Override
protected
void
startPolygonInner
(
int
numInner
)
{
output
.
append
(
numInner
>
0
?
", "
:
", EMPTY"
);
}
@Override
protected
void
endNonEmptyPolygon
()
{
output
.
append
(
')'
);
}
@Override
protected
void
startCollection
(
int
type
,
int
numItems
)
{
writeHeader
(
type
);
if
(
numItems
==
0
)
{
output
.
append
(
"EMPTY"
);
}
if
(
type
!=
GEOMETRY_COLLECTION
)
{
inMulti
=
true
;
}
}
private
void
writeHeader
(
int
type
)
{
this
.
type
=
type
;
if
(
inMulti
)
{
return
;
}
switch
(
type
)
{
case
POINT:
output
.
append
(
"POINT"
);
break
;
case
LINE_STRING:
output
.
append
(
"LINESTRING"
);
break
;
case
POLYGON:
output
.
append
(
"POLYGON"
);
break
;
case
MULTI_POINT:
output
.
append
(
"MULTIPOINT"
);
break
;
case
MULTI_LINE_STRING:
output
.
append
(
"MULTILINESTRING"
);
break
;
case
MULTI_POLYGON:
output
.
append
(
"MULTIPOLYGON"
);
break
;
case
GEOMETRY_COLLECTION:
output
.
append
(
"GEOMETRYCOLLECTION"
);
break
;
default
:
throw
new
IllegalArgumentException
();
}
switch
(
dimensionSystem
)
{
case
DIMENSION_SYSTEM_XYZ:
output
.
append
(
" Z"
);
break
;
case
DIMENSION_SYSTEM_XYM:
output
.
append
(
" M"
);
break
;
case
DIMENSION_SYSTEM_XYZM:
output
.
append
(
" ZM"
);
}
output
.
append
(
' '
);
}
@Override
protected
Target
startCollectionItem
(
int
index
,
int
total
)
{
if
(
index
==
0
)
{
output
.
append
(
'('
);
}
else
{
output
.
append
(
", "
);
}
return
this
;
}
@Override
protected
void
endCollectionItem
(
Target
target
,
int
index
,
int
total
)
{
if
(
index
+
1
==
total
)
{
output
.
append
(
')'
);
}
}
@Override
protected
void
endCollection
(
int
type
)
{
if
(
type
!=
GEOMETRY_COLLECTION
)
{
inMulti
=
false
;
}
}
@Override
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
)
{
output
.
append
(
'('
);
}
else
{
output
.
append
(
", "
);
}
writeDouble
(
x
);
output
.
append
(
' '
);
writeDouble
(
y
);
dimensionZ:
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYZ
)
!=
0
)
{
if
(
SysProperties
.
MIXED_GEOMETRIES
)
{
if
(
Double
.
isNaN
(
z
))
{
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
)
{
throw
new
IllegalArgumentException
();
}
break
dimensionZ
;
}
}
output
.
append
(
' '
);
writeDouble
(
z
);
}
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
)
{
output
.
append
(
' '
);
writeDouble
(
m
);
}
if
(
index
+
1
==
total
)
{
output
.
append
(
')'
);
}
}
private
void
writeDouble
(
double
v
)
{
String
s
=
Double
.
toString
(
GeometryUtils
.
checkFinite
(
v
));
if
(
s
.
endsWith
(
".0"
))
{
output
.
append
(
s
,
0
,
s
.
length
()
-
2
);
}
else
{
int
idx
=
s
.
indexOf
(
".0E"
);
if
(
idx
<
0
)
{
output
.
append
(
s
);
}
else
{
output
.
append
(
s
,
0
,
idx
).
append
(
s
,
idx
+
2
,
s
.
length
());
}
}
}
}
/**
* Helper source object for EWKT reading.
*/
private
static
final
class
EWKTSource
{
private
final
String
ewkt
;
private
int
offset
;
EWKTSource
(
String
ewkt
)
{
this
.
ewkt
=
ewkt
;
}
int
readSRID
()
{
int
srid
;
if
(
ewkt
.
startsWith
(
"SRID="
))
{
int
idx
=
ewkt
.
indexOf
(
';'
,
5
);
srid
=
Integer
.
parseInt
(
ewkt
.
substring
(
5
,
idx
));
offset
=
idx
+
1
;
}
else
{
srid
=
0
;
}
return
srid
;
}
void
read
(
char
symbol
)
{
skipWS
();
int
len
=
ewkt
.
length
();
if
(
offset
>=
len
)
{
throw
new
IllegalArgumentException
();
}
if
(
ewkt
.
charAt
(
offset
)
!=
symbol
)
{
throw
new
IllegalArgumentException
();
}
offset
++;
}
int
readType
()
{
skipWS
();
int
len
=
ewkt
.
length
();
if
(
offset
>=
len
)
{
throw
new
IllegalArgumentException
();
}
int
result
=
0
;
char
ch
=
ewkt
.
charAt
(
offset
);
switch
(
ch
)
{
case
'P'
:
case
'p'
:
result
=
match
(
"POINT"
,
POINT
);
if
(
result
==
0
)
{
result
=
match
(
"POLYGON"
,
POLYGON
);
}
break
;
case
'L'
:
case
'l'
:
result
=
match
(
"LINESTRING"
,
LINE_STRING
);
break
;
case
'M'
:
case
'm'
:
if
(
match
(
"MULTI"
,
1
)
!=
0
)
{
result
=
match
(
"POINT"
,
MULTI_POINT
);
if
(
result
==
0
)
{
result
=
match
(
"POLYGON"
,
MULTI_POLYGON
);
if
(
result
==
0
)
{
result
=
match
(
"LINESTRING"
,
MULTI_LINE_STRING
);
}
}
}
break
;
case
'G'
:
case
'g'
:
result
=
match
(
"GEOMETRYCOLLECTION"
,
GEOMETRY_COLLECTION
);
break
;
}
if
(
result
==
0
)
{
throw
new
IllegalArgumentException
();
}
return
result
;
}
int
readDimensionSystem
()
{
int
o
=
offset
;
skipWS
();
int
len
=
ewkt
.
length
();
if
(
offset
>
len
-
2
)
{
throw
new
IllegalArgumentException
();
}
int
result
;
char
ch
=
ewkt
.
charAt
(
offset
);
switch
(
ch
)
{
case
'M'
:
case
'm'
:
result
=
DIMENSION_SYSTEM_XYM
;
offset
++;
break
;
case
'Z'
:
case
'z'
:
offset
++;
ch
=
ewkt
.
charAt
(
offset
);
if
(
ch
==
'M'
||
ch
==
'm'
)
{
offset
++;
result
=
DIMENSION_SYSTEM_XYZM
;
}
else
{
result
=
DIMENSION_SYSTEM_XYZ
;
}
break
;
default
:
result
=
DIMENSION_SYSTEM_XY
;
if
(
o
!=
offset
)
{
// Token is already terminated by a whitespace
return
result
;
}
}
checkStringEnd
(
len
);
return
result
;
}
boolean
readEmpty
()
{
skipWS
();
int
len
=
ewkt
.
length
();
if
(
offset
>=
len
)
{
throw
new
IllegalArgumentException
();
}
if
(
ewkt
.
charAt
(
offset
)
==
'('
)
{
offset
++;
return
false
;
}
if
(
match
(
"EMPTY"
,
1
)
!=
0
)
{
checkStringEnd
(
len
);
return
true
;
}
throw
new
IllegalArgumentException
();
}
private
int
match
(
String
token
,
int
code
)
{
int
l
=
token
.
length
();
if
(
offset
<=
ewkt
.
length
()
-
l
&&
ewkt
.
regionMatches
(
true
,
offset
,
token
,
0
,
l
))
{
offset
+=
l
;
}
else
{
code
=
0
;
}
return
code
;
}
private
void
checkStringEnd
(
int
len
)
{
if
(
offset
<
len
)
{
char
ch
=
ewkt
.
charAt
(
offset
);
if
(
ch
>
' '
&&
ch
!=
'('
&&
ch
!=
')'
&&
ch
!=
','
)
{
throw
new
IllegalArgumentException
();
}
}
}
public
boolean
hasCoordinate
()
{
skipWS
();
if
(
offset
>=
ewkt
.
length
())
{
return
false
;
}
return
isNumberStart
(
ewkt
.
charAt
(
offset
));
}
public
double
readCoordinate
()
{
skipWS
();
int
len
=
ewkt
.
length
();
if
(
offset
>=
len
)
{
throw
new
IllegalArgumentException
();
}
char
ch
=
ewkt
.
charAt
(
offset
);
if
(!
isNumberStart
(
ch
))
{
throw
new
IllegalArgumentException
();
}
int
start
=
offset
++;
while
(
offset
<
len
&&
isNumberPart
(
ch
=
ewkt
.
charAt
(
offset
)))
{
offset
++;
}
if
(
offset
<
len
)
{
if
(
ch
>
' '
&&
ch
!=
')'
&&
ch
!=
','
)
{
throw
new
IllegalArgumentException
();
}
}
Double
d
=
Double
.
parseDouble
(
ewkt
.
substring
(
start
,
offset
));
return
d
==
0
?
0
:
d
;
}
private
static
boolean
isNumberStart
(
char
ch
)
{
if
(
ch
>=
'0'
&&
ch
<=
'9'
)
{
return
true
;
}
switch
(
ch
)
{
case
'+'
:
case
'-'
:
case
'.'
:
return
true
;
default
:
return
false
;
}
}
private
static
boolean
isNumberPart
(
char
ch
)
{
if
(
ch
>=
'0'
&&
ch
<=
'9'
)
{
return
true
;
}
switch
(
ch
)
{
case
'+'
:
case
'-'
:
case
'.'
:
case
'E'
:
case
'e'
:
return
true
;
default
:
return
false
;
}
}
public
boolean
hasMoreCoordinates
()
{
skipWS
();
if
(
offset
>=
ewkt
.
length
())
{
throw
new
IllegalArgumentException
();
}
switch
(
ewkt
.
charAt
(
offset
))
{
case
','
:
offset
++;
return
true
;
case
')'
:
offset
++;
return
false
;
default
:
throw
new
IllegalArgumentException
();
}
}
boolean
hasData
()
{
skipWS
();
return
offset
<
ewkt
.
length
();
}
int
getItemCount
()
{
int
result
=
1
;
int
offset
=
this
.
offset
,
level
=
0
,
len
=
ewkt
.
length
();
while
(
offset
<
len
)
{
switch
(
ewkt
.
charAt
(
offset
++))
{
case
','
:
if
(
level
==
0
)
{
result
++;
}
break
;
case
'('
:
level
++;
break
;
case
')'
:
if
(--
level
<
0
)
{
return
result
;
}
}
}
throw
new
IllegalArgumentException
();
}
private
void
skipWS
()
{
for
(
int
len
=
ewkt
.
length
();
offset
<
len
&&
ewkt
.
charAt
(
offset
)
<=
' '
;
offset
++)
{
}
}
@Override
public
String
toString
()
{
return
new
StringBuilder
(
ewkt
.
length
()
+
3
).
append
(
ewkt
,
0
,
offset
).
append
(
"<*>"
)
.
append
(
ewkt
,
offset
,
ewkt
.
length
()).
toString
();
}
}
/**
* Converts EWKB to EWKT.
*
* @param ewkb
* source EWKB
* @return EWKT representation
*/
public
static
String
ewkb2ewkt
(
byte
[]
ewkb
)
{
// Determine dimension system first
DimensionSystemTarget
dimensionTarget
=
new
DimensionSystemTarget
();
EWKBUtils
.
parseEWKB
(
ewkb
,
dimensionTarget
);
// Write an EWKT
return
ewkb2ewkt
(
ewkb
,
dimensionTarget
.
getDimensionSystem
());
}
/**
* Converts EWKB to EWKT.
*
* @param ewkb
* source EWKB
* @param dimension
* dimension system
* @return EWKT representation
*/
public
static
String
ewkb2ewkt
(
byte
[]
ewkb
,
int
dimensionSystem
)
{
StringBuilder
output
=
new
StringBuilder
();
EWKTTarget
target
=
new
EWKTTarget
(
output
,
dimensionSystem
);
EWKBUtils
.
parseEWKB
(
ewkb
,
target
);
return
output
.
toString
();
}
/**
* Converts EWKT to EWKB.
*
* @param ewkt
* source EWKT
* @return EWKB representation
*/
public
static
byte
[]
ewkt2ewkb
(
String
ewkt
)
{
// Determine dimension system first
DimensionSystemTarget
dimensionTarget
=
new
DimensionSystemTarget
();
parseEWKT
(
ewkt
,
dimensionTarget
);
// Write an EWKB
return
ewkt2ewkb
(
ewkt
,
dimensionTarget
.
getDimensionSystem
());
}
/**
* Converts EWKT to EWKB.
*
* @param ewkt
* source EWKT
* @param dimension
* dimension system
* @return EWKB representation
*/
public
static
byte
[]
ewkt2ewkb
(
String
ewkt
,
int
dimensionSystem
)
{
ByteArrayOutputStream
output
=
new
ByteArrayOutputStream
();
EWKBTarget
target
=
new
EWKBTarget
(
output
,
dimensionSystem
);
parseEWKT
(
ewkt
,
target
);
return
output
.
toByteArray
();
}
/**
* Parses a EWKB.
*
* @param source
* source EWKT
* @param target
* output target
*/
public
static
void
parseEWKT
(
String
ewkt
,
Target
target
)
{
parseEWKT
(
new
EWKTSource
(
ewkt
),
target
,
0
,
0
);
}
/**
* Parses a EWKB.
*
* @param source
* EWKT source
* @param target
* output target
* @param parentType
* type of parent geometry collection, or 0 for the root geometry
* @param dimensionSystem
* dimension system of parent geometry
*/
private
static
void
parseEWKT
(
EWKTSource
source
,
Target
target
,
int
parentType
,
int
dimensionSystem
)
{
if
(
parentType
==
0
)
{
target
.
init
(
source
.
readSRID
());
}
int
type
;
switch
(
parentType
)
{
default
:
{
type
=
source
.
readType
();
dimensionSystem
=
source
.
readDimensionSystem
();
break
;
}
case
MULTI_POINT:
type
=
POINT
;
break
;
case
MULTI_LINE_STRING:
type
=
LINE_STRING
;
break
;
case
MULTI_POLYGON:
type
=
POLYGON
;
break
;
}
switch
(
type
)
{
case
POINT:
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_POINT
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
boolean
empty
=
source
.
readEmpty
();
target
.
startPoint
();
if
(
empty
)
{
target
.
addCoordinate
(
Double
.
NaN
,
Double
.
NaN
,
Double
.
NaN
,
Double
.
NaN
,
0
,
1
);
}
else
{
addCoordinate
(
source
,
target
,
dimensionSystem
,
0
,
1
);
source
.
read
(
')'
);
}
break
;
}
case
LINE_STRING:
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_LINE_STRING
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
boolean
empty
=
source
.
readEmpty
();
if
(
empty
)
{
target
.
startLineString
(
0
);
}
else
{
ArrayList
<
double
[]>
coordinates
=
new
ArrayList
<>();
do
{
coordinates
.
add
(
readCoordinate
(
source
,
dimensionSystem
));
}
while
(
source
.
hasMoreCoordinates
());
int
numPoints
=
coordinates
.
size
();
if
(
numPoints
<
0
||
numPoints
==
1
)
{
throw
new
IllegalArgumentException
();
}
target
.
startLineString
(
numPoints
);
for
(
int
i
=
0
;
i
<
numPoints
;
i
++)
{
double
[]
c
=
coordinates
.
get
(
i
);
target
.
addCoordinate
(
c
[
X
],
c
[
Y
],
c
[
Z
],
c
[
M
],
i
,
numPoints
);
}
}
break
;
}
case
POLYGON:
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_POLYGON
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
boolean
empty
=
source
.
readEmpty
();
if
(
empty
)
{
target
.
startPolygon
(
0
,
0
);
}
else
{
ArrayList
<
double
[]>
outer
=
readRing
(
source
,
dimensionSystem
);
ArrayList
<
ArrayList
<
double
[]>>
inner
=
new
ArrayList
<>();
while
(
source
.
hasMoreCoordinates
())
{
inner
.
add
(
readRing
(
source
,
dimensionSystem
));
}
int
numInner
=
inner
.
size
();
int
size
=
outer
.
size
();
// Size may be 0 (EMPTY) or 4+
if
(
size
>=
1
&&
size
<=
3
)
{
throw
new
IllegalArgumentException
();
}
if
(
size
==
0
&&
numInner
>
0
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPolygon
(
numInner
,
size
);
if
(
size
>
0
)
{
addRing
(
outer
,
target
);
for
(
int
i
=
0
;
i
<
numInner
;
i
++)
{
ArrayList
<
double
[]>
ring
=
inner
.
get
(
i
);
size
=
ring
.
size
();
// Size may be 0 (EMPTY) or 4+
if
(
size
>=
1
&&
size
<=
3
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPolygonInner
(
size
);
addRing
(
ring
,
target
);
}
target
.
endNonEmptyPolygon
();
}
}
break
;
}
case
MULTI_POINT:
parseCollection
(
source
,
target
,
MULTI_POINT
,
parentType
,
dimensionSystem
);
break
;
case
MULTI_LINE_STRING:
parseCollection
(
source
,
target
,
MULTI_LINE_STRING
,
parentType
,
dimensionSystem
);
break
;
case
MULTI_POLYGON:
parseCollection
(
source
,
target
,
MULTI_POLYGON
,
parentType
,
dimensionSystem
);
break
;
case
GEOMETRY_COLLECTION:
parseCollection
(
source
,
target
,
GEOMETRY_COLLECTION
,
parentType
,
0
);
break
;
default
:
throw
new
IllegalArgumentException
();
}
if
(
parentType
==
0
&&
source
.
hasData
())
{
throw
new
IllegalArgumentException
();
}
}
private
static
void
parseCollection
(
EWKTSource
source
,
Target
target
,
int
type
,
int
parentType
,
int
dimensionSystem
)
{
if
(
parentType
!=
0
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
if
(
source
.
readEmpty
())
{
target
.
startCollection
(
type
,
0
);
}
else
{
if
(
type
==
MULTI_POINT
&&
source
.
hasCoordinate
())
{
parseMultiPointAlternative
(
source
,
target
,
dimensionSystem
);
}
else
{
int
numItems
=
source
.
getItemCount
();
target
.
startCollection
(
type
,
numItems
);
for
(
int
i
=
0
;
i
<
numItems
;
i
++)
{
if
(
i
>
0
)
{
source
.
read
(
','
);
}
Target
innerTarget
=
target
.
startCollectionItem
(
i
,
numItems
);
parseEWKT
(
source
,
innerTarget
,
type
,
dimensionSystem
);
target
.
endCollectionItem
(
innerTarget
,
i
,
numItems
);
}
source
.
read
(
')'
);
}
}
target
.
endCollection
(
type
);
}
private
static
void
parseMultiPointAlternative
(
EWKTSource
source
,
Target
target
,
int
dimensionSystem
)
{
// Special case for MULTIPOINT (1 2, 3 4)
ArrayList
<
double
[]>
points
=
new
ArrayList
<>();
do
{
points
.
add
(
readCoordinate
(
source
,
dimensionSystem
));
}
while
(
source
.
hasMoreCoordinates
());
int
numItems
=
points
.
size
();
target
.
startCollection
(
MULTI_POINT
,
numItems
);
for
(
int
i
=
0
;
i
<
points
.
size
();
i
++)
{
Target
innerTarget
=
target
.
startCollectionItem
(
i
,
numItems
);
target
.
startPoint
();
double
[]
c
=
points
.
get
(
i
);
target
.
addCoordinate
(
c
[
X
],
c
[
Y
],
c
[
Z
],
c
[
M
],
0
,
1
);
target
.
endCollectionItem
(
innerTarget
,
i
,
numItems
);
}
}
private
static
ArrayList
<
double
[]>
readRing
(
EWKTSource
source
,
int
dimensionSystem
)
{
if
(
source
.
readEmpty
())
{
return
new
ArrayList
<>(
0
);
}
ArrayList
<
double
[]>
result
=
new
ArrayList
<>();
double
[]
c
=
readCoordinate
(
source
,
dimensionSystem
);
double
startX
=
c
[
X
],
startY
=
c
[
Y
];
result
.
add
(
c
);
while
(
source
.
hasMoreCoordinates
())
{
result
.
add
(
readCoordinate
(
source
,
dimensionSystem
));
}
int
size
=
result
.
size
();
if
(
size
<
4
)
{
throw
new
IllegalArgumentException
();
}
c
=
result
.
get
(
size
-
1
);
double
endX
=
c
[
X
],
endY
=
c
[
Y
];
/*
* TODO OGC 06-103r4 determines points as equal if they have the same X
* and Y coordinates. Should we check Z and M here too?
*/
if
(
startX
!=
endX
||
startY
!=
endY
)
{
throw
new
IllegalArgumentException
();
}
return
result
;
}
private
static
void
addRing
(
ArrayList
<
double
[]>
ring
,
Target
target
)
{
for
(
int
i
=
0
,
size
=
ring
.
size
();
i
<
size
;
i
++)
{
double
[]
coordinates
=
ring
.
get
(
i
);
target
.
addCoordinate
(
coordinates
[
X
],
coordinates
[
Y
],
coordinates
[
Z
],
coordinates
[
M
],
i
,
size
);
}
}
private
static
void
addCoordinate
(
EWKTSource
source
,
Target
target
,
int
dimensionSystem
,
int
index
,
int
total
)
{
double
x
=
source
.
readCoordinate
();
double
y
=
source
.
readCoordinate
();
double
z
=
Double
.
NaN
,
m
=
Double
.
NaN
;
if
(
source
.
hasCoordinate
())
{
if
(
dimensionSystem
==
DIMENSION_SYSTEM_XYM
)
{
m
=
source
.
readCoordinate
();
}
else
{
z
=
source
.
readCoordinate
();
if
(
source
.
hasCoordinate
())
{
m
=
source
.
readCoordinate
();
}
}
}
target
.
addCoordinate
(
x
,
y
,
z
,
m
,
index
,
total
);
}
private
static
double
[]
readCoordinate
(
EWKTSource
source
,
int
dimensionSystem
)
{
double
x
=
source
.
readCoordinate
();
double
y
=
source
.
readCoordinate
();
double
z
=
Double
.
NaN
,
m
=
Double
.
NaN
;
if
(
source
.
hasCoordinate
())
{
if
(
dimensionSystem
==
DIMENSION_SYSTEM_XYM
)
{
m
=
source
.
readCoordinate
();
}
else
{
z
=
source
.
readCoordinate
();
if
(
source
.
hasCoordinate
())
{
m
=
source
.
readCoordinate
();
}
}
}
return
new
double
[]
{
x
,
y
,
z
,
m
};
}
private
EWKTUtils
()
{
}
}
h2/src/main/org/h2/util/geometry/GeometryUtils.java
0 → 100644
浏览文件 @
ba63ffc6
/*
* 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
.
util
.
geometry
;
/**
* Utilities for GEOMETRY data type.
*/
public
final
class
GeometryUtils
{
/**
* Converter output target.
*/
public
static
abstract
class
Target
{
public
Target
()
{
}
/**
* Initializes top-level target.
*
* @param srid
* SRID
*/
protected
void
init
(
int
srid
)
{
}
/**
* Invoked before writing a POINT.
*/
protected
void
startPoint
()
{
}
/**
* Invoked before writing a LINESTRING.
*
* @param numPoints
* number of points in line string
*/
protected
void
startLineString
(
int
numPoints
)
{
}
/**
* Invoked before writing a POLYGON.
*
* @param numInner
* number of inner polygons
* @param numPoints
* number of points in outer polygon
*/
protected
void
startPolygon
(
int
numInner
,
int
numPoints
)
{
}
/**
* Invoked before writing an inner polygon in POLYGON.
*
* @param numInner
* number of points in inner polygon
*/
protected
void
startPolygonInner
(
int
numInner
)
{
}
/**
* Invoked after writing of non-empty POLYGON.
*/
protected
void
endNonEmptyPolygon
()
{
}
/**
* Invoked before writing of a collection.
*
* @param type
* type of collection, one of
* {@link GeometryUtils#MULTI_POINT},
* {@link GeometryUtils#MULTI_LINE_STRING},
* {@link GeometryUtils#MULTI_POLYGON},
* {@link GeometryUtils#GEOMETRY_COLLECTION}
* @param numItems
* number of items in this collection
*/
protected
void
startCollection
(
int
type
,
int
numItems
)
{
}
/**
* Invoked before writing of a collection item.
*
* @param index
* 0-based index of this item in the collection
* @param total
* total number of items in the collection
* @return output target that should be used for processing of this
* collection item. May return this target or an custom
* sub-target.
*/
protected
Target
startCollectionItem
(
int
index
,
int
total
)
{
return
this
;
}
/**
* Invoked after writing of a collection item. This method is invoked on
* the same target that was used for
* {@link #startCollectionItem(int, int)}.
*
* @param target
* the result of {@link #startCollectionItem(int, int)}
* @param index
* 0-based index of this item in the collection
* @param total
* total number of items in the collection
*/
protected
void
endCollectionItem
(
Target
target
,
int
index
,
int
total
)
{
}
/**
* Invoked after writing of a collection.
*
* @param type
* type of collection, see {@link #startCollection(int, int)}
*/
protected
void
endCollection
(
int
type
)
{
}
/**
* Invoked to add a coordinate to a geometry.
*
* @param x
* X coordinate
* @param y
* Y coordinate
* @param z
* Z coordinate (NaN if not used)
* @param m
* M coordinate (NaN if not used)
* @param index
* 0-based index of coordinate in the current sequence
* @param total
* total number of coordinates in the current sequence
*/
protected
abstract
void
addCoordinate
(
double
x
,
double
y
,
double
z
,
double
m
,
int
index
,
int
total
);
}
/**
* Converter output target that calculates an envelope.
*/
public
static
final
class
EnvelopeTarget
extends
Target
{
/**
* Enables or disables the envelope calculation. Inner rings of polygons
* are not counted.
*/
private
boolean
enabled
;
/**
* Whether envelope was set.
*/
private
boolean
set
;
double
minX
,
maxX
,
minY
,
maxY
;
/**
* Creates a new envelope calculation target.
*/
public
EnvelopeTarget
()
{
}
@Override
protected
void
startPoint
()
{
enabled
=
true
;
}
@Override
protected
void
startLineString
(
int
numPoints
)
{
enabled
=
true
;
}
@Override
protected
void
startPolygon
(
int
numInner
,
int
numPoints
)
{
enabled
=
true
;
}
@Override
protected
void
startPolygonInner
(
int
numInner
)
{
enabled
=
false
;
}
@Override
protected
void
addCoordinate
(
double
x
,
double
y
,
double
z
,
double
m
,
int
index
,
int
total
)
{
// POINT EMPTY has NaNs
if
(
enabled
&&
!
Double
.
isNaN
(
x
)
&&
!
Double
.
isNaN
(
y
))
{
if
(!
set
)
{
minX
=
maxX
=
x
;
minY
=
maxY
=
y
;
set
=
true
;
}
else
{
if
(
minX
>
x
)
{
minX
=
x
;
}
if
(
maxX
<
x
)
{
maxX
=
x
;
}
if
(
minY
>
y
)
{
minY
=
y
;
}
if
(
maxY
<
y
)
{
maxY
=
y
;
}
}
}
}
/**
* Returns the envelope.
*
* @return the envelope, or null
*/
public
double
[]
getEnvelope
()
{
return
set
?
new
double
[]
{
minX
,
maxX
,
minY
,
maxY
}
:
null
;
}
}
/**
* Converter output target that determines minimal dimension system for a
* geometry.
*/
public
static
final
class
DimensionSystemTarget
extends
Target
{
private
boolean
hasZ
;
private
boolean
hasM
;
/**
* Creates a new dimension system determination target.
*/
public
DimensionSystemTarget
()
{
}
@Override
protected
void
addCoordinate
(
double
x
,
double
y
,
double
z
,
double
m
,
int
index
,
int
total
)
{
if
(!
hasZ
&&
!
Double
.
isNaN
(
z
))
{
hasZ
=
true
;
}
if
(!
hasM
&&
!
Double
.
isNaN
(
m
))
{
hasM
=
true
;
}
}
/**
* Returns the minimal dimension system.
*
* @return the minimal dimension system
*/
public
int
getDimensionSystem
()
{
return
(
hasZ
?
DIMENSION_SYSTEM_XYZ
:
0
)
|
(
hasM
?
DIMENSION_SYSTEM_XYM
:
0
);
}
}
/**
* Converter output target that calculates an envelope and determines the
* minimal dimension system.
*/
public
static
final
class
EnvelopeAndDimensionSystemTarget
extends
Target
{
/**
* Enables or disables the envelope calculation. Inner rings of polygons
* are not counted.
*/
private
boolean
enabled
;
/**
* Whether envelope was set.
*/
private
boolean
set
;
double
minX
,
maxX
,
minY
,
maxY
;
private
boolean
hasZ
;
private
boolean
hasM
;
/**
* Creates a new envelope and dimension system calculation target.
*/
public
EnvelopeAndDimensionSystemTarget
()
{
}
@Override
protected
void
startPoint
()
{
enabled
=
true
;
}
@Override
protected
void
startLineString
(
int
numPoints
)
{
enabled
=
true
;
}
@Override
protected
void
startPolygon
(
int
numInner
,
int
numPoints
)
{
enabled
=
true
;
}
@Override
protected
void
startPolygonInner
(
int
numInner
)
{
enabled
=
false
;
}
@Override
protected
void
addCoordinate
(
double
x
,
double
y
,
double
z
,
double
m
,
int
index
,
int
total
)
{
if
(!
hasZ
&&
!
Double
.
isNaN
(
z
))
{
hasZ
=
true
;
}
if
(!
hasM
&&
!
Double
.
isNaN
(
m
))
{
hasM
=
true
;
}
// POINT EMPTY has NaNs
if
(
enabled
&&
!
Double
.
isNaN
(
x
)
&&
!
Double
.
isNaN
(
y
))
{
if
(!
set
)
{
minX
=
maxX
=
x
;
minY
=
maxY
=
y
;
set
=
true
;
}
else
{
if
(
minX
>
x
)
{
minX
=
x
;
}
if
(
maxX
<
x
)
{
maxX
=
x
;
}
if
(
minY
>
y
)
{
minY
=
y
;
}
if
(
maxY
<
y
)
{
maxY
=
y
;
}
}
}
}
/**
* Returns the envelope.
*
* @return the envelope, or null
*/
public
double
[]
getEnvelope
()
{
return
set
?
new
double
[]
{
minX
,
maxX
,
minY
,
maxY
}
:
null
;
}
/**
* Returns the minimal dimension system.
*
* @return the minimal dimension system
*/
public
int
getDimensionSystem
()
{
return
(
hasZ
?
DIMENSION_SYSTEM_XYZ
:
0
)
|
(
hasM
?
DIMENSION_SYSTEM_XYM
:
0
);
}
}
/**
* POINT geometry type.
*/
static
final
int
POINT
=
1
;
/**
* LINESTRING geometry type.
*/
static
final
int
LINE_STRING
=
2
;
/**
* POLYGON geometry type.
*/
static
final
int
POLYGON
=
3
;
/**
* MULTIPOINT geometry type.
*/
static
final
int
MULTI_POINT
=
4
;
/**
* MULTILINESTRING geometry type.
*/
static
final
int
MULTI_LINE_STRING
=
5
;
/**
* MULTIPOLYGON geometry type.
*/
static
final
int
MULTI_POLYGON
=
6
;
/**
* GEOMETRYCOLLECTION geometry type.
*/
static
final
int
GEOMETRY_COLLECTION
=
7
;
/**
* Number of X coordinate.
*/
public
static
final
int
X
=
0
;
/**
* Number of Y coordinate.
*/
public
static
final
int
Y
=
1
;
/**
* Number of Z coordinate.
*/
public
static
final
int
Z
=
2
;
/**
* Number of M coordinate.
*/
public
static
final
int
M
=
3
;
/**
* Code of 2D (XY) dimension system.
*/
public
static
final
int
DIMENSION_SYSTEM_XY
=
0
;
/**
* Code of Z (XYZ) dimension system. Can also be used in bit masks to
* determine presence of dimension Z.
*/
public
static
final
int
DIMENSION_SYSTEM_XYZ
=
1
;
/**
* Code of M (XYM) dimension system. Can also be used in bit masks to
* determine presence of dimension M.
*/
public
static
final
int
DIMENSION_SYSTEM_XYM
=
2
;
/**
* Code of ZM (XYZM) dimension system. Can be also combined from
* {@link #DIMENSION_SYSTEM_XYZ} and {@link #DIMENSION_SYSTEM_XYM} using
* bitwise OR.
*/
public
static
final
int
DIMENSION_SYSTEM_XYZM
=
3
;
/**
* Minimum X coordinate index.
*/
public
static
final
int
MIN_X
=
0
;
/**
* Maximum X coordinate index.
*/
public
static
final
int
MAX_X
=
1
;
/**
* Minimum Y coordinate index.
*/
public
static
final
int
MIN_Y
=
2
;
/**
* Maximum Y coordinate index.
*/
public
static
final
int
MAX_Y
=
3
;
/**
* Calculates an envelope of a specified geometry.
*
* @param ewkb
* EWKB of a geometry
* @return envelope, or null
*/
public
static
double
[]
getEnvelope
(
byte
[]
ewkb
)
{
EnvelopeTarget
target
=
new
EnvelopeTarget
();
EWKBUtils
.
parseEWKB
(
ewkb
,
target
);
return
target
.
getEnvelope
();
}
/**
* Checks whether two envelopes intersect with each other.
*
* @param envelope1
* first envelope, or null
* @param envelope2
* second envelope, or null
* @return whether the specified envelopes intersects
*/
public
static
boolean
intersects
(
double
[]
envelope1
,
double
[]
envelope2
)
{
return
envelope1
!=
null
&&
envelope2
!=
null
//
&&
envelope1
[
MAX_X
]
>=
envelope2
[
MIN_X
]
//
&&
envelope1
[
MIN_X
]
<=
envelope2
[
MAX_X
]
//
&&
envelope1
[
MAX_Y
]
>=
envelope2
[
MIN_Y
]
//
&&
envelope1
[
MIN_Y
]
<=
envelope2
[
MAX_Y
];
}
/**
* Returns union of two envelopes. This method does not modify the specified
* envelopes, but may return one of them as a result.
*
* @param envelope1
* first envelope, or null
* @param envelope2
* second envelope, or null
* @return union of two envelopes
*/
public
static
double
[]
union
(
double
[]
envelope1
,
double
[]
envelope2
)
{
if
(
envelope1
==
null
)
{
return
envelope2
;
}
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
modified
?
new
double
[]
{
minX1
,
maxX1
,
minY1
,
maxY1
}
:
envelope1
;
}
/**
* Normalizes all NaNs into single type on NaN and negative zero to positive
* zero.
*
* @param d
* double value
* @return normalized value
*/
static
double
toCanonicalDouble
(
double
d
)
{
return
Double
.
isNaN
(
d
)
?
Double
.
NaN
:
d
==
0
d
?
0
d
:
d
;
}
static
double
checkFinite
(
double
d
)
{
// Do not push this negation down, it will break NaN rejection
if
(!(
Math
.
abs
(
d
)
<=
Double
.
MAX_VALUE
))
{
throw
new
IllegalArgumentException
();
}
return
d
;
}
private
GeometryUtils
()
{
}
}
h2/src/main/org/h2/util/geometry/JTSUtils.java
0 → 100644
浏览文件 @
ba63ffc6
/*
* 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
.
util
.
geometry
;
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
.
GEOMETRY_COLLECTION
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
LINE_STRING
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
M
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MULTI_LINE_STRING
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MULTI_POINT
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MULTI_POLYGON
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
POINT
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
POLYGON
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
X
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
Y
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
Z
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
checkFinite
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
toCanonicalDouble
;
import
java.io.ByteArrayOutputStream
;
import
java.lang.reflect.Method
;
import
org.h2.engine.SysProperties
;
import
org.h2.message.DbException
;
import
org.h2.util.geometry.EWKBUtils.EWKBTarget
;
import
org.h2.util.geometry.GeometryUtils.DimensionSystemTarget
;
import
org.h2.util.geometry.GeometryUtils.Target
;
import
org.locationtech.jts.geom.CoordinateSequence
;
import
org.locationtech.jts.geom.CoordinateSequenceFactory
;
import
org.locationtech.jts.geom.Geometry
;
import
org.locationtech.jts.geom.GeometryCollection
;
import
org.locationtech.jts.geom.GeometryFactory
;
import
org.locationtech.jts.geom.LineString
;
import
org.locationtech.jts.geom.LinearRing
;
import
org.locationtech.jts.geom.MultiLineString
;
import
org.locationtech.jts.geom.MultiPoint
;
import
org.locationtech.jts.geom.MultiPolygon
;
import
org.locationtech.jts.geom.Point
;
import
org.locationtech.jts.geom.Polygon
;
import
org.locationtech.jts.geom.PrecisionModel
;
import
org.locationtech.jts.geom.impl.CoordinateArraySequenceFactory
;
import
org.locationtech.jts.geom.impl.PackedCoordinateSequenceFactory
;
/**
* Utilities for Geometry data type from JTS library.
*/
public
final
class
JTSUtils
{
/**
* {@code true} if M dimension is supported by used version of JTS,
* {@code false} if M dimension is only partially supported (JTS 1.15).
*/
public
static
final
boolean
M_IS_SUPPORTED
;
static
final
Method
CREATE
;
private
static
final
Method
GET_MEASURES
;
static
{
Method
create
,
getMeasures
;
try
{
create
=
CoordinateSequenceFactory
.
class
.
getMethod
(
"create"
,
int
.
class
,
int
.
class
,
int
.
class
);
getMeasures
=
CoordinateSequence
.
class
.
getMethod
(
"getMeasures"
);
}
catch
(
ReflectiveOperationException
e
)
{
create
=
null
;
getMeasures
=
null
;
}
M_IS_SUPPORTED
=
create
!=
null
;
CREATE
=
create
;
GET_MEASURES
=
getMeasures
;
}
/**
* Converter output target that creates a JTS Geometry.
*/
public
static
final
class
GeometryTarget
extends
Target
{
private
final
int
dimensionSystem
;
private
GeometryFactory
factory
;
private
int
type
;
private
CoordinateSequence
coordinates
;
private
CoordinateSequence
[]
innerCoordinates
;
private
int
innerOffset
;
private
Geometry
[]
subgeometries
;
/**
* Creates a new instance of JTS Geometry target.
*
* @param dimensionSystem
* dimension system to use
*/
public
GeometryTarget
(
int
dimensionSystem
)
{
this
.
dimensionSystem
=
dimensionSystem
;
}
private
GeometryTarget
(
int
dimensionSystem
,
GeometryFactory
factory
)
{
this
.
dimensionSystem
=
dimensionSystem
;
this
.
factory
=
factory
;
}
@Override
protected
void
init
(
int
srid
)
{
factory
=
new
GeometryFactory
(
new
PrecisionModel
(),
srid
,
(
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
?
PackedCoordinateSequenceFactory
.
DOUBLE_FACTORY
:
CoordinateArraySequenceFactory
.
instance
());
}
@Override
protected
void
startPoint
()
{
type
=
POINT
;
initCoordinates
(
1
);
innerOffset
=
-
1
;
}
@Override
protected
void
startLineString
(
int
numPoints
)
{
type
=
LINE_STRING
;
initCoordinates
(
numPoints
);
innerOffset
=
-
1
;
}
@Override
protected
void
startPolygon
(
int
numInner
,
int
numPoints
)
{
type
=
POLYGON
;
initCoordinates
(
numPoints
);
innerCoordinates
=
new
CoordinateSequence
[
numInner
];
innerOffset
=
-
1
;
}
@Override
protected
void
startPolygonInner
(
int
numInner
)
{
innerCoordinates
[++
innerOffset
]
=
createCoordinates
(
numInner
);
}
@Override
protected
void
startCollection
(
int
type
,
int
numItems
)
{
this
.
type
=
type
;
switch
(
type
)
{
case
MULTI_POINT:
subgeometries
=
new
Point
[
numItems
];
break
;
case
MULTI_LINE_STRING:
subgeometries
=
new
LineString
[
numItems
];
break
;
case
MULTI_POLYGON:
subgeometries
=
new
Polygon
[
numItems
];
break
;
case
GEOMETRY_COLLECTION:
subgeometries
=
new
Geometry
[
numItems
];
break
;
default
:
throw
new
IllegalArgumentException
();
}
}
@Override
protected
Target
startCollectionItem
(
int
index
,
int
total
)
{
return
new
GeometryTarget
(
dimensionSystem
,
factory
);
}
@Override
protected
void
endCollectionItem
(
Target
target
,
int
index
,
int
total
)
{
subgeometries
[
index
]
=
((
GeometryTarget
)
target
).
getGeometry
();
}
private
void
initCoordinates
(
int
numPoints
)
{
coordinates
=
createCoordinates
(
numPoints
);
}
private
CoordinateSequence
createCoordinates
(
int
numPoints
)
{
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
)
{
if
(
M_IS_SUPPORTED
)
{
try
{
return
(
CoordinateSequence
)
CREATE
.
invoke
(
factory
.
getCoordinateSequenceFactory
(),
numPoints
,
4
,
1
);
}
catch
(
ReflectiveOperationException
e
)
{
throw
DbException
.
convert
(
e
);
}
}
return
factory
.
getCoordinateSequenceFactory
().
create
(
numPoints
,
4
);
}
else
{
return
factory
.
getCoordinateSequenceFactory
().
create
(
numPoints
,
3
);
}
}
@Override
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
];
coordinates
.
setOrdinate
(
index
,
X
,
checkFinite
(
x
));
coordinates
.
setOrdinate
(
index
,
Y
,
checkFinite
(
y
));
coordinates
.
setOrdinate
(
index
,
Z
,
(
dimensionSystem
&
DIMENSION_SYSTEM_XYZ
)
!=
0
?
SysProperties
.
MIXED_GEOMETRIES
?
z
:
checkFinite
(
z
)
:
Double
.
NaN
);
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
)
{
coordinates
.
setOrdinate
(
index
,
M
,
checkFinite
(
m
));
}
}
Geometry
getGeometry
()
{
switch
(
type
)
{
case
POINT:
return
new
Point
(
coordinates
,
factory
);
case
LINE_STRING:
return
new
LineString
(
coordinates
,
factory
);
case
POLYGON:
{
LinearRing
shell
=
new
LinearRing
(
coordinates
,
factory
);
int
innerCount
=
innerCoordinates
.
length
;
LinearRing
[]
holes
=
new
LinearRing
[
innerCount
];
for
(
int
i
=
0
;
i
<
innerCount
;
i
++)
{
holes
[
i
]
=
new
LinearRing
(
innerCoordinates
[
i
],
factory
);
}
return
new
Polygon
(
shell
,
holes
,
factory
);
}
case
MULTI_POINT:
return
new
MultiPoint
((
Point
[])
subgeometries
,
factory
);
case
MULTI_LINE_STRING:
return
new
MultiLineString
((
LineString
[])
subgeometries
,
factory
);
case
MULTI_POLYGON:
return
new
MultiPolygon
((
Polygon
[])
subgeometries
,
factory
);
case
GEOMETRY_COLLECTION:
return
new
GeometryCollection
(
subgeometries
,
factory
);
default
:
throw
new
IllegalStateException
();
}
}
}
/**
* Converts EWKB to a JTS geometry object.
*
* @param ewkb
* source EWKB
* @return JTS geometry object
*/
public
static
Geometry
ewkb2geometry
(
byte
[]
ewkb
)
{
// Determine dimension system first
DimensionSystemTarget
dimensionTarget
=
new
DimensionSystemTarget
();
EWKBUtils
.
parseEWKB
(
ewkb
,
dimensionTarget
);
// Generate a Geometry
return
ewkb2geometry
(
ewkb
,
dimensionTarget
.
getDimensionSystem
());
}
/**
* Converts EWKB to a JTS geometry object.
*
* @param ewkb
* source EWKB
* @param dimensionSystem
* dimension system
* @return JTS geometry object
*/
public
static
Geometry
ewkb2geometry
(
byte
[]
ewkb
,
int
dimensionSystem
)
{
GeometryTarget
target
=
new
GeometryTarget
(
dimensionSystem
);
EWKBUtils
.
parseEWKB
(
ewkb
,
target
);
return
target
.
getGeometry
();
}
/**
* Converts Geometry to EWKB.
*
* @param geometry
* source geometry
* @return EWKB representation
*/
public
static
byte
[]
geometry2ewkb
(
Geometry
geometry
)
{
// Determine dimension system first
DimensionSystemTarget
dimensionTarget
=
new
DimensionSystemTarget
();
parseGeometry
(
geometry
,
dimensionTarget
);
// Write an EWKB
return
geometry2ewkb
(
geometry
,
dimensionTarget
.
getDimensionSystem
());
}
/**
* Converts Geometry to EWKB.
*
* @param geometry
* source geometry
* @param dimensionSystem
* dimension system
* @return EWKB representation
*/
public
static
byte
[]
geometry2ewkb
(
Geometry
geometry
,
int
dimensionSystem
)
{
// Write an EWKB
ByteArrayOutputStream
output
=
new
ByteArrayOutputStream
();
EWKBTarget
target
=
new
EWKBTarget
(
output
,
dimensionSystem
);
parseGeometry
(
geometry
,
target
);
return
output
.
toByteArray
();
}
/**
* Parses a JTS Geometry object.
*
* @param geometry
* geometry to parse
* @param target
* output target
*/
public
static
void
parseGeometry
(
Geometry
geometry
,
Target
target
)
{
parseGeometry
(
geometry
,
target
,
0
);
}
/**
* Parses a JTS Geometry object.
*
* @param geometry
* geometry to parse
* @param target
* output target
* @param parentType
* type of parent geometry collection, or 0 for the root geometry
*/
private
static
void
parseGeometry
(
Geometry
geometry
,
Target
target
,
int
parentType
)
{
if
(
parentType
==
0
)
{
target
.
init
(
geometry
.
getSRID
());
}
if
(
geometry
instanceof
Point
)
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_POINT
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPoint
();
Point
p
=
(
Point
)
geometry
;
if
(
p
.
isEmpty
())
{
target
.
addCoordinate
(
Double
.
NaN
,
Double
.
NaN
,
Double
.
NaN
,
Double
.
NaN
,
0
,
1
);
}
else
{
CoordinateSequence
sequence
=
p
.
getCoordinateSequence
();
addCoordinate
(
sequence
,
target
,
0
,
1
,
getMeasures
(
sequence
));
}
}
else
if
(
geometry
instanceof
LineString
)
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_LINE_STRING
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
LineString
ls
=
(
LineString
)
geometry
;
CoordinateSequence
cs
=
ls
.
getCoordinateSequence
();
int
numPoints
=
cs
.
size
();
if
(
numPoints
<
0
||
numPoints
==
1
)
{
throw
new
IllegalArgumentException
();
}
target
.
startLineString
(
numPoints
);
int
measures
=
getMeasures
(
cs
);
for
(
int
i
=
0
;
i
<
numPoints
;
i
++)
{
addCoordinate
(
cs
,
target
,
i
,
numPoints
,
measures
);
}
}
else
if
(
geometry
instanceof
Polygon
)
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_POLYGON
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
Polygon
p
=
(
Polygon
)
geometry
;
int
numInner
=
p
.
getNumInteriorRing
();
if
(
numInner
<
0
)
{
throw
new
IllegalArgumentException
();
}
CoordinateSequence
cs
=
p
.
getExteriorRing
().
getCoordinateSequence
();
int
size
=
cs
.
size
();
// Size may be 0 (EMPTY) or 4+
if
(
size
<
0
||
size
>=
1
&&
size
<=
3
)
{
throw
new
IllegalArgumentException
();
}
if
(
size
==
0
&&
numInner
>
0
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPolygon
(
numInner
,
size
);
if
(
size
>
0
)
{
int
measures
=
getMeasures
(
cs
);
addRing
(
cs
,
target
,
size
,
measures
);
for
(
int
i
=
0
;
i
<
numInner
;
i
++)
{
cs
=
p
.
getInteriorRingN
(
i
).
getCoordinateSequence
();
size
=
cs
.
size
();
// Size may be 0 (EMPTY) or 4+
if
(
size
<
0
||
size
>=
1
&&
size
<=
3
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPolygonInner
(
size
);
addRing
(
cs
,
target
,
size
,
measures
);
}
target
.
endNonEmptyPolygon
();
}
}
else
if
(
geometry
instanceof
GeometryCollection
)
{
if
(
parentType
!=
0
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
GeometryCollection
gc
=
(
GeometryCollection
)
geometry
;
int
type
;
if
(
gc
instanceof
MultiPoint
)
{
type
=
MULTI_POINT
;
}
else
if
(
gc
instanceof
MultiLineString
)
{
type
=
MULTI_LINE_STRING
;
}
else
if
(
gc
instanceof
MultiPolygon
)
{
type
=
MULTI_POLYGON
;
}
else
{
type
=
GEOMETRY_COLLECTION
;
}
int
numItems
=
gc
.
getNumGeometries
();
if
(
numItems
<
0
)
{
throw
new
IllegalArgumentException
();
}
target
.
startCollection
(
type
,
numItems
);
for
(
int
i
=
0
;
i
<
numItems
;
i
++)
{
Target
innerTarget
=
target
.
startCollectionItem
(
i
,
numItems
);
parseGeometry
(
gc
.
getGeometryN
(
i
),
innerTarget
,
type
);
target
.
endCollectionItem
(
innerTarget
,
i
,
numItems
);
}
target
.
endCollection
(
type
);
}
else
{
throw
new
IllegalArgumentException
();
}
}
private
static
void
addRing
(
CoordinateSequence
sequence
,
Target
target
,
int
size
,
int
measures
)
{
// 0 or 4+ are valid
if
(
size
>=
4
)
{
double
startX
=
toCanonicalDouble
(
sequence
.
getX
(
0
)),
startY
=
toCanonicalDouble
(
sequence
.
getY
(
0
));
addCoordinate
(
sequence
,
target
,
0
,
size
,
startX
,
startY
,
measures
);
for
(
int
i
=
1
;
i
<
size
-
1
;
i
++)
{
addCoordinate
(
sequence
,
target
,
i
,
size
,
measures
);
}
double
endX
=
toCanonicalDouble
(
sequence
.
getX
(
size
-
1
)),
//
endY
=
toCanonicalDouble
(
sequence
.
getY
(
size
-
1
));
/*
* TODO OGC 06-103r4 determines points as equal if they have the
* same X and Y coordinates. Should we check Z and M here too?
*/
if
(
startX
!=
endX
||
startY
!=
endY
)
{
throw
new
IllegalArgumentException
();
}
addCoordinate
(
sequence
,
target
,
size
-
1
,
size
,
endX
,
endY
,
measures
);
}
}
private
static
void
addCoordinate
(
CoordinateSequence
sequence
,
Target
target
,
int
index
,
int
total
,
int
measures
)
{
addCoordinate
(
sequence
,
target
,
index
,
total
,
toCanonicalDouble
(
sequence
.
getX
(
index
)),
toCanonicalDouble
(
sequence
.
getY
(
index
)),
measures
);
}
private
static
void
addCoordinate
(
CoordinateSequence
sequence
,
Target
target
,
int
index
,
int
total
,
double
x
,
double
y
,
int
measures
)
{
double
m
,
z
;
int
d
=
sequence
.
getDimension
();
if
(
M_IS_SUPPORTED
)
{
d
-=
measures
;
z
=
d
>
2
?
sequence
.
getOrdinate
(
index
,
Z
)
:
Double
.
NaN
;
m
=
measures
>=
1
?
sequence
.
getOrdinate
(
index
,
d
)
:
Double
.
NaN
;
}
else
{
z
=
d
>=
3
?
toCanonicalDouble
(
sequence
.
getOrdinate
(
index
,
Z
))
:
Double
.
NaN
;
m
=
d
>=
4
?
toCanonicalDouble
(
sequence
.
getOrdinate
(
index
,
M
))
:
Double
.
NaN
;
}
target
.
addCoordinate
(
x
,
y
,
z
,
m
,
index
,
total
);
}
private
static
int
getMeasures
(
CoordinateSequence
sequence
)
{
int
m
;
if
(
M_IS_SUPPORTED
)
{
try
{
m
=
(
int
)
GET_MEASURES
.
invoke
(
sequence
);
}
catch
(
ReflectiveOperationException
e
)
{
throw
DbException
.
convert
(
e
);
}
}
else
{
m
=
0
;
}
return
m
;
}
private
JTSUtils
()
{
}
}
h2/src/main/org/h2/util/geometry/package.html
0 → 100644
浏览文件 @
ba63ffc6
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, Version 1.0,
and under the Eclipse Public License, Version 1.0
Initial Developer: H2 Group
-->
<html
xmlns=
"http://www.w3.org/1999/xhtml"
lang=
"en"
xml:lang=
"en"
>
<head><meta
http-equiv=
"Content-Type"
content=
"text/html;charset=utf-8"
/><title>
Javadoc package documentation
</title></head><body
style=
"font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"
><p>
Internal utility classes for GEOMETRY data type.
</p></body></html>
\ No newline at end of file
h2/src/main/org/h2/value/DataType.java
浏览文件 @
ba63ffc6
...
...
@@ -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
:
...
...
h2/src/main/org/h2/value/Value.java
浏览文件 @
ba63ffc6
...
...
@@ -1215,7 +1215,7 @@ public abstract class Value {
private
ValueGeometry
convertToGeometry
()
{
switch
(
getType
())
{
case
BYTES:
return
ValueGeometry
.
get
(
getBytesNoCopy
());
return
ValueGeometry
.
get
FromEWKB
(
getBytesNoCopy
());
case
JAVA_OBJECT:
Object
object
=
JdbcUtils
.
deserialize
(
getBytesNoCopy
(),
getDataHandler
());
if
(
DataType
.
isGeometry
(
object
))
{
...
...
h2/src/main/org/h2/value/ValueGeometry.java
浏览文件 @
ba63ffc6
...
...
@@ -8,22 +8,18 @@ package org.h2.value;
import
java.sql.PreparedStatement
;
import
java.sql.SQLException
;
import
java.util.Arrays
;
import
org.h2.api.ErrorCode
;
import
org.h2.engine.Mode
;
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.EWKBUtils
;
import
org.h2.util.geometry.EWKTUtils
;
import
org.h2.util.geometry.GeometryUtils
;
import
org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget
;
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.
...
...
@@ -47,26 +43,33 @@ public class ValueGeometry extends Value {
private
final
int
hashCode
;
/**
* The value. Converted from WKB only on request as conversion from/to WKB
* cost a significant amount of CPU cycles.
* Dimension system. -1 if not known yet.
*/
private
Geometry
geometry
;
private
int
dimensionSystem
;
/**
* The envelope of the value. Calculated only on request.
*/
private
Envelope
envelope
;
private
double
[]
envelope
;
/**
* The value. Converted from WKB only on request as conversion from/to WKB
* cost a significant amount of CPU cycles.
*/
private
Object
geometry
;
/**
* Create a new geometry object
s
.
* Create a new geometry object.
*
* @param bytes the bytes (always known)
* @param geometry the geometry object (may be null)
* @param bytes the EWKB bytes
* @param dimensionSystem dimension system
* @param envelope the envelope
*/
private
ValueGeometry
(
byte
[]
bytes
,
Geometry
geometry
)
{
private
ValueGeometry
(
byte
[]
bytes
,
int
dimensionSystem
,
double
[]
envelope
)
{
this
.
bytes
=
bytes
;
this
.
geometry
=
geometry
;
this
.
hashCode
=
Arrays
.
hashCode
(
bytes
);
this
.
dimensionSystem
=
dimensionSystem
;
this
.
envelope
=
envelope
;
}
/**
...
...
@@ -77,29 +80,16 @@ 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
;
try
{
EnvelopeAndDimensionSystemTarget
target
=
new
EnvelopeAndDimensionSystemTarget
();
Geometry
g
=
(
Geometry
)
o
;
JTSUtils
.
parseGeometry
(
g
,
target
);
int
dimensionSystem
=
target
.
getDimensionSystem
();
return
(
ValueGeometry
)
Value
.
cache
(
new
ValueGeometry
(
JTSUtils
.
geometry2ewkb
(
g
,
dimensionSystem
),
dimensionSystem
,
target
.
getEnvelope
()));
}
catch
(
RuntimeException
ex
)
{
throw
DbException
.
get
(
ErrorCode
.
DATA_CONVERSION_ERROR_1
,
String
.
valueOf
(
o
));
}
}
/**
...
...
@@ -110,21 +100,13 @@ 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
)
{
throw
DbException
.
convert
(
ex
);
EnvelopeAndDimensionSystemTarget
target
=
new
EnvelopeAndDimensionSystemTarget
();
EWKTUtils
.
parseEWKT
(
s
,
target
);
int
dimensionSystem
=
target
.
getDimensionSystem
();
return
(
ValueGeometry
)
Value
.
cache
(
new
ValueGeometry
(
EWKTUtils
.
ewkt2ewkb
(
s
,
dimensionSystem
),
dimensionSystem
,
target
.
getEnvelope
()));
}
catch
(
RuntimeException
ex
)
{
throw
DbException
.
get
(
ErrorCode
.
DATA_CONVERSION_ERROR_1
,
s
);
}
}
...
...
@@ -137,21 +119,48 @@ public class ValueGeometry extends Value {
*/
public
static
ValueGeometry
get
(
String
s
,
int
srid
)
{
// This method is not used in H2, but preserved for H2GIS
return
get
(
srid
==
0
?
s
:
"SRID="
+
srid
+
';'
+
s
);
}
/**
* Get or create a geometry value for the given internal EWKB representation.
*
* @param bytes the WKB representation of the geometry. May not be modified.
* @return the value
*/
public
static
ValueGeometry
get
(
byte
[]
bytes
)
{
return
(
ValueGeometry
)
Value
.
cache
(
new
ValueGeometry
(
bytes
,
-
1
,
null
));
}
/**
* Get or create a geometry value for the given EWKB value.
*
* @param bytes the WKB representation of the geometry
* @return the value
*/
public
static
ValueGeometry
getFromEWKB
(
byte
[]
bytes
)
{
try
{
return
get
(
new
WKTReader
(
new
GeometryFactory
(
new
PrecisionModel
(),
srid
)).
read
(
s
));
}
catch
(
ParseException
ex
)
{
throw
DbException
.
convert
(
ex
);
EnvelopeAndDimensionSystemTarget
target
=
new
EnvelopeAndDimensionSystemTarget
();
EWKBUtils
.
parseEWKB
(
bytes
,
target
);
int
dimensionSystem
=
target
.
getDimensionSystem
();
return
(
ValueGeometry
)
Value
.
cache
(
new
ValueGeometry
(
EWKBUtils
.
ewkb2ewkb
(
bytes
,
dimensionSystem
),
dimensionSystem
,
target
.
getEnvelope
()));
}
catch
(
RuntimeException
ex
)
{
throw
DbException
.
get
(
ErrorCode
.
DATA_CONVERSION_ERROR_1
,
StringUtils
.
convertBytesToHex
(
bytes
));
}
}
/**
*
Get or create a geometry value for the given geometry
.
*
Creates a geometry value for the given envelope
.
*
* @param
bytes the WKB representation of the geometry
* @param
envelope envelope. May not be modified.
* @return the value
*/
public
static
ValueGeometry
get
(
byte
[]
bytes
)
{
return
(
ValueGeometry
)
Value
.
cache
(
new
ValueGeometry
(
bytes
,
null
));
public
static
Value
fromEnvelope
(
double
[]
envelope
)
{
return
envelope
!=
null
?
Value
.
cache
(
new
ValueGeometry
(
EWKBUtils
.
envelope2wkb
(
envelope
),
GeometryUtils
.
DIMENSION_SYSTEM_XY
,
envelope
))
:
ValueNull
.
INSTANCE
;
}
/**
...
...
@@ -161,53 +170,33 @@ 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
()
{
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
,
getDimensionSystem
());
}
catch
(
RuntimeException
ex
)
{
throw
DbException
.
convert
(
ex
);
}
}
return
geometry
;
return
((
Geometry
)
geometry
).
copy
();
}
private
void
calculateInfo
()
{
if
(
dimensionSystem
<
0
)
{
EnvelopeAndDimensionSystemTarget
target
=
new
EnvelopeAndDimensionSystemTarget
();
EWKBUtils
.
parseEWKB
(
bytes
,
target
);
envelope
=
target
.
getEnvelope
();
dimensionSystem
=
target
.
getDimensionSystem
();
}
}
/**
* Return
the SRID (Spatial Reference Identifier)
.
* Return
a minimal dimension system that can be used for this geometry
.
*
* @return
spatial reference identifier
* @return
dimension system
*/
public
int
getSRID
()
{
if
(
bytes
.
length
>=
9
)
{
boolean
bigEndian
;
switch
(
bytes
[
0
])
{
case
0
:
bigEndian
=
true
;
break
;
case
1
:
bigEndian
=
false
;
break
;
default
:
return
0
;
}
if
((
bytes
[
bigEndian
?
1
:
4
]
&
0x20
)
!=
0
)
{
int
srid
=
Bits
.
readInt
(
bytes
,
5
);
if
(!
bigEndian
)
{
srid
=
Integer
.
reverseBytes
(
srid
);
}
return
srid
;
}
}
return
0
;
public
int
getDimensionSystem
()
{
calculateInfo
();
return
dimensionSystem
;
}
/**
...
...
@@ -215,10 +204,8 @@ public class ValueGeometry extends Value {
*
* @return envelope of this geometry
*/
public
Envelope
getEnvelopeNoCopy
()
{
if
(
envelope
==
null
)
{
envelope
=
getGeometryNoCopy
().
getEnvelopeInternal
();
}
public
double
[]
getEnvelopeNoCopy
()
{
calculateInfo
();
return
envelope
;
}
...
...
@@ -230,7 +217,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 +227,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
fromEnvelope
(
GeometryUtils
.
union
(
getEnvelopeNoCopy
(),
r
.
getEnvelopeNoCopy
()));
}
@Override
...
...
@@ -253,13 +237,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 +263,25 @@ public class ValueGeometry extends Value {
@Override
public
Object
getObject
()
{
return
getGeometry
();
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
...
...
@@ -305,15 +291,12 @@ public class ValueGeometry extends Value {
@Override
public
int
getMemory
()
{
return
getEWKB
()
.
length
*
20
+
24
;
return
bytes
.
length
*
20
+
24
;
}
@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
(
bytes
,
((
ValueGeometry
)
other
).
bytes
);
}
/**
...
...
@@ -322,12 +305,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
,
getDimensionSystem
());
}
/**
...
...
@@ -347,40 +325,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
;
}
}
}
h2/src/test/org/h2/test/TestAll.java
浏览文件 @
ba63ffc6
...
...
@@ -188,6 +188,7 @@ import org.h2.test.unit.TestFileLockProcess;
import
org.h2.test.unit.TestFileLockSerialized
;
import
org.h2.test.unit.TestFileSystem
;
import
org.h2.test.unit.TestFtp
;
import
org.h2.test.unit.TestGeometryUtils
;
import
org.h2.test.unit.TestIntArray
;
import
org.h2.test.unit.TestIntIntHashMap
;
import
org.h2.test.unit.TestIntPerfectHash
;
...
...
@@ -961,6 +962,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest
(
new
TestDbException
());
addTest
(
new
TestFile
());
addTest
(
new
TestFtp
());
addTest
(
new
TestGeometryUtils
());
addTest
(
new
TestInterval
());
addTest
(
new
TestIntArray
());
addTest
(
new
TestIntIntHashMap
());
...
...
h2/src/test/org/h2/test/db/TestSpatial.java
浏览文件 @
ba63ffc6
...
...
@@ -608,16 +608,17 @@ public class TestSpatial extends TestDb {
* Test serialization of Z and SRID values.
*/
private
void
testWKB
()
{
String
ewkt
=
"SRID=27572;POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))"
;
String
ewkt
=
"SRID=27572;POLYGON
Z
((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))"
;
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
=
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
...
...
@@ -697,13 +698,6 @@ public class TestSpatial extends TestDb {
ValueGeometry
valueGeometry3
=
ValueGeometry
.
getFromGeometry
(
geometry
);
assertEquals
(
valueGeometry
,
valueGeometry3
);
assertEquals
(
geometry
.
getSRID
(),
valueGeometry3
.
getGeometry
().
getSRID
());
// Check illegal geometry (no WKB representation)
try
{
ValueGeometry
.
get
(
"POINT EMPTY"
);
fail
(
"expected this to throw IllegalArgumentException"
);
}
catch
(
IllegalArgumentException
ex
)
{
// expected
}
}
/**
...
...
h2/src/test/org/h2/test/unit/TestGeometryUtils.java
0 → 100644
浏览文件 @
ba63ffc6
/*
* 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
.
test
.
unit
;
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_XYZ
;
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_Y
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MIN_X
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
MIN_Y
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
X
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
Y
;
import
static
org
.
h2
.
util
.
geometry
.
GeometryUtils
.
Z
;
import
java.io.ByteArrayOutputStream
;
import
java.lang.ProcessBuilder.Redirect
;
import
java.util.Random
;
import
org.h2.test.TestBase
;
import
org.h2.util.StringUtils
;
import
org.h2.util.geometry.EWKBUtils
;
import
org.h2.util.geometry.EWKBUtils.EWKBTarget
;
import
org.h2.util.geometry.EWKTUtils
;
import
org.h2.util.geometry.EWKTUtils.EWKTTarget
;
import
org.h2.util.geometry.GeometryUtils
;
import
org.h2.util.geometry.GeometryUtils.DimensionSystemTarget
;
import
org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget
;
import
org.h2.util.geometry.GeometryUtils.Target
;
import
org.h2.util.geometry.JTSUtils
;
import
org.h2.util.geometry.JTSUtils.GeometryTarget
;
import
org.locationtech.jts.geom.CoordinateSequence
;
import
org.locationtech.jts.geom.Envelope
;
import
org.locationtech.jts.geom.Geometry
;
import
org.locationtech.jts.geom.GeometryCollection
;
import
org.locationtech.jts.geom.GeometryFactory
;
import
org.locationtech.jts.geom.LineString
;
import
org.locationtech.jts.geom.Point
;
import
org.locationtech.jts.io.WKBWriter
;
import
org.locationtech.jts.io.WKTReader
;
import
org.locationtech.jts.io.WKTWriter
;
/**
* Tests the classes from org.h2.util.geometry package.
*/
public
class
TestGeometryUtils
extends
TestBase
{
private
static
final
byte
[][]
NON_FINITE
=
{
//
// XY
StringUtils
.
convertHexToBytes
(
"0000000001"
//
+
"0000000000000000"
//
+
"7ff8000000000000"
),
//
// XY
StringUtils
.
convertHexToBytes
(
"0000000001"
//
+
"7ff8000000000000"
//
+
"0000000000000000"
),
//
// XYZ
StringUtils
.
convertHexToBytes
(
"0080000001"
//
+
"0000000000000000"
//
+
"0000000000000000"
//
+
"7ff8000000000000"
),
//
// XYM
StringUtils
.
convertHexToBytes
(
"0040000001"
//
+
"0000000000000000"
//
+
"0000000000000000"
//
+
"7ff8000000000000"
)
};
private
static
final
int
[]
NON_FINITE_DIMENSIONS
=
{
//
DIMENSION_SYSTEM_XY
,
//
DIMENSION_SYSTEM_XY
,
//
DIMENSION_SYSTEM_XYZ
,
//
DIMENSION_SYSTEM_XYM
};
private
static
final
String
MIXED_WKT
=
"LINESTRING (1 2, 3 4 5)"
;
private
static
final
String
MIXED_WKT_Z
=
"LINESTRING Z (1 2, 3 4 5)"
;
private
static
final
byte
[]
MIXED_WKB
=
StringUtils
.
convertHexToBytes
(
""
// BOM (BigEndian)
+
"00"
// Z | LINESTRING
+
"80000002"
// 2 items
+
"00000002"
// 1.0
+
"3ff0000000000000"
// 2.0
+
"4000000000000000"
// NaN
+
"7ff8000000000000"
// 3.0
+
"4008000000000000"
// 4.0
+
"4010000000000000"
// 5.0
+
"4014000000000000"
);
/**
* May be used to run only this test and may be launched by this test in a
* subprocess.
*
* @param a
* if empty run this test only
*/
public
static
void
main
(
String
...
a
)
throws
Exception
{
TestGeometryUtils
test
=
(
TestGeometryUtils
)
TestBase
.
createCaller
().
init
();
if
(
a
.
length
==
0
)
{
test
.
test
();
}
else
{
test
.
testMixedGeometriesAcceptImpl
();
}
}
@Override
public
void
test
()
throws
Exception
{
testPoint
();
testLineString
();
testPolygon
();
testMultiPoint
();
testMultiLineString
();
testMultiPolygon
();
testGeometryCollection
();
testEmptyPoint
();
testDimensionM
();
testDimensionZM
();
testFiniteOnly
();
testSRID
();
testIntersectionAndUnion
();
testMixedGeometries
();
testMixedGeometriesAccept
();
}
private
void
testPoint
()
throws
Exception
{
testGeometry
(
"POINT (1 2)"
,
2
);
testGeometry
(
"POINT (-1.3 15)"
,
2
);
testGeometry
(
"POINT (-1E32 1.000001)"
,
"POINT (-1E32 1.000001)"
,
"POINT (-100000000000000000000000000000000 1.000001)"
,
2
);
testGeometry
(
"POINT Z (2.7 -3 34)"
,
3
);
assertEquals
(
"POINT Z (1 2 3)"
,
EWKTUtils
.
ewkb2ewkt
(
EWKTUtils
.
ewkt2ewkb
(
"POINTZ(1 2 3)"
)));
assertEquals
(
"POINT Z (1 2 3)"
,
EWKTUtils
.
ewkb2ewkt
(
EWKTUtils
.
ewkt2ewkb
(
"pointz(1 2 3)"
)));
}
private
void
testLineString
()
throws
Exception
{
testGeometry
(
"LINESTRING (-1 -2, 10 1, 2 20)"
,
2
);
testGeometry
(
"LINESTRING (1 2, 1 3)"
,
2
);
testGeometry
(
"LINESTRING (1 2, 2 2)"
,
2
);
testGeometry
(
"LINESTRING EMPTY"
,
2
);
testGeometry
(
"LINESTRING Z (-1 -2 -3, 10 15.7 3)"
,
3
);
}
private
void
testPolygon
()
throws
Exception
{
testGeometry
(
"POLYGON ((-1 -2, 10 1, 2 20, -1 -2))"
,
2
);
testGeometry
(
"POLYGON EMPTY"
,
2
);
testGeometry
(
"POLYGON ((-1 -2, 10 1, 2 20, -1 -2), (0.5 0.5, 1 0.5, 1 1, 0.5 0.5))"
,
2
);
// TODO is EMPTY inner ring valid?
testGeometry
(
"POLYGON ((-1 -2, 10 1, 2 20, -1 -2), EMPTY)"
,
2
);
testGeometry
(
"POLYGON Z ((-1 -2 7, 10 1 7, 2 20 7, -1 -2 7), (0.5 0.5 7, 1 0.5 7, 1 1 7, 0.5 0.5 7))"
,
3
);
}
private
void
testMultiPoint
()
throws
Exception
{
testGeometry
(
"MULTIPOINT ((1 2), (3 4))"
,
2
);
// Alternative syntax
testGeometry
(
"MULTIPOINT (1 2, 3 4)"
,
"MULTIPOINT ((1 2), (3 4))"
,
"MULTIPOINT ((1 2), (3 4))"
,
2
);
testGeometry
(
"MULTIPOINT (1 2)"
,
"MULTIPOINT ((1 2))"
,
"MULTIPOINT ((1 2))"
,
2
);
testGeometry
(
"MULTIPOINT EMPTY"
,
2
);
testGeometry
(
"MULTIPOINT Z ((1 2 0.5), (3 4 -3))"
,
3
);
}
private
void
testMultiLineString
()
throws
Exception
{
testGeometry
(
"MULTILINESTRING ((1 2, 3 4, 5 7))"
,
2
);
testGeometry
(
"MULTILINESTRING ((1 2, 3 4, 5 7), (-1 -1, 0 0, 2 2, 4 6.01))"
,
2
);
testGeometry
(
"MULTILINESTRING EMPTY"
,
2
);
testGeometry
(
"MULTILINESTRING Z ((1 2 0.5, 3 4 -3, 5 7 10))"
,
3
);
}
private
void
testMultiPolygon
()
throws
Exception
{
testGeometry
(
"MULTIPOLYGON (((-1 -2, 10 1, 2 20, -1 -2)))"
,
2
);
testGeometry
(
"MULTIPOLYGON (((-1 -2, 10 1, 2 20, -1 -2)), ((1 2, 2 2, 3 3, 1 2)))"
,
2
);
testGeometry
(
"MULTIPOLYGON EMPTY"
,
2
);
testGeometry
(
"MULTIPOLYGON (((-1 -2, 10 1, 2 20, -1 -2), (0.5 0.5, 1 0.5, 1 1, 0.5 0.5)))"
,
2
);
testGeometry
(
"MULTIPOLYGON Z (((-1 -2 7, 10 1 7, 2 20 7, -1 -2 7), (0.5 1 7, 1 0.5 7, 1 1 7, 0.5 1 7)))"
,
3
);
}
private
void
testGeometryCollection
()
throws
Exception
{
testGeometry
(
"GEOMETRYCOLLECTION (POINT (1 2))"
,
2
);
testGeometry
(
"GEOMETRYCOLLECTION (POINT (1 2), "
//
+
"MULTILINESTRING ((1 2, 3 4, 5 7), (-1 -1, 0 0, 2 2, 4 6.01)), "
//
+
"POINT (100 130))"
,
2
);
testGeometry
(
"GEOMETRYCOLLECTION EMPTY"
,
2
);
testGeometry
(
"GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (1 3)), MULTIPOINT ((4 8)), GEOMETRYCOLLECTION EMPTY)"
,
2
);
testGeometry
(
"GEOMETRYCOLLECTION Z (POINT Z (1 2 3))"
,
3
);
}
private
void
testGeometry
(
String
wkt
,
int
numOfDimensions
)
throws
Exception
{
testGeometry
(
wkt
,
wkt
,
wkt
,
numOfDimensions
);
}
private
void
testGeometry
(
String
wkt
,
String
h2Wkt
,
String
jtsWkt
,
int
numOfDimensions
)
throws
Exception
{
Geometry
geometryFromJTS
=
new
WKTReader
().
read
(
wkt
);
byte
[]
wkbFromJTS
=
new
WKBWriter
(
numOfDimensions
).
write
(
geometryFromJTS
);
// Test WKB->WKT conversion
assertEquals
(
h2Wkt
,
EWKTUtils
.
ewkb2ewkt
(
wkbFromJTS
));
// Test WKT->WKB conversion
assertEquals
(
wkbFromJTS
,
EWKTUtils
.
ewkt2ewkb
(
wkt
));
// Test WKB->WKB no-op normalization
assertEquals
(
wkbFromJTS
,
EWKBUtils
.
ewkb2ewkb
(
wkbFromJTS
));
// Test WKB->Geometry conversion
Geometry
geometryFromH2
=
JTSUtils
.
ewkb2geometry
(
wkbFromJTS
);
String
got
=
new
WKTWriter
(
numOfDimensions
).
write
(
geometryFromH2
);
if
(!
jtsWkt
.
equals
(
got
))
{
if
(!
jtsWkt
.
replaceAll
(
" Z"
,
""
).
equals
(
got
))
{
// JTS 1.15
assertEquals
(
jtsWkt
.
replaceAll
(
" Z "
,
" Z"
),
got
);
// JTS 1.16
}
}
// Test Geometry->WKB conversion
assertEquals
(
wkbFromJTS
,
JTSUtils
.
geometry2ewkb
(
geometryFromJTS
));
// Test Envelope
Envelope
envelopeFromJTS
=
geometryFromJTS
.
getEnvelopeInternal
();
testEnvelope
(
envelopeFromJTS
,
GeometryUtils
.
getEnvelope
(
wkbFromJTS
));
EnvelopeAndDimensionSystemTarget
target
=
new
EnvelopeAndDimensionSystemTarget
();
EWKBUtils
.
parseEWKB
(
wkbFromJTS
,
target
);
testEnvelope
(
envelopeFromJTS
,
target
.
getEnvelope
());
// Test dimensions
testDimensions
(
numOfDimensions
>
2
?
GeometryUtils
.
DIMENSION_SYSTEM_XYZ
:
GeometryUtils
.
DIMENSION_SYSTEM_XY
,
wkbFromJTS
);
}
private
void
testEnvelope
(
Envelope
envelopeFromJTS
,
double
[]
envelopeFromH2
)
{
if
(
envelopeFromJTS
.
isNull
())
{
assertNull
(
envelopeFromH2
);
assertNull
(
EWKBUtils
.
envelope2wkb
(
envelopeFromH2
));
}
else
{
assertEquals
(
envelopeFromJTS
.
getMinX
(),
envelopeFromH2
[
0
]);
assertEquals
(
envelopeFromJTS
.
getMaxX
(),
envelopeFromH2
[
1
]);
assertEquals
(
envelopeFromJTS
.
getMinY
(),
envelopeFromH2
[
2
]);
assertEquals
(
envelopeFromJTS
.
getMaxY
(),
envelopeFromH2
[
3
]);
assertEquals
(
new
WKBWriter
(
2
).
write
(
new
GeometryFactory
().
toGeometry
(
envelopeFromJTS
)),
EWKBUtils
.
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
()
throws
Exception
{
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
(
EWKTUtils
.
ewkt2ewkb
(
"POINTM(1 2 3)"
)));
assertEquals
(
"POINT M (1 2 3)"
,
EWKTUtils
.
ewkb2ewkt
(
EWKTUtils
.
ewkt2ewkb
(
"pointm(1 2 3)"
)));
Point
p
=
(
Point
)
JTSUtils
.
ewkb2geometry
(
ewkb
);
CoordinateSequence
cs
=
p
.
getCoordinateSequence
();
testDimensionMCheckPoint
(
cs
);
assertEquals
(
ewkb
,
JTSUtils
.
geometry2ewkb
(
p
));
testDimensions
(
GeometryUtils
.
DIMENSION_SYSTEM_XYM
,
ewkb
);
if
(
JTSUtils
.
M_IS_SUPPORTED
)
{
p
=
(
Point
)
new
WKTReader
().
read
(
"POINT M (1 2 3)"
);
cs
=
p
.
getCoordinateSequence
();
assertEquals
(
3
,
cs
.
getDimension
());
assertEquals
(
1
,
(
int
)
cs
.
getClass
().
getMethod
(
"getMeasures"
).
invoke
(
cs
));
assertEquals
(
1
,
cs
.
getOrdinate
(
0
,
0
));
assertEquals
(
2
,
cs
.
getOrdinate
(
0
,
1
));
assertEquals
(
3
,
cs
.
getOrdinate
(
0
,
2
));
ewkb
=
JTSUtils
.
geometry2ewkb
(
p
);
assertEquals
(
"POINT M (1 2 3)"
,
EWKTUtils
.
ewkb2ewkt
(
ewkb
));
p
=
(
Point
)
JTSUtils
.
ewkb2geometry
(
ewkb
);
cs
=
p
.
getCoordinateSequence
();
testDimensionMCheckPoint
(
cs
);
assertEquals
(
1
,
(
int
)
cs
.
getClass
().
getMethod
(
"getMeasures"
).
invoke
(
cs
));
}
}
private
void
testDimensionMCheckPoint
(
CoordinateSequence
cs
)
{
assertEquals
(
4
,
cs
.
getDimension
());
assertEquals
(
1
,
cs
.
getOrdinate
(
0
,
X
));
assertEquals
(
2
,
cs
.
getOrdinate
(
0
,
Y
));
assertEquals
(
Double
.
NaN
,
cs
.
getOrdinate
(
0
,
Z
));
assertEquals
(
3
,
cs
.
getOrdinate
(
0
,
M
));
}
private
void
testDimensionZM
()
throws
Exception
{
byte
[]
ewkb
=
EWKTUtils
.
ewkt2ewkb
(
"POINT ZM (1 2 3 4)"
);
assertEquals
(
"POINT ZM (1 2 3 4)"
,
EWKTUtils
.
ewkb2ewkt
(
ewkb
));
assertEquals
(
"POINT ZM (1 2 3 4)"
,
EWKTUtils
.
ewkb2ewkt
(
EWKTUtils
.
ewkt2ewkb
(
"POINTZM(1 2 3 4)"
)));
assertEquals
(
"POINT ZM (1 2 3 4)"
,
EWKTUtils
.
ewkb2ewkt
(
EWKTUtils
.
ewkt2ewkb
(
"pointzm(1 2 3 4)"
)));
Point
p
=
(
Point
)
JTSUtils
.
ewkb2geometry
(
ewkb
);
CoordinateSequence
cs
=
p
.
getCoordinateSequence
();
testDimensionZMCheckPoint
(
cs
);
assertEquals
(
ewkb
,
JTSUtils
.
geometry2ewkb
(
p
));
testDimensions
(
GeometryUtils
.
DIMENSION_SYSTEM_XYZM
,
ewkb
);
if
(
JTSUtils
.
M_IS_SUPPORTED
)
{
p
=
(
Point
)
new
WKTReader
().
read
(
"POINT ZM (1 2 3 4)"
);
cs
=
p
.
getCoordinateSequence
();
testDimensionZMCheckPoint
(
cs
);
assertEquals
(
1
,
(
int
)
cs
.
getClass
().
getMethod
(
"getMeasures"
).
invoke
(
cs
));
ewkb
=
JTSUtils
.
geometry2ewkb
(
p
);
assertEquals
(
"POINT ZM (1 2 3 4)"
,
EWKTUtils
.
ewkb2ewkt
(
ewkb
));
p
=
(
Point
)
JTSUtils
.
ewkb2geometry
(
ewkb
);
cs
=
p
.
getCoordinateSequence
();
testDimensionZMCheckPoint
(
cs
);
assertEquals
(
1
,
(
int
)
cs
.
getClass
().
getMethod
(
"getMeasures"
).
invoke
(
cs
));
}
}
private
void
testDimensionZMCheckPoint
(
CoordinateSequence
cs
)
{
assertEquals
(
4
,
cs
.
getDimension
());
assertEquals
(
1
,
cs
.
getOrdinate
(
0
,
X
));
assertEquals
(
2
,
cs
.
getOrdinate
(
0
,
Y
));
assertEquals
(
3
,
cs
.
getOrdinate
(
0
,
Z
));
assertEquals
(
4
,
cs
.
getOrdinate
(
0
,
M
));
}
private
void
testFiniteOnly
()
{
for
(
int
i
=
0
;
i
<
NON_FINITE
.
length
;
i
++)
{
testFiniteOnly
(
NON_FINITE
[
i
],
new
EWKBTarget
(
new
ByteArrayOutputStream
(),
NON_FINITE_DIMENSIONS
[
i
]));
}
for
(
int
i
=
0
;
i
<
NON_FINITE
.
length
;
i
++)
{
testFiniteOnly
(
NON_FINITE
[
i
],
new
EWKTTarget
(
new
StringBuilder
(),
NON_FINITE_DIMENSIONS
[
i
]));
}
for
(
int
i
=
0
;
i
<
NON_FINITE
.
length
;
i
++)
{
testFiniteOnly
(
NON_FINITE
[
i
],
new
GeometryTarget
(
NON_FINITE_DIMENSIONS
[
i
]));
}
}
private
void
testFiniteOnly
(
byte
[]
ewkb
,
Target
target
)
{
try
{
EWKBUtils
.
parseEWKB
(
ewkb
,
target
);
fail
(
target
.
getClass
().
getName
()
+
' '
+
StringUtils
.
convertBytesToHex
(
ewkb
));
}
catch
(
IllegalArgumentException
e
)
{
// Expected
}
}
private
void
testSRID
()
throws
Exception
{
byte
[]
ewkb
=
EWKTUtils
.
ewkt2ewkb
(
"SRID=10;GEOMETRYCOLLECTION (POINT (1 2))"
);
assertEquals
(
StringUtils
.
convertHexToBytes
(
""
// ******** Geometry collection ********
// BOM (BigEndian)
+
"00"
// Only top-level object has a SRID
// type (SRID | POINT)
+
"20000007"
// SRID = 10
+
"0000000a"
// 1 item
+
"00000001"
// ******** Point ********
// BOM (BigEndian)
+
"00"
// type (POINT)
+
"00000001"
// 1.0
+
"3ff0000000000000"
// 2.0
+
"4000000000000000"
),
ewkb
);
assertEquals
(
"SRID=10;GEOMETRYCOLLECTION (POINT (1 2))"
,
EWKTUtils
.
ewkb2ewkt
(
ewkb
));
GeometryCollection
gc
=
(
GeometryCollection
)
JTSUtils
.
ewkb2geometry
(
ewkb
);
assertEquals
(
10
,
gc
.
getSRID
());
assertEquals
(
10
,
gc
.
getGeometryN
(
0
).
getSRID
());
assertEquals
(
ewkb
,
JTSUtils
.
geometry2ewkb
(
gc
));
}
private
void
testDimensions
(
int
expected
,
byte
[]
ewkb
)
{
DimensionSystemTarget
dst
=
new
DimensionSystemTarget
();
EWKBUtils
.
parseEWKB
(
ewkb
,
dst
);
assertEquals
(
expected
,
dst
.
getDimensionSystem
());
EnvelopeAndDimensionSystemTarget
envelopeAndDimensionTarget
=
new
EnvelopeAndDimensionSystemTarget
();
EWKBUtils
.
parseEWKB
(
ewkb
,
envelopeAndDimensionTarget
);
assertEquals
(
expected
,
envelopeAndDimensionTarget
.
getDimensionSystem
());
}
private
void
testIntersectionAndUnion
()
{
double
[]
zero
=
new
double
[
4
];
assertFalse
(
GeometryUtils
.
intersects
(
null
,
null
));
assertFalse
(
GeometryUtils
.
intersects
(
null
,
zero
));
assertFalse
(
GeometryUtils
.
intersects
(
zero
,
null
));
assertNull
(
GeometryUtils
.
union
(
null
,
null
));
assertEquals
(
zero
,
GeometryUtils
.
union
(
null
,
zero
));
assertEquals
(
zero
,
GeometryUtils
.
union
(
zero
,
null
));
// These 30 values with fixed seed 0 are enough to cover all remaining
// cases
Random
r
=
new
Random
(
0
);
for
(
int
i
=
0
;
i
<
30
;
i
++)
{
double
[]
envelope1
=
getEnvelope
(
r
);
double
[]
envelope2
=
getEnvelope
(
r
);
Envelope
e1
=
convert
(
envelope1
);
Envelope
e2
=
convert
(
envelope2
);
assertEquals
(
e1
.
intersects
(
e2
),
GeometryUtils
.
intersects
(
envelope1
,
envelope2
));
e1
.
expandToInclude
(
e2
);
assertEquals
(
e1
,
convert
(
GeometryUtils
.
union
(
envelope1
,
envelope2
)));
}
}
private
static
Envelope
convert
(
double
[]
envelope
)
{
return
new
Envelope
(
envelope
[
MIN_X
],
envelope
[
MAX_X
],
envelope
[
MIN_Y
],
envelope
[
MAX_Y
]);
}
private
static
double
[]
getEnvelope
(
Random
r
)
{
double
minX
=
r
.
nextDouble
();
double
maxX
=
r
.
nextDouble
();
if
(
minX
>
maxX
)
{
double
t
=
minX
;
minX
=
maxX
;
maxX
=
t
;
}
double
minY
=
r
.
nextDouble
();
double
maxY
=
r
.
nextDouble
();
if
(
minY
>
maxY
)
{
double
t
=
minY
;
minY
=
maxY
;
maxY
=
t
;
}
return
new
double
[]
{
minX
,
maxX
,
minY
,
maxY
};
}
private
void
testMixedGeometries
()
throws
Exception
{
try
{
EWKTUtils
.
ewkt2ewkb
(
MIXED_WKT
);
fail
();
}
catch
(
IllegalArgumentException
ex
)
{
// Expected
}
try
{
EWKTUtils
.
ewkb2ewkt
(
MIXED_WKB
);
fail
();
}
catch
(
IllegalArgumentException
ex
)
{
// Expected
}
try
{
JTSUtils
.
ewkb2geometry
(
MIXED_WKB
);
fail
();
}
catch
(
IllegalArgumentException
ex
)
{
// Expected
}
Geometry
g
=
new
WKTReader
().
read
(
MIXED_WKT
);
try
{
JTSUtils
.
geometry2ewkb
(
g
);
fail
();
}
catch
(
IllegalArgumentException
ex
)
{
// Expected
}
}
private
void
testMixedGeometriesAccept
()
throws
Exception
{
ProcessBuilder
pb
=
new
ProcessBuilder
().
redirectError
(
Redirect
.
INHERIT
);
pb
.
command
(
getJVM
(),
"-cp"
,
getClassPath
(),
"-ea"
,
"-Dh2.mixedGeometries=true"
,
getClass
().
getName
(),
"dummy"
);
assertEquals
(
0
,
pb
.
start
().
waitFor
());
}
private
void
testMixedGeometriesAcceptImpl
()
throws
Exception
{
assertEquals
(
MIXED_WKB
,
EWKTUtils
.
ewkt2ewkb
(
MIXED_WKT
));
assertEquals
(
MIXED_WKT_Z
,
EWKTUtils
.
ewkb2ewkt
(
MIXED_WKB
));
Geometry
g
=
new
WKTReader
().
read
(
MIXED_WKT
);
assertEquals
(
MIXED_WKB
,
JTSUtils
.
geometry2ewkb
(
g
));
LineString
ls
=
(
LineString
)
JTSUtils
.
ewkb2geometry
(
MIXED_WKB
);
CoordinateSequence
cs
=
ls
.
getCoordinateSequence
();
assertEquals
(
2
,
cs
.
size
());
assertEquals
(
3
,
cs
.
getDimension
());
assertEquals
(
1
,
cs
.
getOrdinate
(
0
,
X
));
assertEquals
(
2
,
cs
.
getOrdinate
(
0
,
Y
));
assertEquals
(
Double
.
NaN
,
cs
.
getOrdinate
(
0
,
Z
));
assertEquals
(
3
,
cs
.
getOrdinate
(
1
,
X
));
assertEquals
(
4
,
cs
.
getOrdinate
(
1
,
Y
));
assertEquals
(
5
,
cs
.
getOrdinate
(
1
,
Z
));
}
}
h2/src/test/org/h2/test/unit/TestUtils.java
浏览文件 @
ba63ffc6
...
...
@@ -47,6 +47,7 @@ public class TestUtils extends TestBase {
testIOUtils
();
testSortTopN
();
testSortTopNRandom
();
testWriteReadInt
();
testWriteReadLong
();
testGetNonPrimitiveClass
();
testGetNonPrimitiveClass
();
...
...
@@ -94,23 +95,62 @@ public class TestUtils extends TestBase {
}
}
private
void
testWriteReadInt
()
{
byte
[]
buff
=
new
byte
[
4
];
for
(
int
x
:
new
int
[]{
Integer
.
MIN_VALUE
,
Integer
.
MAX_VALUE
,
0
,
1
,
-
1
,
Short
.
MIN_VALUE
,
Short
.
MAX_VALUE
})
{
testIntImpl1
(
buff
,
x
);
}
Random
r
=
new
Random
(
1
);
for
(
int
i
=
0
;
i
<
1000
;
i
++)
{
testIntImpl1
(
buff
,
r
.
nextInt
());
}
}
private
void
testIntImpl1
(
byte
[]
buff
,
int
x
)
{
int
r
=
Integer
.
reverseBytes
(
x
);
Bits
.
writeInt
(
buff
,
0
,
x
);
testIntImpl2
(
buff
,
x
,
r
);
Bits
.
writeIntLE
(
buff
,
0
,
x
);
testIntImpl2
(
buff
,
r
,
x
);
}
private
void
testIntImpl2
(
byte
[]
buff
,
int
x
,
int
r
)
{
assertEquals
(
x
,
Bits
.
readInt
(
buff
,
0
));
assertEquals
(
r
,
Bits
.
readIntLE
(
buff
,
0
));
}
private
void
testWriteReadLong
()
{
byte
[]
buff
=
new
byte
[
8
];
for
(
long
x
:
new
long
[]{
Long
.
MIN_VALUE
,
Long
.
MAX_VALUE
,
0
,
1
,
-
1
,
Integer
.
MIN_VALUE
,
Integer
.
MAX_VALUE
})
{
Bits
.
writeLong
(
buff
,
0
,
x
);
long
y
=
Bits
.
readLong
(
buff
,
0
);
assertEquals
(
x
,
y
);
testLongImpl1
(
buff
,
x
);
}
Random
r
=
new
Random
(
1
);
for
(
int
i
=
0
;
i
<
1000
;
i
++)
{
long
x
=
r
.
nextLong
();
Bits
.
writeLong
(
buff
,
0
,
x
);
long
y
=
Bits
.
readLong
(
buff
,
0
);
assertEquals
(
x
,
y
);
testLongImpl1
(
buff
,
r
.
nextLong
());
}
}
private
void
testLongImpl1
(
byte
[]
buff
,
long
x
)
{
long
r
=
Long
.
reverseBytes
(
x
);
Bits
.
writeLong
(
buff
,
0
,
x
);
testLongImpl2
(
buff
,
x
,
r
);
Bits
.
writeLongLE
(
buff
,
0
,
x
);
testLongImpl2
(
buff
,
r
,
x
);
Bits
.
writeDouble
(
buff
,
0
,
Double
.
longBitsToDouble
(
x
));
testLongImpl2
(
buff
,
x
,
r
);
Bits
.
writeDoubleLE
(
buff
,
0
,
Double
.
longBitsToDouble
(
x
));
testLongImpl2
(
buff
,
r
,
x
);
}
private
void
testLongImpl2
(
byte
[]
buff
,
long
x
,
long
r
)
{
assertEquals
(
x
,
Bits
.
readLong
(
buff
,
0
));
assertEquals
(
r
,
Bits
.
readLongLE
(
buff
,
0
));
assertEquals
(
Double
.
longBitsToDouble
(
x
),
Bits
.
readDouble
(
buff
,
0
));
assertEquals
(
Double
.
longBitsToDouble
(
r
),
Bits
.
readDoubleLE
(
buff
,
0
));
}
private
void
testSortTopN
()
{
Comparator
<
Integer
>
comp
=
new
Comparator
<
Integer
>()
{
@Override
...
...
h2/src/tools/org/h2/build/doc/dictionary.txt
浏览文件 @
ba63ffc6
...
...
@@ -792,3 +792,6 @@ converging smth rng curs casts unmapping unmapper
immediate hhmmss scheduled hhmm prematurely postponed arranges subexpression subexpressions encloses plane caution
minxf maxxf minyf maxyf bminxf bmaxxf bminyf bmaxyf
minxd maxxd minyd maxyd bminxd bmaxxd bminyd bmaxyd
interior envelopes multilinestring multipoint packed exterior normalization awkward determination subgeometries
xym normalizes coord setz xyzm geometrycollection multipolygon mixup rings polygons rejection finite
pointzm pointz pointm dimensionality
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论