Skip to content

Commit

Permalink
WKBWriter: Support curved geometry types (#1104)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaston authored Jun 11, 2024
1 parent d0d3d5a commit 76d0e8f
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 53 deletions.
7 changes: 6 additions & 1 deletion include/geos/io/WKBConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ namespace WKBConstants {
wkbMultiPoint = 4,
wkbMultiLineString = 5,
wkbMultiPolygon = 6,
wkbGeometryCollection = 7
wkbGeometryCollection = 7,
wkbCircularString = 8,
wkbCompoundCurve = 9,
wkbCurvePolygon = 10,
wkbMultiCurve = 11,
wkbMultiSurface = 12,
};

enum wkbFlavour {
Expand Down
13 changes: 11 additions & 2 deletions include/geos/io/WKBWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ namespace geos {
namespace geom {

class CoordinateSequence;
class CompoundCurve;
class CurvePolygon;
class Geometry;
class GeometryCollection;
class Point;
Expand All @@ -43,6 +45,7 @@ class MultiPoint;
class MultiLineString;
class MultiPolygon;
class PrecisionModel;
class SimpleCurve;

} // namespace geom
} // namespace geos
Expand Down Expand Up @@ -194,6 +197,8 @@ class GEOS_DLL WKBWriter {
void writeHEX(const geom::Geometry& g, std::ostream& os);
// throws IOException, ParseException

static int getWkbType(const geom::Geometry&);

private:

// 2, 3, or 4
Expand All @@ -215,13 +220,17 @@ class GEOS_DLL WKBWriter {
void writePointEmpty(const geom::Point& p);
// throws IOException

void writeLineString(const geom::LineString& ls);
void writeSimpleCurve(const geom::SimpleCurve& ls);
// throws IOException

void writeCompoundCurve(const geom::CompoundCurve& curve);

void writePolygon(const geom::Polygon& p);
// throws IOException

void writeGeometryCollection(const geom::GeometryCollection& c, int wkbtype);
void writeCurvePolygon(const geom::CurvePolygon& p);

void writeGeometryCollection(const geom::GeometryCollection& gc);
// throws IOException, ParseException

void writeCoordinateSequence(const geom::CoordinateSequence& cs, bool sized);
Expand Down
132 changes: 95 additions & 37 deletions src/io/WKBWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include <geos/io/CheckOrdinatesFilter.h>
#include <geos/util/IllegalArgumentException.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/CompoundCurve.h>
#include <geos/geom/CurvePolygon.h>
#include <geos/geom/Point.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/LineString.h>
Expand Down Expand Up @@ -99,45 +101,28 @@ WKBWriter::writeHEX(const Geometry& g, std::ostream& os)
void
WKBWriter::write(const Geometry& g, std::ostream& os)
{
util::ensureNoCurvedComponents(g);

OrdinateSet inputOrdinates = OrdinateSet::createXY();
inputOrdinates.setM(g.hasM());
inputOrdinates.setZ(g.hasZ());
outputOrdinates = getOutputOrdinates(inputOrdinates);

outStream = &os;

if (const Point* x = dynamic_cast<const Point*>(&g)) {
return writePoint(*x);
}

if (const LineString* x = dynamic_cast<const LineString*>(&g)) {
return writeLineString(*x);
}

if (const Polygon* x = dynamic_cast<const Polygon*>(&g)) {
return writePolygon(*x);
}

if (const MultiPoint* x = dynamic_cast<const MultiPoint*>(&g)) {
return writeGeometryCollection(*x, WKBConstants::wkbMultiPoint);
switch(g.getGeometryTypeId()) {
case GEOS_POINT: writePoint(static_cast<const Point&>(g)); break;
case GEOS_LINESTRING:
case GEOS_LINEARRING:
case GEOS_CIRCULARSTRING: writeSimpleCurve(static_cast<const SimpleCurve&>(g)); break;
case GEOS_COMPOUNDCURVE: writeCompoundCurve(static_cast<const CompoundCurve&>(g)); break;
case GEOS_POLYGON: writePolygon(static_cast<const Polygon&>(g)); break;
case GEOS_CURVEPOLYGON: writeCurvePolygon(static_cast<const CurvePolygon&>(g)); break;
case GEOS_MULTIPOINT:
case GEOS_MULTILINESTRING:
case GEOS_MULTIPOLYGON:
case GEOS_MULTICURVE:
case GEOS_MULTISURFACE:
case GEOS_GEOMETRYCOLLECTION: writeGeometryCollection(static_cast<const GeometryCollection&>(g)); break;
}

if (const MultiLineString* x = dynamic_cast<const MultiLineString*>(&g)) {
return writeGeometryCollection(*x, WKBConstants::wkbMultiLineString);
}

if (const MultiPolygon* x = dynamic_cast<const MultiPolygon*>(&g)) {
return writeGeometryCollection(*x, WKBConstants::wkbMultiPolygon);
}

if (const GeometryCollection* x =
dynamic_cast<const GeometryCollection*>(&g)) {
return writeGeometryCollection(*x, WKBConstants::wkbGeometryCollection);
}

assert(0); // Unknown Geometry type
}

void
Expand Down Expand Up @@ -172,24 +157,45 @@ WKBWriter::writePoint(const Point& g)
}

void
WKBWriter::writeLineString(const LineString& g)
WKBWriter::writeSimpleCurve(const SimpleCurve& g)
{
writeByteOrder();

writeGeometryType(WKBConstants::wkbLineString, g.getSRID());
writeGeometryType(getWkbType(g), g.getSRID());
writeSRID(g.getSRID());

const CoordinateSequence* cs = g.getCoordinatesRO();
assert(cs);
writeCoordinateSequence(*cs, true);
}

void
WKBWriter::writeCompoundCurve(const CompoundCurve& g)
{
writeByteOrder();

writeGeometryType(getWkbType(g), g.getSRID());
writeSRID(g.getSRID());

writeInt(static_cast<int>(g.getNumCurves()));

auto orig_includeSRID = includeSRID;
includeSRID = false;

for (std::size_t i = 0; i < g.getNumCurves(); i++) {
const SimpleCurve& section = *g.getCurveN(i);
writeSimpleCurve(section);
}

includeSRID = orig_includeSRID;
}

void
WKBWriter::writePolygon(const Polygon& g)
{
writeByteOrder();

writeGeometryType(WKBConstants::wkbPolygon, g.getSRID());
writeGeometryType(getWkbType(g), g.getSRID());
writeSRID(g.getSRID());

if (g.isEmpty()) {
Expand Down Expand Up @@ -219,12 +225,42 @@ WKBWriter::writePolygon(const Polygon& g)
}

void
WKBWriter::writeGeometryCollection(const GeometryCollection& g,
int wkbtype)
WKBWriter::writeCurvePolygon(const CurvePolygon& g)
{
// Why not combine this with writePolygon?
// A CurvePolygon differs from a Polygon in that its rings
// can one of three different types. Therefore, the type
// information is written for each ring, unlike a Polygon.

writeByteOrder();

writeGeometryType(wkbtype, g.getSRID());
writeGeometryType(getWkbType(g), g.getSRID());

writeSRID(g.getSRID());

if (g.isEmpty()) {
writeInt(0);
return;
}

std::size_t nholes = g.getNumInteriorRing();
writeInt(static_cast<int>(nholes + 1));

const Curve* ring = g.getExteriorRing();
write(*ring, *outStream);

for(std::size_t i = 0; i < nholes; i++) {
ring = g.getInteriorRingN(i);
write(*ring, *outStream);
}
}

void
WKBWriter::writeGeometryCollection(const GeometryCollection& g)
{
writeByteOrder();

writeGeometryType(getWkbType(g), g.getSRID());
writeSRID(g.getSRID());

auto ngeoms = g.getNumGeometries();
Expand Down Expand Up @@ -381,6 +417,28 @@ WKBWriter::getOutputOrdinates(OrdinateSet ordinates)
return newOrdinates;
}

int
WKBWriter::getWkbType(const Geometry& g) {
switch(g.getGeometryTypeId()) {
case GEOS_POINT: return WKBConstants::wkbPoint;
case GEOS_LINESTRING:
case GEOS_LINEARRING: return WKBConstants::wkbLineString;
case GEOS_CIRCULARSTRING: return WKBConstants::wkbCircularString;
case GEOS_COMPOUNDCURVE: return WKBConstants::wkbCompoundCurve;
case GEOS_POLYGON: return WKBConstants::wkbPolygon;
case GEOS_CURVEPOLYGON: return WKBConstants::wkbCurvePolygon;
case GEOS_MULTIPOINT: return WKBConstants::wkbMultiPoint;
case GEOS_MULTILINESTRING: return WKBConstants::wkbMultiLineString;
case GEOS_MULTICURVE: return WKBConstants::wkbMultiCurve;
case GEOS_MULTIPOLYGON: return WKBConstants::wkbMultiPolygon;
case GEOS_MULTISURFACE: return WKBConstants::wkbMultiSurface;
case GEOS_GEOMETRYCOLLECTION: return WKBConstants::wkbGeometryCollection;
}

// Avoid -Wreturn-type warning
throw util::IllegalArgumentException("Invalid geometry type.");
}


} // namespace geos.io
} // namespace geos
Expand Down
13 changes: 0 additions & 13 deletions tests/unit/capi/GEOSWKBWriterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,18 +186,5 @@ void object::test<9>()
ensure_equals(hexstr, "010100008000000000000008400000000000002040000000000000F03F");
}

template<>
template<>
void object::test<10>
()
{
input_ = fromWKT("CIRCULARSTRING (0 0, 1 1, 2 0)");
ensure(input_);

std::size_t hex_size = 0;
buf_ = GEOSWKBWriter_writeHEX(wkbwriter_, input_, &hex_size);
ensure("curved geometry not supported", buf_ == nullptr);
}

} // namespace tut

102 changes: 102 additions & 0 deletions tests/unit/io/WKBWriterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ struct test_wkbwriter_data {
wkbreader(*gf)
{}

void checkHexOutput(const std::string& wkt, const std::string& hex) {
std::stringstream out;
auto geom = wktreader.read(wkt);
wkbwriter.writeHEX(*geom, out);
ensure_equals(wkt, out.str(), hex );
}

void checkHexOutputWithSRID(const std::string& wkt, int srid, const std::string& hex) {
std::stringstream out;
auto geom = wktreader.read(wkt);
geom->setSRID(srid);
wkbwriter.setIncludeSRID(true);
wkbwriter.writeHEX(*geom, out);
ensure_equals(wkt, out.str(), hex );
}

};

typedef test_group<test_wkbwriter_data> group;
Expand Down Expand Up @@ -375,5 +391,91 @@ void object::test<12>
"010200004002000000000000000000F03F00000000000000400000000000000840000000000000104000000000000014400000000000001840");
}

