Why is boost multipolygon is rotated upside down?
I have the obtained the WKT string of a boost multipolygon which is constructed by union of boost polygons in a vector called vectorPolygons
std::vector<boost::geometry::Polygon> vectorPolygons;
After adding data in vectorPolygons below code computes its union and then extracts the WKT of the multi polygon
if (!vectorPolygons.empty())
{
multi_polygon boost_multipolygon; // Will store union of polygons
// Create the union of all the polygons
for (const boost::geometry::Polygon& p : vectorPolygons) {
// add another polygon each iteration
multi_polygon tmp_poly;
boost::geometry::union_(boost_multipolygon, p, tmp_poly);
boost_multipolygon= tmp_poly;
boost::geometry::clear(tmp_poly);
}
std::string validity_reason;
bool valid = boost::geometry::is_valid(boost_multipolygon, validity_reason);
if (!valid)
{
boost::geometry::correct(boost_multipolygon);
}
std::stringstream ss;
ss << boost::geometry::wkt(boost_multipolygon);
std::string wkt = ss.str();
}
The WKT string comes out as below
MULTIPOLYGON(((40000 30000,40000 -0,30000 -0,30000 10000,20000 10000,20000 20000,10000 20000,0 20000,0 30000,40000 30000)))
When i display this multipolygon by using the code example below then I am not sure why its display is always rotated upside down. In reality it should have been exactly upside down of what it displays.
I have tried to use boost::geometry::correct
on the boost multipolygon before extracting the WKT also but still the display is always rotated. What is that i am doing wrong?
Here is the code which is trying to display the WKT generated above and the output is upside down
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/geometries/adapted/boost_polygon.hpp>
#include <vector>
namespace boost {
namespace geometry {
typedef model::d2::point_xy<double> point;
typedef model::polygon<point> polygon;
}
}
using multi_polygon = boost::geometry::model::multi_polygon<boost::geometry::polygon>;
namespace bg = boost::geometry;
int main()
{
// Specify the basic type
typedef boost::geometry::model::d2::point_xy<double> point_type;
multi_polygon b;
boost::geometry::read_wkt("MULTIPOLYGON(((40000 30000,40000 -0,30000 -0,30000 10000,20000 10000,20000 20000,10000 20000,0 20000,0 30000,40000 30000)))", b);
boost::geometry::correct(b);
// Declare a stream and an SVG mapper
std::ofstream svg("my_map.svg");
boost::geometry::svg_mapper<point_type> mapper(svg, 500, 500);
// Add geometries such that all these geometries fit on the map
mapper.add(b);
// Draw the geometries on the SVG map, using a specific SVG style
mapper.map(b, "fill-opacity:0.3;fill:rgb(51,51,153);stroke:rgb(51,51,153);stroke-width:2");
// Destructor of map will be called - adding </svg>
// Destructor of stream will be called, closing the file
return 0;
}
There is no "right side up". Cartesian coordinate systems are just that: coordinate systems. How you map them on a visual projection is your choice.
Here's the output on my system:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <fstream>
#include <iostream>
namespace bg = boost::geometry;
int main()
{
using Point = bg::model::d2::point_xy<double>;
using Poly = bg::model::polygon<Point>;
bg::model::multi_polygon<Poly> p;
bg::read_wkt("MULTIPOLYGON(((40 30,40 -0,30 -0,30 10,20 10,20 20,10 20,0 20,0 30,40 30)))",
p);
{
std::ofstream svg("my_map.svg");
bg::svg_mapper<Point> mapper(svg, 400, 400);
mapper.add(p);
mapper.map(p, "fill-opacity:0.3;fill:rgb(51,51,153);stroke:rgb(51,51,153);stroke-width:1");
}
}
Shows as
So you can see that axes grow down/to the right. This agrees with the first random online WKT renderer I could find:
By that count I'd rate the output as "correct".
Flipping It?
You could just manually flip the system:
for (auto& pt : make_iterator_range(bg::points_begin(p), bg::points_end(p))) {
pt.x(pt.x() * -1);
pt.y(pt.y() * -1);
}
Result
A more versatile approach is to use a transform that can scale, rotate and translate over arbitrary distances/amounts: https://www.boost.org/doc/libs/1_78_0/libs/geometry/doc/html/geometry/reference/algorithms/transform/transform_3_with_strategy.html
UPDATE
I have updated the example with actual data at coliru.stacked-crooked.com/a/2ba8df83a1ecce91 . The code you shared marks my input as invalid and boost::geometry::correct operates on it and that rotates the output. Is it not possible to retain the input so that output is not rotated? – Test 16 hours ago
Taking it point by point: "Is it not possible to retain the input" - yes, it is always possible to retain invalid input, and you should expect to retain invalid output.
However, you didn't say what was being corrected. I ran my code with your data:
POLYGON((30 10,30 -0,40 -0,40 10,30 10)): -100
Correcting source poly: Geometry has wrong orientation
Union: MULTIPOLYGON(((30 10,40 10,40 -0,30 -0,30 10))): 100
POLYGON((30 20,30 10,40 10,40 20,30 20)): -100
Correcting source poly: Geometry has wrong orientation
Union: MULTIPOLYGON(((40 -0,30 -0,30 10,30 20,40 20,40 -0))): 200
POLYGON((30 30,30 20,40 20,40 30,30 30)): -100
Correcting source poly: Geometry has wrong orientation
Union: MULTIPOLYGON(((40 -0,30 -0,30 20,30 30,40 30,40 -0))): 300
POLYGON((20 20,20 10,30 10,30 20,20 20)): -100
Correcting source poly: Geometry has wrong orientation
Union: MULTIPOLYGON(((30 20,30 30,40 30,40 -0,30 -0,30 10,20 10,20 20,30 20))): 400
POLYGON((20 30,20 20,30 20,30 30,20 30)): -100
Correcting source poly: Geometry has wrong orientation
Union: MULTIPOLYGON(((40 30,40 -0,30 -0,30 10,20 10,20 20,20 30,40 30))): 500
POLYGON((10 30,10 20,20 20,20 30,10 30)): -100
Correcting source poly: Geometry has wrong orientation
Union: MULTIPOLYGON(((40 30,40 -0,30 -0,30 10,20 10,20 20,10 20,10 30,40 30))): 600
POLYGON((0 30,0 20,10 20,10 30,0 30)): -100
Correcting source poly: Geometry has wrong orientation
Union: MULTIPOLYGON(((40 30,40 -0,30 -0,30 10,20 10,20 20,10 20,0 20,0 30,40 30))): 700
The important bit: "Correcting source poly: Geometry has wrong orientation".
In a way, your data is not invalid, it's just invalid for the chosen geometry type! You could simply change the orientation for your polygon type:
using Poly = bg::model::polygon<Point, false>;
Without any further changes you get: http://coliru.stacked-crooked.com/a/f02e56fc2402112d
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <fstream>
#include <iostream>
namespace bg = boost::geometry;
template <typename T> auto from_wkt(std::string const& wkt) {
T result;
bg::read_wkt(wkt, result);
return result;
}
template <typename T> void check(T& geo, std::string_view label) {
for (std::string reason; !bg::is_valid(geo, reason); bg::correct(geo)) {
std::cout << "Correcting " << label << ": " << reason << "\n";
}
}
int main()
{
using Point = bg::model::d2::point_xy<double>;
using Poly = bg::model::polygon<Point, false>;
using MPoly = bg::model::multi_polygon<Poly>;
std::vector vectorPolygons{
from_wkt<Poly>(R"(POLYGON(( 30 10, 30 -0, 40 -0, 40 10, 30 10 )))"),
from_wkt<Poly>(R"(POLYGON(( 30 20, 30 10, 40 10, 40 20, 30 20 )))"),
from_wkt<Poly>(R"(POLYGON(( 30 30, 30 20, 40 20, 40 30, 30 30 )))"),
from_wkt<Poly>(R"(POLYGON(( 20 20, 20 10, 30 10, 30 20, 20 20 )))"),
from_wkt<Poly>(R"(POLYGON(( 20 30, 20 20, 30 20, 30 30, 20 30 )))"),
from_wkt<Poly>(R"(POLYGON(( 10 30, 10 20, 20 20, 20 30, 10 30 )))"),
from_wkt<Poly>(R"(POLYGON(( 0 30, 0 20, 10 20, 10 30, 0 30 )))"),
};
MPoly union_poly; // will store union of polygons
for (auto& p : vectorPolygons) {
std::cout << bg::wkt(p) << ": " << bg::area(p) << "\n";
check(p, "source poly");
MPoly tmp;
boost::geometry::union_(union_poly, p, tmp);
union_poly.swap(tmp);
//check(union_poly, "union");
std::cout << "Union: " << bg::wkt(union_poly) << ": "
<< bg::area(union_poly) << "\n";
}
{
std::ofstream svg("my_map.svg");
bg::svg_mapper<Point> mapper(svg, 400, 400);
mapper.add(union_poly);
mapper.map(union_poly,
"fill-opacity:0.1;fill:rgb(51,51,153);stroke:rgb(51,51,153);"
"stroke-width:0");
for (auto const& p : vectorPolygons) {
mapper.add(p);
mapper.map(
p,
"fill-opacity:0.3;fill:rgb(77,77,77);stroke:rgb(77,77,77);"
"stroke-width:1;stroke-dasharray:1 2");
}
}
}
Printing
POLYGON((30 10,30 -0,40 -0,40 10,30 10)): 100
Union: MULTIPOLYGON(((30 10,30 -0,40 -0,40 10,30 10))): 100
POLYGON((30 20,30 10,40 10,40 20,30 20)): 100
Union: MULTIPOLYGON(((40 -0,40 20,30 20,30 10,30 -0,40 -0))): 200
POLYGON((30 30,30 20,40 20,40 30,30 30)): 100
Union: MULTIPOLYGON(((40 -0,40 30,30 30,30 20,30 -0,40 -0))): 300
POLYGON((20 20,20 10,30 10,30 20,20 20)): 100
Union: MULTIPOLYGON(((30 20,20 20,20 10,30 10,30 -0,40 -0,40 30,30 30,30 20))): 400
POLYGON((20 30,20 20,30 20,30 30,20 30)): 100
Union: MULTIPOLYGON(((40 30,20 30,20 20,20 10,30 10,30 -0,40 -0,40 30))): 500
POLYGON((10 30,10 20,20 20,20 30,10 30)): 100
Union: MULTIPOLYGON(((40 30,10 30,10 20,20 20,20 10,30 10,30 -0,40 -0,40 30))): 600
POLYGON((0 30,0 20,10 20,10 30,0 30)): 100
Union: MULTIPOLYGON(((40 30,0 30,0 20,10 20,20 20,20 10,30 10,30 -0,40 -0,40 30))): 700
Summary
Moral of the story: always check that your input data is valid.
Also, please note, NONE of these ever "rotated" the output. The output was simply undefined and not what you wanted (it was more akin to the "negative complement" of the shape you were looking for, but that was actually accidental and the result was unspecified because the input violated the pre-conditions).