Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
9ca18311
提交
9ca18311
authored
9月 09, 2018
作者:
Evgenij Ryazanov
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add utilities for GEOMETRY data type
上级
5f682ece
隐藏空白字符变更
内嵌
并排
正在显示
8 个修改的文件
包含
2482 行增加
和
0 行删除
+2482
-0
EWKBUtils.java
h2/src/main/org/h2/util/geometry/EWKBUtils.java
+438
-0
EWKTUtils.java
h2/src/main/org/h2/util/geometry/EWKTUtils.java
+757
-0
GeometryUtils.java
h2/src/main/org/h2/util/geometry/GeometryUtils.java
+586
-0
JTSUtils.java
h2/src/main/org/h2/util/geometry/JTSUtils.java
+385
-0
package.html
h2/src/main/org/h2/util/geometry/package.html
+14
-0
TestAll.java
h2/src/test/org/h2/test/TestAll.java
+2
-0
TestGeometryUtils.java
h2/src/test/org/h2/test/unit/TestGeometryUtils.java
+298
-0
dictionary.txt
h2/src/tools/org/h2/build/doc/dictionary.txt
+2
-0
没有找到文件。
h2/src/main/org/h2/util/geometry/EWKBUtils.java
0 → 100644
浏览文件 @
9ca18311
/*
* 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
.
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
java.io.ByteArrayOutputStream
;
import
org.h2.util.Bits
;
import
org.h2.util.StringUtils
;
import
org.h2.util.geometry.GeometryUtils.DimensionSystemTarget
;
import
org.h2.util.geometry.GeometryUtils.Target
;
/**
* Utilities for GEOMETRY data type.
*/
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
level
;
/**
* 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
startPoint
(
int
srid
)
{
writeHeader
(
POINT
,
srid
);
}
@Override
protected
void
startLineString
(
int
srid
,
int
numPoints
)
{
writeHeader
(
LINE_STRING
,
srid
);
writeInt
(
numPoints
);
}
@Override
protected
void
startPolygon
(
int
srid
,
int
numInner
,
int
numPoints
)
{
writeHeader
(
POLYGON
,
srid
);
writeInt
(
numInner
+
1
);
writeInt
(
numPoints
);
}
@Override
protected
void
startPolygonInner
(
int
numInner
)
{
writeInt
(
numInner
);
}
@Override
protected
void
startCollection
(
int
type
,
int
srid
,
int
numItems
)
{
writeHeader
(
type
,
srid
);
writeInt
(
numItems
);
}
private
void
writeHeader
(
int
type
,
int
srid
)
{
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
;
}
// Never write SRID in inner objects
if
(
level
>
0
)
{
srid
=
0
;
}
else
if
(
srid
!=
0
)
{
type
|=
EWKB_SRID
;
}
output
.
write
(
0
);
writeInt
(
type
);
if
(
srid
!=
0
)
{
writeInt
(
srid
);
}
}
@Override
protected
Target
startCollectionItem
(
int
index
,
int
total
)
{
level
++;
return
this
;
}
@Override
protected
void
endCollectionItem
(
Target
target
,
int
index
,
int
total
)
{
level
--;
}
@Override
protected
void
addCoordinate
(
double
x
,
double
y
,
double
z
,
double
m
,
int
index
,
int
total
)
{
writeDouble
(
x
);
writeDouble
(
y
);
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYZ
)
!=
0
)
{
writeDouble
(
z
);
}
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
)
{
writeDouble
(
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
.
writeLong
(
buf
,
0
,
Double
.
doubleToLongBits
(
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
=
Bits
.
readInt
(
ewkb
,
offset
);
offset
+=
4
;
return
bigEndian
?
result
:
Integer
.
reverseBytes
(
result
);
}
/**
* Reads a 64-bit floating point using current byte order.
*
* @return next 64-bit floating point
*/
double
readCoordinate
()
{
long
v
=
Bits
.
readLong
(
ewkb
,
offset
);
offset
+=
8
;
if
(!
bigEndian
)
{
Long
.
reverseBytes
(
v
);
}
return
toCanonicalDouble
(
Double
.
longBitsToDouble
(
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
();
parseEKWB
(
ewkb
,
dimensionTarget
);
// Write an EWKB
ByteArrayOutputStream
output
=
new
ByteArrayOutputStream
();
EWKBTarget
target
=
new
EWKBTarget
(
output
,
dimensionTarget
.
getDimensionSystem
());
parseEKWB
(
ewkb
,
target
);
return
output
.
toByteArray
();
}
/**
* Parses a EWKB.
*
* @param ewkb
* EWKB representation
* @param target
* output target
*/
public
static
void
parseEKWB
(
byte
[]
ewkb
,
Target
target
)
{
parseEKWB
(
new
EWKBSource
(
ewkb
),
target
,
0
,
0
);
}
/**
* Parses a EWKB.
*
* @param source
* EWKB source
* @param target
* output target
* @param parentType
* type of parent geometry collection, or 0 for the root geometry
* @param parentSrid
* SRID of a parent geometry collection, or any value for the
* root geometry (will be determined from the EWKB instead)
*/
private
static
void
parseEKWB
(
EWKBSource
source
,
Target
target
,
int
parentType
,
int
parentSrid
)
{
try
{
// 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
;
// Preserve parent SRID unconditionally
if
(
parentType
!=
0
)
{
srid
=
parentSrid
;
}
// 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
(
srid
);
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
(
srid
,
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
(
srid
,
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
,
srid
,
numItems
);
for
(
int
i
=
0
;
i
<
numItems
;
i
++)
{
Target
innerTarget
=
target
.
startCollectionItem
(
i
,
numItems
);
parseEKWB
(
source
,
innerTarget
,
type
,
srid
);
target
.
endCollectionItem
(
innerTarget
,
i
,
numItems
);
}
target
.
endCollection
(
type
);
break
;
}
default
:
throw
new
IllegalArgumentException
();
}
}
catch
(
ArrayIndexOutOfBoundsException
e
)
{
e
.
printStackTrace
();
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
);
}
/**
* 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
;
}
private
EWKBUtils
()
{
}
}
h2/src/main/org/h2/util/geometry/EWKTUtils.java
0 → 100644
浏览文件 @
9ca18311
/*
* 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
.
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.util.StringUtils
;
import
org.h2.util.geometry.EWKBUtils.EWKBTarget
;
import
org.h2.util.geometry.GeometryUtils.DimensionSystemTarget
;
import
org.h2.util.geometry.GeometryUtils.Target
;
/**
* Utilities for GEOMETRY data type.
*/
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
level
;
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
startPoint
(
int
srid
)
{
writeHeader
(
POINT
,
srid
);
}
@Override
protected
void
startLineString
(
int
srid
,
int
numPoints
)
{
writeHeader
(
LINE_STRING
,
srid
);
if
(
numPoints
==
0
)
{
output
.
append
(
"EMPTY"
);
}
}
@Override
protected
void
startPolygon
(
int
srid
,
int
numInner
,
int
numPoints
)
{
writeHeader
(
POLYGON
,
srid
);
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
srid
,
int
numItems
)
{
writeHeader
(
type
,
srid
);
if
(
numItems
==
0
)
{
output
.
append
(
"EMPTY"
);
}
if
(
type
!=
GEOMETRY_COLLECTION
)
{
inMulti
=
true
;
}
}
private
void
writeHeader
(
int
type
,
int
srid
)
{
// Never write SRID in inner objects
if
(
level
==
0
&&
srid
!=
0
)
{
output
.
append
(
"SRID="
).
append
(
srid
).
append
(
';'
);
}
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
)
{
level
++;
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
(
')'
);
}
level
--;
}
@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
(
index
==
0
)
{
output
.
append
(
'('
);
}
else
{
output
.
append
(
", "
);
}
writeDouble
(
x
);
output
.
append
(
' '
);
writeDouble
(
y
);
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYZ
)
!=
0
)
{
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
(
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
;
int
srid
;
EWKTSource
(
String
ewkt
)
{
this
.
ewkt
=
ewkt
;
if
(
ewkt
.
startsWith
(
"SRID="
))
{
int
idx
=
ewkt
.
indexOf
(
';'
,
5
);
srid
=
Integer
.
parseInt
(
ewkt
.
substring
(
5
,
idx
));
offset
=
idx
+
1
;
}
else
{
srid
=
0
;
}
}
void
read
(
char
symbol
)
{
skipWS
();
int
len
=
ewkt
.
length
();
if
(
offset
>=
len
)
{
throw
new
IllegalArgumentException
();
}
if
(
ewkt
.
charAt
(
offset
)
!=
symbol
)
{
throw
new
IllegalArgumentException
();
}
offset
++;
}
boolean
readEmpty
(
boolean
empty
)
{
if
(
empty
)
{
return
true
;
}
skipWS
();
int
len
=
ewkt
.
length
();
if
(
offset
>=
len
)
{
throw
new
IllegalArgumentException
();
}
if
(
ewkt
.
charAt
(
offset
)
==
'('
)
{
offset
++;
return
false
;
}
if
(!
readWord
().
equals
(
"EMPTY"
))
{
throw
new
IllegalArgumentException
();
}
return
true
;
}
String
readWord
()
{
return
readWordImpl
(
true
);
}
String
tryReadWord
()
{
return
readWordImpl
(
false
);
}
private
String
readWordImpl
(
boolean
required
)
{
skipWS
();
int
len
=
ewkt
.
length
();
if
(
offset
>=
len
)
{
if
(
required
)
{
throw
new
IllegalArgumentException
();
}
else
{
return
null
;
}
}
char
ch
=
ewkt
.
charAt
(
offset
);
if
(!
isLatinLetter
(
ch
))
{
if
(
required
)
{
throw
new
IllegalArgumentException
();
}
else
{
return
null
;
}
}
int
start
=
offset
++;
while
(
offset
<
len
&&
isLatinLetter
(
ch
=
ewkt
.
charAt
(
offset
)))
{
offset
++;
}
if
(
offset
<
len
)
{
if
(
ch
>
' '
&&
ch
!=
'('
&&
ch
!=
')'
&&
ch
!=
','
)
{
throw
new
IllegalArgumentException
();
}
}
return
StringUtils
.
toUpperEnglish
(
ewkt
.
substring
(
start
,
offset
));
}
private
static
boolean
isLatinLetter
(
char
ch
)
{
return
ch
>=
'A'
&&
ch
<=
'Z'
||
ch
>=
'a'
&&
ch
<=
'z'
;
}
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
'.'
:
case
'N'
:
case
'n'
:
return
true
;
default
:
return
false
;
}
}
private
static
boolean
isNumberPart
(
char
ch
)
{
if
(
ch
>=
'0'
&&
ch
<=
'9'
)
{
return
true
;
}
switch
(
ch
)
{
case
'+'
:
case
'-'
:
case
'.'
:
case
'A'
:
case
'E'
:
case
'N'
:
case
'a'
:
case
'e'
:
case
'n'
:
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
.
parseEKWB
(
ewkb
,
dimensionTarget
);
// Write an EWKT
StringBuilder
output
=
new
StringBuilder
();
EWKTTarget
target
=
new
EWKTTarget
(
output
,
dimensionTarget
.
getDimensionSystem
());
EWKBUtils
.
parseEKWB
(
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
ByteArrayOutputStream
output
=
new
ByteArrayOutputStream
();
EWKBTarget
target
=
new
EWKBTarget
(
output
,
dimensionTarget
.
getDimensionSystem
());
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
,
false
,
false
);
}
/**
* 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 useZ
* parent geometry uses dimension Z
* @param useM
* parent geometry uses dimension M
*/
private
static
void
parseEWKT
(
EWKTSource
source
,
Target
target
,
int
parentType
,
boolean
useZ
,
boolean
useM
)
{
String
type
;
boolean
empty
=
false
;
switch
(
parentType
)
{
default
:
{
type
=
source
.
readWord
();
if
(
type
.
endsWith
(
"M"
))
{
useM
=
true
;
if
(
type
.
endsWith
(
"ZM"
))
{
useZ
=
true
;
type
=
type
.
substring
(
0
,
type
.
length
()
-
2
);
}
else
{
type
=
type
.
substring
(
0
,
type
.
length
()
-
1
);
}
}
else
if
(
type
.
endsWith
(
"Z"
))
{
useZ
=
true
;
type
=
type
.
substring
(
0
,
type
.
length
()
-
1
);
}
else
{
String
s
=
source
.
tryReadWord
();
if
(
s
!=
null
)
{
switch
(
s
)
{
case
"Z"
:
useZ
=
true
;
break
;
case
"M"
:
useM
=
true
;
break
;
case
"ZM"
:
useZ
=
useM
=
true
;
break
;
case
"EMPTY"
:
empty
=
true
;
break
;
default
:
throw
new
IllegalArgumentException
();
}
}
}
break
;
}
case
MULTI_POINT:
type
=
"POINT"
;
break
;
case
MULTI_LINE_STRING:
type
=
"LINESTRING"
;
break
;
case
MULTI_POLYGON:
type
=
"POLYGON"
;
break
;
}
switch
(
type
)
{
case
"POINT"
:
if
(
parentType
!=
0
&&
parentType
!=
MULTI_POINT
&&
parentType
!=
GEOMETRY_COLLECTION
||
empty
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPoint
(
source
.
srid
);
source
.
read
(
'('
);
addCoordinate
(
source
,
target
,
useZ
,
useM
,
0
,
1
);
source
.
read
(
')'
);
break
;
case
"LINESTRING"
:
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_LINE_STRING
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
empty
=
source
.
readEmpty
(
empty
);
if
(
empty
)
{
target
.
startLineString
(
source
.
srid
,
0
);
}
else
{
ArrayList
<
double
[]>
coordinates
=
new
ArrayList
<>();
do
{
coordinates
.
add
(
readCoordinate
(
source
,
useZ
,
useM
));
}
while
(
source
.
hasMoreCoordinates
());
int
numPoints
=
coordinates
.
size
();
if
(
numPoints
<
0
||
numPoints
==
1
)
{
throw
new
IllegalArgumentException
();
}
target
.
startLineString
(
source
.
srid
,
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
();
}
empty
=
source
.
readEmpty
(
empty
);
if
(
empty
)
{
target
.
startPolygon
(
source
.
srid
,
0
,
0
);
}
else
{
ArrayList
<
double
[]>
outer
=
readRing
(
source
,
useZ
,
useM
);
ArrayList
<
ArrayList
<
double
[]>>
inner
=
new
ArrayList
<>();
while
(
source
.
hasMoreCoordinates
())
{
inner
.
add
(
readRing
(
source
,
useZ
,
useM
));
}
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
(
source
.
srid
,
numInner
,
size
);
if
(
size
>
0
)
{
addRing
(
outer
,
target
,
useZ
,
useM
);
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
,
useZ
,
useM
);
}
target
.
endNonEmptyPolygon
();
}
}
break
;
}
case
"MULTIPOINT"
:
parseCollection
(
source
,
target
,
MULTI_POINT
,
parentType
,
empty
,
useZ
,
useM
);
break
;
case
"MULTILINESTRING"
:
parseCollection
(
source
,
target
,
MULTI_LINE_STRING
,
parentType
,
empty
,
useZ
,
useM
);
break
;
case
"MULTIPOLYGON"
:
parseCollection
(
source
,
target
,
MULTI_POLYGON
,
parentType
,
empty
,
useZ
,
useM
);
break
;
case
"GEOMETRYCOLLECTION"
:
parseCollection
(
source
,
target
,
GEOMETRY_COLLECTION
,
parentType
,
empty
,
useZ
,
useM
);
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
,
boolean
empty
,
boolean
useZ
,
boolean
useM
)
{
if
(
parentType
!=
0
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
if
(
source
.
readEmpty
(
empty
))
{
target
.
startCollection
(
type
,
source
.
srid
,
0
);
}
else
{
if
(
type
==
MULTI_POINT
&&
source
.
hasCoordinate
())
{
parseMultiPointAlternative
(
source
,
target
,
useZ
,
useM
);
}
else
{
int
numItems
=
source
.
getItemCount
();
target
.
startCollection
(
type
,
source
.
srid
,
numItems
);
for
(
int
i
=
0
;
i
<
numItems
;
i
++)
{
if
(
i
>
0
)
{
source
.
read
(
','
);
}
Target
innerTarget
=
target
.
startCollectionItem
(
i
,
numItems
);
parseEWKT
(
source
,
innerTarget
,
type
,
useZ
,
useM
);
target
.
endCollectionItem
(
innerTarget
,
i
,
numItems
);
}
source
.
read
(
')'
);
}
}
target
.
endCollection
(
type
);
}
private
static
void
parseMultiPointAlternative
(
EWKTSource
source
,
Target
target
,
boolean
useZ
,
boolean
useM
)
{
// Special case for MULTIPOINT (1 2, 3 4)
ArrayList
<
double
[]>
points
=
new
ArrayList
<>();
do
{
points
.
add
(
readCoordinate
(
source
,
useZ
,
useM
));
}
while
(
source
.
hasMoreCoordinates
());
int
numItems
=
points
.
size
();
target
.
startCollection
(
MULTI_POINT
,
source
.
srid
,
numItems
);
for
(
int
i
=
0
;
i
<
points
.
size
();
i
++)
{
Target
innerTarget
=
target
.
startCollectionItem
(
i
,
numItems
);
target
.
startPoint
(
source
.
srid
);
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
,
boolean
useZ
,
boolean
useM
)
{
if
(
source
.
readEmpty
(
false
))
{
return
new
ArrayList
<>(
0
);
}
ArrayList
<
double
[]>
result
=
new
ArrayList
<>();
double
[]
c
=
readCoordinate
(
source
,
useZ
,
useM
);
double
startX
=
c
[
X
],
startY
=
c
[
Y
];
result
.
add
(
c
);
while
(
source
.
hasMoreCoordinates
())
{
result
.
add
(
readCoordinate
(
source
,
useZ
,
useM
));
}
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
,
boolean
useZ
,
boolean
useM
)
{
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
,
boolean
useZ
,
boolean
useM
,
int
index
,
int
total
)
{
double
x
=
source
.
readCoordinate
();
double
y
=
source
.
readCoordinate
();
double
z
=
Double
.
NaN
,
m
=
Double
.
NaN
;
if
(
source
.
hasCoordinate
())
{
if
(!
useZ
&&
useM
)
{
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
,
boolean
useZ
,
boolean
useM
)
{
double
x
=
source
.
readCoordinate
();
double
y
=
source
.
readCoordinate
();
double
z
=
Double
.
NaN
,
m
=
Double
.
NaN
;
if
(
source
.
hasCoordinate
())
{
if
(!
useZ
&&
useM
)
{
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
浏览文件 @
9ca18311
/*
* 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
org.h2.util.Bits
;
/**
* Utilities for GEOMETRY data type.
*/
public
final
class
GeometryUtils
{
/**
* Converter output target.
*/
public
static
abstract
class
Target
{
public
Target
()
{
}
/**
* Invoked before writing a POINT.
*
* @param srid
* SRID
*/
protected
void
startPoint
(
int
srid
)
{
}
/**
* Invoked before writing a LINESTRING.
*
* @param srid
* SRID
* @param numPoints
* number of points in line string
*/
protected
void
startLineString
(
int
srid
,
int
numPoints
)
{
}
/**
* Invoked before writing a POLYGON.
*
* @param srid
* SRID
* @param numInner
* number of inner polygons
* @param numPoints
* number of points in outer polygon
*/
protected
void
startPolygon
(
int
srid
,
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 srid
* SRID
* @param numItems
* number of items in this collection
*/
protected
void
startCollection
(
int
type
,
int
srid
,
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, 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
(
int
srid
)
{
enabled
=
true
;
}
@Override
protected
void
startLineString
(
int
srid
,
int
numPoints
)
{
enabled
=
true
;
}
@Override
protected
void
startPolygon
(
int
srid
,
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
(
enabled
)
{
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
(
int
srid
)
{
enabled
=
true
;
}
@Override
protected
void
startLineString
(
int
srid
,
int
numPoints
)
{
enabled
=
true
;
}
@Override
protected
void
startPolygon
(
int
srid
,
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
;
}
if
(
enabled
)
{
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
)
{
EnvelopeAndDimensionSystemTarget
target
=
new
EnvelopeAndDimensionSystemTarget
();
EWKBUtils
.
parseEKWB
(
ewkb
,
target
);
return
target
.
getEnvelope
();
}
/**
* 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
.
writeLong
(
result
,
5
,
Double
.
doubleToRawLongBits
(
minX
));
Bits
.
writeLong
(
result
,
13
,
Double
.
doubleToRawLongBits
(
minY
));
}
else
if
(
minX
==
maxX
||
minY
==
maxY
)
{
result
=
new
byte
[
41
];
result
[
4
]
=
LINE_STRING
;
result
[
8
]
=
2
;
Bits
.
writeLong
(
result
,
9
,
Double
.
doubleToRawLongBits
(
minX
));
Bits
.
writeLong
(
result
,
17
,
Double
.
doubleToRawLongBits
(
minY
));
Bits
.
writeLong
(
result
,
25
,
Double
.
doubleToRawLongBits
(
maxX
));
Bits
.
writeLong
(
result
,
33
,
Double
.
doubleToRawLongBits
(
maxY
));
}
else
{
result
=
new
byte
[
93
];
result
[
4
]
=
POLYGON
;
result
[
8
]
=
1
;
result
[
12
]
=
5
;
Bits
.
writeLong
(
result
,
13
,
Double
.
doubleToRawLongBits
(
minX
));
Bits
.
writeLong
(
result
,
21
,
Double
.
doubleToRawLongBits
(
minY
));
Bits
.
writeLong
(
result
,
29
,
Double
.
doubleToRawLongBits
(
minX
));
Bits
.
writeLong
(
result
,
37
,
Double
.
doubleToRawLongBits
(
maxY
));
Bits
.
writeLong
(
result
,
45
,
Double
.
doubleToRawLongBits
(
maxX
));
Bits
.
writeLong
(
result
,
53
,
Double
.
doubleToRawLongBits
(
maxY
));
Bits
.
writeLong
(
result
,
61
,
Double
.
doubleToRawLongBits
(
maxX
));
Bits
.
writeLong
(
result
,
69
,
Double
.
doubleToRawLongBits
(
minY
));
Bits
.
writeLong
(
result
,
77
,
Double
.
doubleToRawLongBits
(
minX
));
Bits
.
writeLong
(
result
,
85
,
Double
.
doubleToRawLongBits
(
minY
));
}
return
result
;
}
/**
* 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
];
if
(
minX1
>
minX2
)
{
minX1
=
minX2
;
}
if
(
maxX1
<
maxX2
)
{
maxX1
=
maxX2
;
}
if
(
minY1
>
minY2
)
{
minY1
=
minY2
;
}
if
(
maxY1
<
maxY2
)
{
maxY1
=
maxY2
;
}
return
new
double
[]
{
minX1
,
maxX1
,
minY1
,
maxY1
};
}
/**
* 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
;
}
private
GeometryUtils
()
{
}
}
h2/src/main/org/h2/util/geometry/JTSUtils.java
0 → 100644
浏览文件 @
9ca18311
/*
* 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
.
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
.
toCanonicalDouble
;
import
java.io.ByteArrayOutputStream
;
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.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
{
/**
* 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
;
}
@Override
protected
void
startPoint
(
int
srid
)
{
init
(
POINT
,
srid
);
initCoordinates
(
1
);
innerOffset
=
-
1
;
}
@Override
protected
void
startLineString
(
int
srid
,
int
numPoints
)
{
init
(
LINE_STRING
,
srid
);
initCoordinates
(
numPoints
);
innerOffset
=
-
1
;
}
@Override
protected
void
startPolygon
(
int
srid
,
int
numInner
,
int
numPoints
)
{
init
(
POLYGON
,
srid
);
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
srid
,
int
numItems
)
{
init
(
type
,
srid
);
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
);
}
@Override
protected
void
endCollectionItem
(
Target
target
,
int
index
,
int
total
)
{
subgeometries
[
index
]
=
((
GeometryTarget
)
target
).
getGeometry
();
}
private
void
init
(
int
type
,
int
srid
)
{
factory
=
new
GeometryFactory
(
new
PrecisionModel
(
PrecisionModel
.
FLOATING
),
srid
,
(
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
?
PackedCoordinateSequenceFactory
.
DOUBLE_FACTORY
:
CoordinateArraySequenceFactory
.
instance
());
this
.
type
=
type
;
}
private
void
initCoordinates
(
int
numPoints
)
{
coordinates
=
createCoordinates
(
numPoints
);
}
private
CoordinateSequence
createCoordinates
(
int
numPoints
)
{
return
factory
.
getCoordinateSequenceFactory
().
create
(
numPoints
,
(
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
?
4
:
3
);
}
@Override
protected
void
addCoordinate
(
double
x
,
double
y
,
double
z
,
double
m
,
int
index
,
int
total
)
{
CoordinateSequence
coordinates
=
innerOffset
<
0
?
this
.
coordinates
:
innerCoordinates
[
innerOffset
];
coordinates
.
setOrdinate
(
index
,
X
,
x
);
coordinates
.
setOrdinate
(
index
,
Y
,
y
);
coordinates
.
setOrdinate
(
index
,
Z
,
z
);
if
((
dimensionSystem
&
DIMENSION_SYSTEM_XYM
)
!=
0
)
{
coordinates
.
setOrdinate
(
index
,
M
,
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
.
parseEKWB
(
ewkb
,
dimensionTarget
);
// Generate a Geometry
GeometryTarget
target
=
new
GeometryTarget
(
dimensionTarget
.
getDimensionSystem
());
EWKBUtils
.
parseEKWB
(
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
ByteArrayOutputStream
output
=
new
ByteArrayOutputStream
();
EWKBTarget
target
=
new
EWKBTarget
(
output
,
dimensionTarget
.
getDimensionSystem
());
parseGeometry
(
geometry
,
target
);
return
output
.
toByteArray
();
}
/**
* Parses a JTS Geometry object.
*
* @param geometry
* geometry to parse
* @param target
* output target
*/
private
static
void
parseGeometry
(
Geometry
geometry
,
Target
target
)
{
parseGeometry
(
geometry
,
target
,
0
,
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
* @param parentSrid
* SRID of a parent geometry collection, or any value for the
* root geometry (will be determined from the EWKB instead)
*/
private
static
void
parseGeometry
(
Geometry
geometry
,
Target
target
,
int
parentType
,
int
parentSrid
)
{
int
srid
=
geometry
.
getSRID
();
// Preserve parent SRID unconditionally
if
(
parentType
!=
0
)
{
srid
=
parentSrid
;
}
if
(
geometry
instanceof
Point
)
{
if
(
parentType
!=
0
&&
parentType
!=
MULTI_POINT
&&
parentType
!=
GEOMETRY_COLLECTION
)
{
throw
new
IllegalArgumentException
();
}
target
.
startPoint
(
srid
);
Point
p
=
(
Point
)
geometry
;
addCoordinate
(
p
.
getCoordinateSequence
(),
target
,
0
,
1
);
}
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
(
srid
,
numPoints
);
for
(
int
i
=
0
;
i
<
numPoints
;
i
++)
{
addCoordinate
(
cs
,
target
,
i
,
numPoints
);
}
}
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
(
srid
,
numInner
,
size
);
if
(
size
>
0
)
{
addRing
(
cs
,
target
,
size
);
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
);
}
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
,
srid
,
numItems
);
for
(
int
i
=
0
;
i
<
numItems
;
i
++)
{
Target
innerTarget
=
target
.
startCollectionItem
(
i
,
numItems
);
parseGeometry
(
gc
.
getGeometryN
(
i
),
innerTarget
,
type
,
srid
);
target
.
endCollectionItem
(
innerTarget
,
i
,
numItems
);
}
target
.
endCollection
(
type
);
}
else
{
throw
new
IllegalArgumentException
();
}
}
private
static
void
addRing
(
CoordinateSequence
sequence
,
Target
target
,
int
size
)
{
// 0 or 4+ are valid
if
(
size
>=
4
)
{
int
d
=
sequence
.
getDimension
();
boolean
useZ
=
d
>=
3
,
useM
=
d
>=
4
;
double
startX
=
toCanonicalDouble
(
sequence
.
getOrdinate
(
0
,
X
)),
startY
=
toCanonicalDouble
(
sequence
.
getOrdinate
(
0
,
Y
));
target
.
addCoordinate
(
startX
,
startY
,
//
useZ
?
toCanonicalDouble
(
sequence
.
getOrdinate
(
0
,
Z
))
:
Double
.
NaN
,
useM
?
toCanonicalDouble
(
sequence
.
getOrdinate
(
0
,
M
))
:
Double
.
NaN
,
//
0
,
size
);
for
(
int
i
=
1
;
i
<
size
-
1
;
i
++)
{
addCoordinate
(
sequence
,
target
,
i
,
size
);
}
double
endX
=
toCanonicalDouble
(
sequence
.
getOrdinate
(
size
-
1
,
X
)),
endY
=
toCanonicalDouble
(
sequence
.
getOrdinate
(
size
-
1
,
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
();
}
target
.
addCoordinate
(
endX
,
endY
,
//
useZ
?
toCanonicalDouble
(
sequence
.
getOrdinate
(
size
-
1
,
Z
))
:
Double
.
NaN
,
useM
?
toCanonicalDouble
(
sequence
.
getOrdinate
(
size
-
1
,
M
))
:
Double
.
NaN
,
//
size
-
1
,
size
);
}
}
private
static
void
addCoordinate
(
CoordinateSequence
sequence
,
Target
target
,
int
index
,
int
total
)
{
int
d
=
sequence
.
getDimension
();
target
.
addCoordinate
(
toCanonicalDouble
(
sequence
.
getOrdinate
(
index
,
X
)),
toCanonicalDouble
(
sequence
.
getOrdinate
(
index
,
Y
)),
d
>=
3
?
toCanonicalDouble
(
sequence
.
getOrdinate
(
index
,
Z
))
:
Double
.
NaN
,
d
>=
4
?
toCanonicalDouble
(
sequence
.
getOrdinate
(
index
,
M
))
:
Double
.
NaN
,
//
index
,
total
);
}
private
JTSUtils
()
{
}
}
h2/src/main/org/h2/util/geometry/package.html
0 → 100644
浏览文件 @
9ca18311
<!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/test/org/h2/test/TestAll.java
浏览文件 @
9ca18311
...
...
@@ -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
;
...
...
@@ -960,6 +961,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/unit/TestGeometryUtils.java
0 → 100644
浏览文件 @
9ca18311
/*
* 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
.
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.util.Locale
;
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.EWKTUtils
;
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.JTSUtils
;
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.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
{
/**
* Run just this test.
*
* @param a
* ignored
*/
public
static
void
main
(
String
...
a
)
throws
Exception
{
TestBase
.
createCaller
().
init
().
test
();
}
@Override
public
void
test
()
throws
Exception
{
testPoint
();
testLineString
();
testPolygon
();
testMultiPoint
();
testMultiLineString
();
testMultiPolygon
();
testGeometryCollection
();
testDimensionM
();
testDimensionZM
();
testSRID
();
testIntersectionAndUnion
();
}
private
void
testPoint
()
throws
Exception
{
testGeometry
(
"POINT (1 2)"
,
2
);
testGeometry
(
"POINT (-1.3 NaN)"
,
2
);
testGeometry
(
"POINT (-1E32 NaN)"
,
"POINT (-1E32 NaN)"
,
"POINT (-100000000000000000000000000000000 NaN)"
,
2
);
testGeometry
(
"POINT Z (2.7 -3 34)"
,
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 (1 NaN, 2 NaN)"
,
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
);
// JTS has a bug with NaN on non-English locales
Locale
l
=
Locale
.
getDefault
();
Locale
.
setDefault
(
Locale
.
ENGLISH
);
assertEquals
(
jtsWkt
.
replaceAll
(
" Z"
,
""
),
new
WKTWriter
(
numOfDimensions
).
write
(
geometryFromH2
));
Locale
.
setDefault
(
l
);
// 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
.
parseEKWB
(
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
(
GeometryUtils
.
envelope2wkb
(
envelopeFromH2
));
}
else
{
double
minX
=
envelopeFromJTS
.
getMinX
(),
maxX
=
envelopeFromJTS
.
getMaxX
();
double
minY
=
envelopeFromJTS
.
getMinY
(),
maxY
=
envelopeFromJTS
.
getMaxY
();
assertEquals
(
minX
,
envelopeFromH2
[
0
]);
assertEquals
(
maxX
,
envelopeFromH2
[
1
]);
assertEquals
(
minY
,
envelopeFromH2
[
2
]);
assertEquals
(
maxY
,
envelopeFromH2
[
3
]);
// TODO determine what to do with NaNs in dimensions X and Y
if
(!
Double
.
isNaN
(
minX
)
&&
!
Double
.
isNaN
(
maxX
)
&&
!
Double
.
isNaN
(
minY
)
&&
!
Double
.
isNaN
(
maxY
))
{
assertEquals
(
new
WKBWriter
(
2
).
write
(
new
GeometryFactory
().
toGeometry
(
envelopeFromJTS
)),
GeometryUtils
.
envelope2wkb
(
envelopeFromH2
));
}
}
}
private
void
testDimensionM
()
{
byte
[]
ewkb
=
EWKTUtils
.
ewkt2ewkb
(
"POINT M (1 2 3)"
);
assertEquals
(
"POINT M (1 2 3)"
,
EWKTUtils
.
ewkb2ewkt
(
ewkb
));
Point
p
=
(
Point
)
JTSUtils
.
ewkb2geometry
(
ewkb
);
CoordinateSequence
cs
=
p
.
getCoordinateSequence
();
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
));
assertEquals
(
ewkb
,
JTSUtils
.
geometry2ewkb
(
p
));
testDimensions
(
GeometryUtils
.
DIMENSION_SYSTEM_XYM
,
ewkb
);
}
private
void
testDimensionZM
()
{
byte
[]
ewkb
=
EWKTUtils
.
ewkt2ewkb
(
"POINT ZM (1 2 3 4)"
);
assertEquals
(
"POINT ZM (1 2 3 4)"
,
EWKTUtils
.
ewkb2ewkt
(
ewkb
));
Point
p
=
(
Point
)
JTSUtils
.
ewkb2geometry
(
ewkb
);
CoordinateSequence
cs
=
p
.
getCoordinateSequence
();
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
));
assertEquals
(
ewkb
,
JTSUtils
.
geometry2ewkb
(
p
));
testDimensions
(
GeometryUtils
.
DIMENSION_SYSTEM_XYZM
,
ewkb
);
}
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
.
parseEKWB
(
ewkb
,
dst
);
assertEquals
(
expected
,
dst
.
getDimensionSystem
());
EnvelopeAndDimensionSystemTarget
envelopeAndDimensionTarget
=
new
EnvelopeAndDimensionSystemTarget
();
EWKBUtils
.
parseEKWB
(
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
};
}
}
h2/src/tools/org/h2/build/doc/dictionary.txt
浏览文件 @
9ca18311
...
...
@@ -792,3 +792,5 @@ 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 ekwb normalizes coord setz xyzm geometrycollection multipolygon mixup rings polygons
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论