// CircularString
template<>
template<>
void object::test<13>
()
{
checkHexOutput("CIRCULARSTRING EMPTY",
"010800000000000000");

checkHexOutput("CIRCULARSTRING (1 3, 2 4, 3 1)",
"010800000003000000000000000000F03F0000000000000840000000000000004000000000000010400000000000000840000000000000F03F");
}

// CompoundCurve
template<>
template<>
void object::test<14>
()
{
checkHexOutput("COMPOUNDCURVE EMPTY",
"010900000000000000");

checkHexOutput("COMPOUNDCURVE (CIRCULARSTRING (1 3, 2 4, 3 1), (3 1, 0 0))",
"010900000002000000010800000003000000000000000000F03F0000000000000840000000000000004000000000000010400000000000000840000000000000F03F0102000000020000000000000000000840000000000000F03F00000000000000000000000000000000");
}

// CurvePolygon
template<>
template<>
void object::test<15>
()
{
checkHexOutput("CURVEPOLYGON EMPTY",
"010A00000000000000");

checkHexOutput("CURVEPOLYGON( COMPOUNDCURVE( CIRCULARSTRING(0 0,2 0, 2 1, 2 3, 4 3), (4 3, 4 5, 1 4, 0 0)), CIRCULARSTRING(1.7 1, 1.4 0.4, 1.6 0.4, 1.6 0.5, 1.7 1) )",
"010A0000000200000001090000000200000001080000000500000000000000000000000000000000000000000000000000004000000000000000000000000000000040000000000000F03F00000000000000400000000000000840000000000000104000000000000008400102000000040000000000000000001040000000000000084000000000000010400000000000001440000000000000F03F000000000000104000000000000000000000000000000000010800000005000000333333333333FB3F000000000000F03F666666666666F63F9A9999999999D93F9A9999999999F93F9A9999999999D93F9A9999999999F93F000000000000E03F333333333333FB3F000000000000F03F");
}

