diff --git a/include/geos/io/WKBConstants.h b/include/geos/io/WKBConstants.h index 51d9615f45..438574841b 100644 --- a/include/geos/io/WKBConstants.h +++ b/include/geos/io/WKBConstants.h @@ -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 { diff --git a/include/geos/io/WKBWriter.h b/include/geos/io/WKBWriter.h index 0910f52f5a..90b2fbc672 100644 --- a/include/geos/io/WKBWriter.h +++ b/include/geos/io/WKBWriter.h @@ -33,6 +33,8 @@ namespace geos { namespace geom { class CoordinateSequence; +class CompoundCurve; +class CurvePolygon; class Geometry; class GeometryCollection; class Point; @@ -43,6 +45,7 @@ class MultiPoint; class MultiLineString; class MultiPolygon; class PrecisionModel; +class SimpleCurve; } // namespace geom } // namespace geos @@ -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 @@ -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); diff --git a/src/io/WKBWriter.cpp b/src/io/WKBWriter.cpp index 0e622b0d49..f7c83d38dd 100644 --- a/src/io/WKBWriter.cpp +++ b/src/io/WKBWriter.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -99,8 +101,6 @@ 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()); @@ -108,36 +108,21 @@ WKBWriter::write(const Geometry& g, std::ostream& os) outStream = &os; - if (const Point* x = dynamic_cast(&g)) { - return writePoint(*x); - } - - if (const LineString* x = dynamic_cast(&g)) { - return writeLineString(*x); - } - - if (const Polygon* x = dynamic_cast(&g)) { - return writePolygon(*x); - } - - if (const MultiPoint* x = dynamic_cast(&g)) { - return writeGeometryCollection(*x, WKBConstants::wkbMultiPoint); + switch(g.getGeometryTypeId()) { + case GEOS_POINT: writePoint(static_cast(g)); break; + case GEOS_LINESTRING: + case GEOS_LINEARRING: + case GEOS_CIRCULARSTRING: writeSimpleCurve(static_cast(g)); break; + case GEOS_COMPOUNDCURVE: writeCompoundCurve(static_cast(g)); break; + case GEOS_POLYGON: writePolygon(static_cast(g)); break; + case GEOS_CURVEPOLYGON: writeCurvePolygon(static_cast(g)); break; + case GEOS_MULTIPOINT: + case GEOS_MULTILINESTRING: + case GEOS_MULTIPOLYGON: + case GEOS_MULTICURVE: + case GEOS_MULTISURFACE: + case GEOS_GEOMETRYCOLLECTION: writeGeometryCollection(static_cast(g)); break; } - - if (const MultiLineString* x = dynamic_cast(&g)) { - return writeGeometryCollection(*x, WKBConstants::wkbMultiLineString); - } - - if (const MultiPolygon* x = dynamic_cast(&g)) { - return writeGeometryCollection(*x, WKBConstants::wkbMultiPolygon); - } - - if (const GeometryCollection* x = - dynamic_cast(&g)) { - return writeGeometryCollection(*x, WKBConstants::wkbGeometryCollection); - } - - assert(0); // Unknown Geometry type } void @@ -172,11 +157,11 @@ 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(); @@ -184,12 +169,33 @@ WKBWriter::writeLineString(const LineString& g) writeCoordinateSequence(*cs, true); } +void +WKBWriter::writeCompoundCurve(const CompoundCurve& g) +{ + writeByteOrder(); + + writeGeometryType(getWkbType(g), g.getSRID()); + writeSRID(g.getSRID()); + + writeInt(static_cast(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()) { @@ -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(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(); @@ -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 diff --git a/tests/unit/capi/GEOSWKBWriterTest.cpp b/tests/unit/capi/GEOSWKBWriterTest.cpp index 1bb95c8081..2f3e041d66 100644 --- a/tests/unit/capi/GEOSWKBWriterTest.cpp +++ b/tests/unit/capi/GEOSWKBWriterTest.cpp @@ -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 diff --git a/tests/unit/io/WKBWriterTest.cpp b/tests/unit/io/WKBWriterTest.cpp index eb4cf31024..ff31b7f0e4 100644 --- a/tests/unit/io/WKBWriterTest.cpp +++ b/tests/unit/io/WKBWriterTest.cpp @@ -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 group; @@ -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