#include #include #include #include #include "utils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include // stl #include #if 0 // FIXME struct output_geometry_backend { output_geometry_backend(mapnik::geometry_container & paths, mapnik::geometry_type::types type) : paths_(paths), type_(type) {} template void add_path(T & path) { mapnik::vertex2d vtx(mapnik::vertex2d::no_init); path.rewind(0); std::unique_ptr geom_ptr(new mapnik::geometry_type(type_)); while ((vtx.cmd = path.vertex(&vtx.x, &vtx.y)) != mapnik::SEG_END) { //std::cerr << vtx.x << "," << vtx.y << " cmd=" << vtx.cmd << std::endl; geom_ptr->push_vertex(vtx.x, vtx.y, (mapnik::CommandType)vtx.cmd); } paths_.push_back(geom_ptr.release()); } mapnik::geometry_container & paths_; mapnik::geometry_type::types type_; }; boost::optional linestring_bbox_clipping(mapnik::box2d bbox, std::string wkt_in) { using namespace mapnik; agg::trans_affine tr; projection src(MAPNIK_LONGLAT_PROJ); projection dst(MAPNIK_LONGLAT_PROJ); proj_transform prj_trans(src,dst); line_symbolizer sym; view_transform t(bbox.width(),bbox.height(), bbox); mapnik::geometry_container output_paths; output_geometry_backend backend(output_paths, mapnik::new_geometry::geometry_types::LineString); mapnik::context_ptr ctx = std::make_shared(); mapnik::feature_impl f(ctx,0); vertex_converter converter(bbox, backend, sym, t, prj_trans, tr, f, attributes(), 1.0); converter.set(); mapnik::geometry_container p; if (!mapnik::from_wkt(wkt_in , p)) { throw std::runtime_error("Failed to parse WKT"); } for (geometry_type const& geom : p) { vertex_adapter va(geom); converter.apply(va); } using sink_type = std::back_insert_iterator; std::string wkt; sink_type sink(wkt); static const mapnik::wkt::wkt_multi_generator generator; if (boost::spirit::karma::generate(sink, generator, output_paths)) { return boost::optional(wkt); } return boost::optional(); } boost::optional polygon_bbox_clipping(mapnik::box2d bbox, std::string wkt_in) { using namespace mapnik; agg::trans_affine tr; projection src(MAPNIK_LONGLAT_PROJ); projection dst(MAPNIK_LONGLAT_PROJ); proj_transform prj_trans(src,dst); polygon_symbolizer sym; view_transform t(bbox.width(),bbox.height(), bbox); mapnik::geometry_container output_paths; output_geometry_backend backend(output_paths, mapnik::new_geometry::geometry_types::Polygon); mapnik::context_ptr ctx = std::make_shared(); mapnik::feature_impl f(ctx,0); vertex_converter converter(bbox, backend, sym, t, prj_trans, tr, f, attributes(), 1.0); converter.set(); mapnik::geometry_container p; if (!mapnik::from_wkt(wkt_in , p)) { throw std::runtime_error("Failed to parse WKT"); } for (geometry_type const& geom : p) { vertex_adapter va(geom); converter.apply(va); } using sink_type = std::back_insert_iterator; std::string wkt; // Use Python String directly ? sink_type sink(wkt); static const mapnik::wkt::wkt_multi_generator generator; if (boost::spirit::karma::generate(sink, generator, output_paths)) { return boost::optional(wkt); } return boost::optional(); } #endif int main(int argc, char** argv) { std::vector args; for (int i=1;i result = linestring_bbox_clipping(mapnik::box2d(50,50,150,150),wkt_in); BOOST_TEST(result); BOOST_TEST_EQ(*result,std::string("LineString(50 50,150 150)")); } // Polygon/bbox clipping { std::string wkt_in("Polygon((50 50,150 50,150 150,50 150,50 50))"); boost::optional result = polygon_bbox_clipping(mapnik::box2d(50,50,150,150),wkt_in); BOOST_TEST(result); // TODO - the extra 50 50 is not ideal, but we enforce this result for now to prevent // regressions and because we don't have actionable solution to drop extra 50 50 BOOST_TEST_EQ(*result,std::string("Polygon((50 50,150 50,150 150,50 150,50 50,50 50))")); // below is ideal, but not current result //BOOST_TEST_EQ(*result,std::string("Polygon((50 50,150 50,150 150,50 150,50 50))")); } { std::string wkt_in("Polygon((60 60,140 60,140 160,60 140,60 60))"); boost::optional result = polygon_bbox_clipping(mapnik::box2d(50,50,150,150),wkt_in); BOOST_TEST(result); BOOST_TEST_EQ(*result,std::string("Polygon((60 60,140 60,140 150,100 150,60 140,60 60,60 60))")); //BOOST_TEST_EQ(*result,std::string("Polygon((60 60,140 60,140 160,60 140,60 60))")); } { std::string wkt_in("Polygon((0 0,10 0,10 10,0 10,0 0))"); boost::optional result = polygon_bbox_clipping(mapnik::box2d(50,50,150,150),wkt_in); BOOST_TEST(result); // TODO - this is completely wrong: should not have )) and ideally should be EMPTY BOOST_TEST_EQ(*result, std::string("Polygon())")); } { std::string wkt_in("Polygon((0 0,100 200,200 0,0 0 ))"); boost::optional result = polygon_bbox_clipping(mapnik::box2d(50,50,150,150),wkt_in); BOOST_TEST(result); BOOST_TEST_EQ(*result, std::string("Polygon((50 50,50 100,75 150,125 150,150 100,150 50))")); //BOOST_TEST_EQ(*result,std::string("Polygon((50 50,50 100,75 150,125 150,150 100,150 50,50 50))")); } #endif } catch (std::exception const & ex) { std::clog << ex.what() << "\n"; BOOST_TEST(false); } if (!::boost::detail::test_errors()) { if (quiet) std::clog << "\x1b[1;32m.\x1b[0m"; else std::clog << "C++ geometry conversions: \x1b[1;32m✓ \x1b[0m\n"; ::boost::detail::report_errors_remind().called_report_errors_function = true; } else { return ::boost::report_errors(); } }