// MultiCurve
template<>
template<>
void object::test<16>
()
{
checkHexOutput("MULTICURVE EMPTY",
"010B00000000000000");

checkHexOutput("MULTICURVE( (0 0, 5 5), COMPOUNDCURVE( (-1 -1, 0 0), CIRCULARSTRING (0 0, 1 1, 2 0)), CIRCULARSTRING(4 0, 4 4, 8 4))",
"010B000000030000000102000000020000000000000000000000000000000000000000000000000014400000000000001440010900000002000000010200000002000000000000000000F0BF000000000000F0BF0000000000000000000000000000000001080000000300000000000000000000000000000000000000000000000000F03F000000000000F03F00000000000000400000000000000000010800000003000000000000000000104000000000000000000000000000001040000000000000104000000000000020400000000000001040" );
}

// MultiSurface
template<>
template<>
void object::test<17>
()
{
checkHexOutput("MULTISURFACE EMPTY",
"010C00000000000000");

checkHexOutput("MULTISURFACE( CURVEPOLYGON( CIRCULARSTRING( 0 0, 4 0, 4 4, 0 4, 0 0), (1 1, 3 3, 3 1, 1 1)), POLYGON ((10 10, 14 12, 11 10, 10 10), (11 11, 11.5 11, 11 11.5, 11 11)))",
"010C00000002000000010A000000020000000108000000050000000000000000000000000000000000000000000000000010400000000000000000000000000000104000000000000010400000000000000000000000000000104000000000000000000000000000000000010200000004000000000000000000F03F000000000000F03F000000000000084000000000000008400000000000000840000000000000F03F000000000000F03F000000000000F03F01030000000200000004000000000000000000244000000000000024400000000000002C40000000000000284000000000000026400000000000002440000000000000244000000000000024400400000000000000000026400000000000002640000000000000274000000000000026400000000000002640000000000000274000000000000026400000000000002640");
}

