Skip to content

Commit c5d9320

Browse files
committed
Add TWKB Writer.
1 parent 77b0c8c commit c5d9320

10 files changed

Lines changed: 300 additions & 3 deletions

File tree

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@
4646
</exclusion>
4747
</exclusions>
4848
</dependency>
49+
<dependency>
50+
<groupId>org.locationtech.jts.io</groupId>
51+
<artifactId>jts-io-common</artifactId>
52+
<version>1.19.0</version>
53+
</dependency>
4954
<dependency>
5055
<groupId>org.geotools</groupId>
5156
<artifactId>gt-main</artifactId>

src/main/groovy/geoscript/geom/io/GeometryExtensionModule.groovy

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,24 @@ class GeometryExtensionModule {
3535
new WkbWriter().writeBytes(geometry)
3636
}
3737

38+
/**
39+
* Get the TWKB of the Geometry
40+
* @param geometry The Geometry
41+
* @return The TWKB hex string of this Geometry
42+
*/
43+
static String getTwkb(Geometry geometry) {
44+
new TWkbWriter().write(geometry)
45+
}
46+
47+
/**
48+
* Get the WKB of the Geometry
49+
* @param geometry The Geometry
50+
* @return The WKB byte array of this Geometry
51+
*/
52+
static byte[] getTwkbBytes(Geometry geometry) {
53+
new TWkbWriter().writeBytes(geometry)
54+
}
55+
3856
/**
3957
* Get a KML String for this Geometry
4058
* @param geometry The Geometry

src/main/groovy/geoscript/geom/io/StaticGeometryExtensionModule.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,26 @@ class StaticGeometryExtensionModule {
3838
new WkbReader().read(wkb)
3939
}
4040

41+
/**
42+
* Get a Geometry from TWKB byte array
43+
* @param geometry The Geometry
44+
* @param wkb The TWKB byte array
45+
* @return A Geometry
46+
*/
47+
static Geometry fromTwkb(Geometry geometry, byte[] twkb) {
48+
new TWkbReader().read(twkb)
49+
}
50+
51+
/**
52+
* Get a Geometry from TWKB hex string
53+
* @param geometry The Geometry
54+
* @param wkb The TWKB hex string
55+
* @return A Geometry
56+
*/
57+
static Geometry fromTwkb(Geometry geometry, String twkb) {
58+
new TWkbReader().read(twkb)
59+
}
60+
4161
/**
4262
* Get a Geometry from a KML String
4363
* @param geometry The Geometry

src/main/groovy/geoscript/geom/io/TWkbReader.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package geoscript.geom.io
22

33
import geoscript.geom.Geometry
4-
import org.geotools.data.postgis.TWKBReader
54
import org.locationtech.jts.geom.GeometryFactory
65
import org.locationtech.jts.io.WKBReader
6+
import org.locationtech.jts.io.twkb.TWKBReader
77

88
/**
99
* Read a {@link geoscript.geom.Geometry Geometry} from a TWKB hex String or byte array.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package geoscript.geom.io
2+
3+
import geoscript.geom.Geometry
4+
import org.locationtech.jts.io.WKBWriter
5+
import org.locationtech.jts.io.twkb.TWKBWriter
6+
7+
/**
8+
* Write a {@link geoscript.geom.Geometry Geometry} to a WKB hex String or byte array.
9+
* <p><blockquote><pre>
10+
* WkbWriter writer = new WkbWriter()
11+
* String wkb = writer.write(new {@link geoscript.geom.Point Point}(111,-47)
12+
*
13+
* "0000000001405BC00000000000C047800000000000"
14+
* </pre></blockquote></p>
15+
*/
16+
class TWkbWriter implements Writer {
17+
18+
/**
19+
* The JTS WKBWriter
20+
*/
21+
private final TWKBWriter writer
22+
23+
/**
24+
* Create a new WkbWriter with an output dimension of 2 and big endian byte order.
25+
*/
26+
TWkbWriter() {
27+
writer = new TWKBWriter()
28+
}
29+
30+
/**
31+
* Write the Geometry to WKB hex String
32+
* @param geom The Geometry
33+
* @return A WKB hex String
34+
*/
35+
String write(Geometry geom) {
36+
WKBWriter.toHex(writer.write(geom.g))
37+
}
38+
39+
/**
40+
* Write the Geometry to WKB byte array
41+
* @param geom The Geometry
42+
* @return A WKB byte array
43+
*/
44+
byte[] writeBytes(Geometry geom) {
45+
writer.write(geom.g)
46+
}
47+
}

