Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WKBWriter: Support curved geometry types #1104

Merged
merged 1 commit into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

Loading