// CompoundCurve with SRID
template<>
template<>
void object::test<18>
()
{
checkHexOutputWithSRID("COMPOUNDCURVE (CIRCULARSTRING (1 3, 2 4, 3 1), (3 1, 0 0))", 5646,
"01090000200E16000002000000010800000003000000000000000000F03F0000000000000840000000000000004000000000000010400000000000000840000000000000F03F0102000000020000000000000000000840000000000000F03F00000000000000000000000000000000");
}

// CurvePolygon with SRID
template<>
template<>
void object::test<19>
()
{
checkHexOutputWithSRID("CURVEPOLYGON( COMPOUNDCURVE( CIRCULARSTRING(0 0,2 0, 2 1, 2 3, 4 3), (4 3, 4 5, 1 4, 0 0)), CIRCULARSTRING(1.7 1, 1.4 0.4, 1.6 0.4, 1.6 0.5, 1.7 1) )", 5646,
"010A0000200E1600000200000001090000000200000001080000000500000000000000000000000000000000000000000000000000004000000000000000000000000000000040000000000000F03F00000000000000400000000000000840000000000000104000000000000008400102000000040000000000000000001040000000000000084000000000000010400000000000001440000000000000F03F000000000000104000000000000000000000000000000000010800000005000000333333333333FB3F000000000000F03F666666666666F63F9A9999999999D93F9A9999999999F93F9A9999999999D93F9A9999999999F93F000000000000E03F333333333333FB3F000000000000F03F");
}


} // namespace tut

0 comments on commit 76d0e8f

Please sign in to comment.