src/main/resources/META-INF/services/geoscript.geom.io.Writer

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ geoscript.geom.io.WkbWriter
99
geoscript.geom.io.WktWriter
1010
geoscript.geom.io.GeoPackageWriter
1111
geoscript.geom.io.GooglePolylineEncoder
12-
geoscript.geom.io.YamlWriter
12+
geoscript.geom.io.YamlWriter
13+
geoscript.geom.io.TWkbWriter

src/test/groovy/geoscript/geom/GeometryTest.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,26 @@ class GeometryTest {
209209
assertEquals "POINT (111 -47)", g.toString()
210210
}
211211

212+
@Test void fromTWKB() {
213+
Geometry g = Geometry.fromTwkb("01000204")
214+
assertEquals "POINT (1 2)", g.toString()
215+
}
216+
217+
@Test void fromTWKBBytes() {
218+
Geometry g = Geometry.fromTwkb([-31, 8, 1, -128, -122, -54, -94, 8, -1, -123, -99, -64, 3, 0] as byte[])
219+
assertEquals "POINT (111 -47)", g.toString()
220+
}
221+
222+
@Test void getTwkb() {
223+
Geometry g = Geometry.fromWKT("POINT (111 -47)")
224+
assertEquals "E108018086CAA208FF859DC00300", g.twkb
225+
}
226+
227+
@Test void getTwkbBytes() {
228+
Geometry g = Geometry.fromWKT("POINT (111 -47)")
229+
assertArrayEquals([-31, 8, 1, -128, -122, -54, -94, 8, -1, -123, -99, -64, 3, 0] as byte[], g.twkbBytes)
230+
}
231+
212232
@Test void getGeoJSON() {
213233
Geometry g = Geometry.fromWKT("POINT (111 -47)")
214234
assertEquals """{"type":"Point","coordinates":[111,-47]}""", g.geoJSON
Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,86 @@
11
package geoscript.geom.io
22

33
import geoscript.geom.Geometry
4+
import geoscript.geom.GeometryCollection
5+
import geoscript.geom.LineString
6+
import geoscript.geom.LinearRing
7+
import geoscript.geom.MultiLineString
8+
import geoscript.geom.MultiPoint
9+
import geoscript.geom.MultiPolygon
10+
import geoscript.geom.Point
11+
import geoscript.geom.Polygon
412
import org.junit.jupiter.api.Test
513
import static org.junit.jupiter.api.Assertions.assertEquals
614

715
class TWkbReaderTest {
816

917
@Test
10-
void read() {
18+
void readPoint() {
1119
TWkbReader reader = new TWkbReader()
1220
Geometry geometry = reader.read("01000204")
1321
assertEquals("POINT (1 2)", geometry.wkt)
1422
geometry = reader.read("01000204".decodeHex())
1523
assertEquals("POINT (1 2)", geometry.wkt)
1624
}
1725

26+
@Test
27+
void readLineString() {
28+
TWkbReader reader = new TWkbReader()
29+
Geometry geometry = reader.read("E20801038086CAA208FF859DC0030080B8B872FFD9C40900FF91FD7B80DAC40900")
30+
LineString l = new LineString([[111.0, -47], [123.0, -48], [110.0, -47]])
31+
assertEquals(l.wkt, geometry.wkt)
32+
}
33+
34+
@Test
35+
void readLinearRing() {
36+
TWkbReader reader = new TWkbReader()
37+
Geometry geometry = reader.read("E20801048086CAA208FF859DC0030080B8B872FFD9C40900FF91FD7B80DAC4090080DAC4090000")
38+
LineString l = new LineString([[111.0, -47], [123.0, -48], [110.0, -47], [111.0, -47]])
39+
assertEquals(l.wkt, geometry.wkt)
40+
}
41+
42+
@Test
43+
void readPolygon() {
44+
TWkbReader reader = new TWkbReader()
45+
Geometry geometry = reader.read("E30801030580DAC40980DAC4090080AAEA5500000080AAEA5500FFA9EA55000000FFA9EA55000580DAC40980DAC4090080B4891300000080B4891300FFB38913000000FFB389130005808ECE1C808ECE1C0080DAC40900000080DAC40900FFD9C409000000FFD9C40900")
46+
Polygon p = new Polygon(new LinearRing([1, 1], [10, 1], [10, 10], [1, 10], [1, 1]),
47+
[
48+
new LinearRing([2,2], [4,2], [4,4], [2,4], [2,2]),
49+
new LinearRing([5,5], [6,5], [6,6], [5,6], [5,5])
50+
]
51+
)
52+
assertEquals(p.wkt, geometry.wkt)
53+
}
54+
55+
@Test
56+
void readMultiPoint() {
57+
TWkbReader reader = new TWkbReader()
58+
Geometry geometry = reader.read("E40801028086CAA208FF859DC00300FFD9C40980ADE20400")
59+
MultiPoint p = new MultiPoint([111, -47],[110, -46.5])
60+
assertEquals(p.wkt, geometry.wkt)
61+
}
62+
63+
@Test
64+
void readMultiLineString() {
65+
TWkbReader reader = new TWkbReader()
66+
Geometry geometry = reader.read("E50801020280DAC40980B489130080B4891380B48913000280B4891380B489130080B4891380B4891300")
67+
MultiLineString m = new MultiLineString(new LineString([1, 2],[3, 4]), new LineString([5, 6],[7, 8]))
68+
assertEquals(m.wkt, geometry.wkt)
69+
}
70+
71+
@Test
72+
void readMultiPolygon() {
73+
TWkbReader reader = new TWkbReader()
74+
Geometry geometry = reader.read("E6080102010480DAC40980B489130080B4891380B489130080B4891380B4891300FFE79226FFE79226000104809C9C39809C9C390080B4891380B489130080B4891380B4891300FFE79226FFE7922600")
75+
MultiPolygon mp = new MultiPolygon([[[[1, 2], [3, 4], [5, 6], [1, 2]]], [[[7, 8], [9, 10], [11, 12], [7, 8]]]])
76+
assertEquals(mp.wkt, geometry.wkt)
77+
}
78+
79+
@Test
80+
void readGeometryCollection() {
81+
TWkbReader reader = new TWkbReader()
82+
Geometry geometry = reader.read("E7080102E1080180A8D6B9070000E208010280829BC307000080DAC40980DAC40900")
83+
GeometryCollection gc = new GeometryCollection(new Point(100.0, 0.0), new LineString([101.0, 0.0], [102.0, 1.0]))
84+
assertEquals(gc.wkt, geometry.wkt)
85+
}
1886
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package geoscript.geom.io
2+
3+
import geoscript.geom.*
4+
import org.junit.jupiter.api.Test
5+
6+
import static org.junit.jupiter.api.Assertions.assertArrayEquals
7+
import static org.junit.jupiter.api.Assertions.assertEquals
8+
9+
/**
10+
* The TwkbWriter UnitTest
11+
*/
12+
class TWkbWriterTest {
13+
14+
@Test void writePoint() {
15+
TWkbWriter writer = new TWkbWriter()
16+
Point p = new Point(111,-47)
17+
String expected = "E108018086CAA208FF859DC00300"
18+
String actual = writer.write(p)
19+
assertEquals expected, actual
20+
21+
byte[] expecteds = [-31, 8, 1, -128, -122, -54, -94, 8, -1, -123, -99, -64, 3, 0] as byte[]
22+
byte[] actuals = writer.writeBytes(p)
23+
assertArrayEquals(expecteds, actuals)
24+
}
25+
26+
@Test void writeLineString() {
27+
TWkbWriter writer = new TWkbWriter()
28+
LineString l = new LineString([[111.0, -47],[123.0, -48],[110.0, -47]])
29+
String expected = "E20801038086CAA208FF859DC0030080B8B872FFD9C40900FF91FD7B80DAC40900"
30+
String actual = writer.write(l)
31+
assertEquals expected, actual
32+
33+
byte[] expecteds = [-30, 8, 1, 3, -128, -122, -54, -94, 8, -1, -123, -99, -64, 3, 0, -128, -72, -72, 114, -1, -39, -60, 9, 0, -1, -111, -3, 123, -128, -38, -60, 9, 0] as byte[]
34+
byte[] actuals = writer.writeBytes(l)
35+
assertArrayEquals(expecteds, actuals)
36+
}
37+
38+
@Test void writeLinearRing() {
39+
TWkbWriter writer = new TWkbWriter()
40+
LinearRing l = new LinearRing([[111.0, -47],[123.0, -48],[110.0, -47],[111.0, -47]])
41+
String expected = "E20801048086CAA208FF859DC0030080B8B872FFD9C40900FF91FD7B80DAC4090080DAC4090000"
42+
String actual = writer.write(l)
43+
assertEquals expected, actual
44+
45+
byte[] expecteds = [-30, 8, 1, 4, -128, -122, -54, -94, 8, -1, -123, -99, -64, 3, 0, -128, -72, -72, 114, -1, -39, -60, 9, 0, -1, -111, -3, 123, -128, -38, -60, 9, 0, -128, -38, -60, 9, 0, 0] as byte[]
46+
byte[] actuals = writer.writeBytes(l)
47+
assertArrayEquals(expecteds, actuals)
48+
}
49+
50+
@Test void writePolygon() {
51+
TWkbWriter writer = new TWkbWriter()
52+
Polygon p = new Polygon(new LinearRing([1,1], [10,1], [10,10], [1,10], [1,1]),
53+
[
54+
new LinearRing([2,2], [4,2], [4,4], [2,4], [2,2]),
55+
new LinearRing([5,5], [6,5], [6,6], [5,6], [5,5])
56+
]
57+
)
58+
String expected = "E30801030580DAC40980DAC4090080AAEA5500000080AAEA5500FFA9EA55000000FFA9EA55000580DAC40980DAC4090080B4891300000080B4891300FFB38913000000FFB389130005808ECE1C808ECE1C0080DAC40900000080DAC40900FFD9C409000000FFD9C40900"
59+
String actual = writer.write(p)
60+
assertEquals expected, actual
61+
62+
byte[] expecteds = [-29, 8, 1, 3, 5, -128, -38, -60, 9, -128, -38, -60, 9, 0, -128, -86, -22, 85, 0, 0, 0, -128, -86, -22, 85, 0, -1, -87, -22, 85, 0, 0, 0, -1, -87, -22, 85, 0, 5, -128, -38, -60, 9, -128, -38, -60, 9, 0, -128, -76, -119, 19, 0, 0, 0, -128, -76, -119, 19, 0, -1, -77, -119, 19, 0, 0, 0, -1, -77, -119, 19, 0, 5, -128, -114, -50, 28, -128, -114, -50, 28, 0, -128, -38, -60, 9, 0, 0, 0, -128, -38, -60, 9, 0, -1, -39, -60, 9, 0, 0, 0, -1, -39, -60, 9, 0] as byte[]
63+
byte[] actuals = writer.writeBytes(p)
64+
assertArrayEquals(expecteds, actuals)
65+
}
66+
67+
@Test void writeMultiPoint() {
68+
TWkbWriter writer = new TWkbWriter()
69+
MultiPoint p = new MultiPoint([111,-47],[110,-46.5])
70+
String expected = "E40801028086CAA208FF859DC00300FFD9C40980ADE20400"
71+
String actual = writer.write(p)
72+
assertEquals expected, actual
73+
74+
byte[] expecteds = [-28, 8, 1, 2, -128, -122, -54, -94, 8, -1, -123, -99, -64, 3, 0, -1, -39, -60, 9, -128, -83, -30, 4, 0] as byte[]
75+
byte[] actuals = writer.writeBytes(p)
76+
assertArrayEquals(expecteds, actuals)
77+
}
78+
79+
@Test void writeMultiLineString() {
80+
TWkbWriter writer = new TWkbWriter()
81+
MultiLineString m = new MultiLineString(new LineString([1,2],[3,4]), new LineString([5,6],[7,8]))
82+
String expected = "E50801020280DAC40980B489130080B4891380B48913000280B4891380B489130080B4891380B4891300"
83+
String actual = writer.write(m)
84+
assertEquals expected, actual
85+
86+
byte[] expecteds = [-27, 8, 1, 2, 2, -128, -38, -60, 9, -128, -76, -119, 19, 0, -128, -76, -119, 19, -128, -76, -119, 19, 0, 2, -128, -76, -119, 19, -128, -76, -119, 19, 0, -128, -76, -119, 19, -128, -76, -119, 19, 0] as byte[]
87+
byte[] actuals = writer.writeBytes(m)
88+
assertArrayEquals(expecteds, actuals)
89+
}
90+
91+
@Test void writeMultiPolygon() {
92+
TWkbWriter writer = new TWkbWriter()
93+
MultiPolygon mp = new MultiPolygon([[[[1,2],[3,4],[5,6],[1,2]]], [[[7,8],[9,10],[11,12],[7,8]]]])
94+
String expected = "E6080102010480DAC40980B489130080B4891380B489130080B4891380B4891300FFE79226FFE79226000104809C9C39809C9C390080B4891380B489130080B4891380B4891300FFE79226FFE7922600"
95+
String actual = writer.write(mp)
96+
assertEquals expected, actual
97+
98+
byte[] expecteds = [-26, 8, 1, 2, 1, 4, -128, -38, -60, 9, -128, -76, -119, 19, 0, -128, -76, -119, 19, -128, -76, -119, 19, 0, -128, -76, -119, 19, -128, -76, -119, 19, 0, -1, -25, -110, 38, -1, -25, -110, 38, 0, 1, 4, -128, -100, -100, 57, -128, -100, -100, 57, 0, -128, -76, -119, 19, -128, -76, -119, 19, 0, -128, -76, -119, 19, -128, -76, -119, 19, 0, -1, -25, -110, 38, -1, -25, -110, 38, 0] as byte[]
99+
byte[] actuals = writer.writeBytes(mp)
100+
assertArrayEquals(expecteds, actuals)
101+
}
102+
103+
@Test void writeGeometryCollection() {
104+
TWkbWriter writer = new TWkbWriter()
105+
GeometryCollection gc = new GeometryCollection(new Point(100.0, 0.0), new LineString([101.0, 0.0], [102.0,1.0]))
106+
String expected = "E7080102E1080180A8D6B9070000E208010280829BC307000080DAC40980DAC40900"
107+
String actual = writer.write(gc)
108+
assertEquals expected, actual
109+
110+
byte[] expecteds = [-25, 8, 1, 2, -31, 8, 1, -128, -88, -42, -71, 7, 0, 0, -30, 8, 1, 2, -128, -126, -101, -61, 7, 0, 0, -128, -38, -60, 9, -128, -38, -60, 9, 0] as byte[]
111+
byte[] actuals = writer.writeBytes(gc)
112+
assertArrayEquals(expecteds, actuals)
113+
}
114+
115+
}

src/test/groovy/geoscript/geom/io/WritersTest.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class WritersTest {
1919
Writer writer = Writers.find("wkt")
2020
assertNotNull writer
2121

22+
writer = Writers.find("twkb")
23+
assertNotNull writer
24+
2225
writer = Writers.find("asdf")
2326
assertNull writer
2427
}

0 commit comments

Comments
 (0)