diff --git a/extensions/example/io/Jamfile b/extensions/example/io/Jamfile new file mode 100644 index 0000000000..a78ef49064 --- /dev/null +++ b/extensions/example/io/Jamfile @@ -0,0 +1,13 @@ +# Boost.Geometry (aka GGL, Generic Geometry Library) +# +# Copyright (c) 2020 Baidyanath Kundu, Haldia, India. + +# Use, modification and distribution is subject to the Boost Software License, +# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +project boost-geometry-extensions-io + : #requirements + ; + +exe plotly_example : plotly_example.cpp ; \ No newline at end of file diff --git a/extensions/example/io/plotly_example.cpp b/extensions/example/io/plotly_example.cpp new file mode 100644 index 0000000000..762cbc7894 --- /dev/null +++ b/extensions/example/io/plotly_example.cpp @@ -0,0 +1,42 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// QuickBook Example + +// Copyright (c) 2013 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +namespace bg = boost::geometry; + +int main() +{ + typedef bg::model::point> point_t; + typedef bg::model::polygon polygon_t; + + polygon_t poly1; + + bg::append(poly1.outer(), point_t(0.0, 0.0)); + bg::append(poly1.outer(), point_t(0.0, 5.0)); + bg::append(poly1.outer(), point_t(5.0, 5.0)); + bg::append(poly1.outer(), point_t(5.0, 0.0)); + bg::append(poly1.outer(), point_t(0.0, 0.0)); + + poly1.inners().resize(1); + bg::append(poly1.inners()[0], point_t(1.0, 1.0)); + bg::append(poly1.inners()[0], point_t(4.0, 1.0)); + bg::append(poly1.inners()[0], point_t(4.0, 4.0)); + bg::append(poly1.inners()[0], point_t(1.0, 4.0)); + bg::append(poly1.inners()[0], point_t(1.0, 1.0)); + + bg::plotly_plotter plotter("my_plot.json"); + plotter.plot(poly1, bg::plotly_color(255, 39, 0)); + + return 0; +} \ No newline at end of file diff --git a/include/boost/geometry/extensions/io/plotly/plotter.hpp b/include/boost/geometry/extensions/io/plotly/plotter.hpp new file mode 100644 index 0000000000..7c0b706a7e --- /dev/null +++ b/include/boost/geometry/extensions/io/plotly/plotter.hpp @@ -0,0 +1,509 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2020 Baidyanath Kundu, Haldia, India. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_EXTENSIONS_IO_PLOT_LY_PLOTTER_HPP +#define BOOST_GEOMETRY_EXTENSIONS_IO_PLOT_LY_PLOTTER_HPP + +#include + +#include + +namespace boost { namespace geometry +{ + +class plotly_color +{ +public: + inline plotly_color(int r, int g, int b) + : m_r(r), m_g(g), m_b(b) + {} + + inline std::string rgb() + { + return "rgb(" + + boost::lexical_cast(m_r) + + "," + + boost::lexical_cast(m_g) + + "," + + boost::lexical_cast(m_b) + + ")"; + } + + inline std::string rgba(float a) + { + return "rgba(" + + boost::lexical_cast(m_r) + + "," + + boost::lexical_cast(m_g) + + "," + + boost::lexical_cast(m_b) + + "," + + boost::lexical_cast(a) + + ")"; + } +private: + int m_r; + int m_g; + int m_b; +}; + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace plotly +{ + +template +struct stream_coordinate +{ + static inline void apply(std::vector& ss, P const& p, bool first) + { + stream_coordinate::apply(ss, p, first); + } +}; + +template +struct stream_coordinate +{ + static inline void apply(std::vector& ss, P const& p, bool first) + { + ss[2] += (first ? T::d3() : ", \"") + boost::lexical_cast(get<2>(p)) + "\""; + stream_coordinate::apply(ss, p, first); + } +}; + +template +struct stream_coordinate +{ + static inline void apply(std::vector& ss, P const& p, bool first) + { + ss[1] += (first ? T::d2() : ", \"") + boost::lexical_cast(get<1>(p)) + "\""; + stream_coordinate::apply(ss, p, first); + } +}; + +template +struct stream_coordinate +{ + static inline void apply(std::vector& ss, P const& p, bool first) + { + ss[0] += (first ? T::d1() : ", \"") + boost::lexical_cast(get<0>(p)) + "\""; + } +}; + + +struct type_cartesian_2 +{ + static inline const char* plot_type() { return "scatter"; } + static inline const char* fill_type() { return "tozerox"; } + static inline const char* d1() { return "\"x\": [\""; } + static inline const char* d2() { return "\"y\": [\""; } +}; + + +struct type_cartesian_3 +{ + static inline const char* plot_type() { return "scatter3d"; } + static inline const char* fill_type() { return "tozerox"; } + static inline const char* d1() { return "\"x\": [\""; } + static inline const char* d2() { return "\"y\": [\""; } + static inline const char* d3() { return "\"z\": [\""; } +}; + +struct type_geographic +{ + static inline const char* plot_type() { return "scattermapbox"; } + static inline const char* fill_type() { return "toself"; } + static inline const char* d1() { return "\"lat\": [\""; } + static inline const char* d2() { return "\"lon\": [\""; } +}; + +/*! +\brief Stream points as plotly style json +*/ +template +< + typename Point, + int Dimension, + typename PlotType +> +struct plotly_point +{ + template + static inline void apply(std::basic_ostream& os, + Point const& p, boost::geometry::plotly_color& c) + { + os << "{ \"mode\": \"lines\", \"type\": \"" + << PlotType::plot_type() + << "\""; + + std::vector ss(Dimension); + stream_coordinate::apply(ss, p, true); + + for(std::vector::iterator it=ss.begin(); + it!=ss.end(); it++) + { + os << ", " << *it << "]"; + } + + os << ", \"marker\": {\"color\": \"" << c.rgb() << "\"}, \"showlegend\": false}"; + } +}; + +template +< + typename Segment, + int Dimension, + typename PlotType +> +struct plotly_segment +{ + typedef typename point_type::type point_type; + + template + static inline void apply(std::basic_ostream& os, + Segment const& segment, boost::geometry::plotly_color& c) + { + os << "{ \"mode\": \"lines\", \"type\": \"" + << PlotType::plot_type() + << "\""; + + typedef boost::array sequence; + + sequence points; + geometry::detail::assign_point_from_index<0>(segment, points[0]); + geometry::detail::assign_point_from_index<1>(segment, points[1]); + + std::vector ss(Dimension); + stream_coordinate::apply(ss, points[0], true); + stream_coordinate::apply(ss, points[1], false); + + for(std::vector::iterator it=ss.begin(); + it!=ss.end(); it++) + { + os << ", " << *it << "]"; + } + + os << ", \"marker\": {\"color\": \"" << c.rgb() + << "\"}, \"line\": {\"dash\": \"solid\", \"color\": \"" << c.rgb() << "\", \"width\": 2}, \"showlegend\": false}"; + } +}; + +template +< + typename Range, + int Dimension, + typename PlotType +> +struct plotly_range +{ + template + static inline void apply(std::basic_ostream& os, + Range const& range, boost::geometry::plotly_color& c) + { + typedef typename boost::range_iterator::type iterator_t; + + bool first = true; + + os << "{ \"mode\": \"lines\", \"type\": \"" + << PlotType::plot_type() + << "\""; + + std::vector ss(Dimension); + + for (iterator_t it = boost::begin(range); + it != boost::end(range); + ++it, first = false) + { + stream_coordinate::apply(ss, *it, first); + } + + for(std::vector::iterator it=ss.begin(); + it!=ss.end(); it++) + { + os << ", " << *it << "]"; + } + + os << ", \"line\": {\"dash\": \"solid\", \"color\": \"" << c.rgb() << "\", \"width\": 2}, \"showlegend\": false}"; + } +private: + typedef typename boost::range_value::type point_type; +}; + +template +< + typename Polygon, + int Dimension, + typename PlotType +> +struct plotly_poly +{ + template + static inline void apply(std::basic_ostream& os, + Polygon const& polygon, boost::geometry::plotly_color& c) + { + typedef typename geometry::ring_type::type ring_t; + typedef typename boost::range_iterator::type iterator_t; + + typedef typename boost::range_value::type point_type; + + bool first = true; + os << "{ \"mode\": \"lines\", \"type\": \"" + << PlotType::plot_type() + << "\""; + + std::vector ss(Dimension); + + ring_t const& ring = geometry::exterior_ring(polygon); + for (iterator_t it = boost::begin(ring); + it != boost::end(ring); + ++it, first = false) + { + stream_coordinate::apply(ss, *it, first); + } + + for(std::vector::iterator it=ss.begin(); + it!=ss.end(); it++) + { + os << ", " << *it << "]"; + } + + os << ", \"line\": {\"dash\": \"solid\", \"color\": \"" << c.rgb() << "\", \"width\": 2}" + << ", \"fill\": \""<< PlotType::fill_type() <<"\", \"fillcolor\": \"" << c.rgba(0.3) << "\", \"showlegend\": false}"; + + // Inner rings: + boost::geometry::plotly_color w(255,255,255); + typename interior_return_type::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator::type + rit = boost::begin(rings); rit != boost::end(rings); ++rit) + { + + first = true; + os << ", { \"mode\": \"lines\", \"type\": \"" + << PlotType::plot_type() + << "\""; + + for(std::vector::iterator it=ss.begin(); + it!=ss.end(); it++) + { + *it = ""; + } + + for (typename detail::interior_ring_iterator::type + it = boost::begin(*rit); it != boost::end(*rit); + ++it, first = false) + { + stream_coordinate::apply(ss, *it, first); + } + + for(std::vector::iterator it=ss.begin(); + it!=ss.end(); it++) + { + os << ", " << *it << "]"; + } + + os << ", \"line\": {\"dash\": \"solid\", \"color\": \"" << c.rgb() << "\", \"width\": 2}" + << ", \"fill\": \""<< PlotType::fill_type() <<"\", \"fillcolor\": \"" << w.rgba(0.2) << "\", \"showlegend\": false}"; + } + } +}; + + +template +struct plotly_multi +{ + template + static inline void apply(std::basic_ostream& os, + MultiGeometry const& multi, boost::geometry::plotly_color& c) + { + for (typename boost::range_iterator::type + it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + Policy::apply(os, *it, c); + } + + } + +}; + + + +}} // namespace detail::plotly +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry, + typename Tag = typename cs_tag::type, + int Dimension = dimension::value +> +struct plotly_type +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_CS_TAG_OR_DIMENSION + , (Geometry) + ); +}; + +template +struct plotly_type + : detail::plotly::type_cartesian_2 +{}; + +template +struct plotly_type + : detail::plotly::type_cartesian_2 +{}; + +template +struct plotly_type + : detail::plotly::type_geographic +{}; + +template +< + typename Geometry, + typename Tag = typename tag::type +> +struct plotly_plot +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (Geometry) + ); +}; + +template +struct plotly_plot + : detail::plotly::plotly_point + < + Point, + dimension::value, + plotly_type + > +{}; + +template +struct plotly_plot + : detail::plotly::plotly_segment + < + Segment, + dimension::value, + plotly_type + > +{}; + +template +struct plotly_plot + : detail::plotly::plotly_range + < + Linestring, + dimension::value, + plotly_type + > +{}; + + +template +struct plotly_plot + : detail::plotly::plotly_poly + < + Polygon, + dimension::value, + plotly_type + > +{}; + +template +struct plotly_plot + : detail::plotly::plotly_multi + < + MultiPoint, + detail::plotly::plotly_point + < + typename boost::range_value::type, + dimension::value, + plotly_type + > + > +{}; + +template +struct plotly_plot + : detail::plotly::plotly_multi + < + MultiLinestring, + detail::plotly::plotly_range + < + typename boost::range_value::type, + dimension::value, + plotly_type + > + + > +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +template +inline void plotly_plot(std::basic_ostream& stream, Geometry const& geometry, boost::geometry::plotly_color& c) +{ + dispatch::plotly_plot::apply(stream, geometry, c); +} + +template +class plotly_plotter : boost::noncopyable +{ +public: + template + inline plotly_plotter(Ts &&... params) + : m_stream(std::forward(params)...) + { + m_stream << "{\n" + << " data: ["; + first = true; + } + + inline ~plotly_plotter() + { + m_stream << "], \n" + << " layout: {\"scene\": {\"camera\": {\"up\": {\"x\": 0, \"y\": 0, \"z\": 1}, \"eye\": {\"x\": 0.2829440102575065, \"y\": 0.05402592432697305, \"z\": 2.14581543627592}, \"center\": {\"x\": 0, \"y\": 0, \"z\": 0}, \"projection\": {\"type\": \"perspective\"}}, \"aspectmode\": \"auto\", \"aspectratio\": {\"x\": 1, \"y\": 1, \"z\": 1}}, \"xaxis\": {\"range\": [-1, 6], \"autorange\": true}, \"yaxis\": {\"range\": [-1, 4], \"autorange\": true}, \"autosize\": true, \"template\": {\"data\": {\"bar\": [{\"type\": \"bar\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"table\": [{\"type\": \"table\", \"cells\": {\"fill\": {\"color\": \"#EBF0F8\"}, \"line\": {\"color\": \"white\"}}, \"header\": {\"fill\": {\"color\": \"#C8D4E3\"}, \"line\": {\"color\": \"white\"}}}], \"carpet\": [{\"type\": \"carpet\", \"aaxis\": {\"gridcolor\": \"#C8D4E3\", \"linecolor\": \"#C8D4E3\", \"endlinecolor\": \"#2a3f5f\", \"minorgridcolor\": \"#C8D4E3\", \"startlinecolor\": \"#2a3f5f\"}, \"baxis\": {\"gridcolor\": \"#C8D4E3\", \"linecolor\": \"#C8D4E3\", \"endlinecolor\": \"#2a3f5f\", \"minorgridcolor\": \"#C8D4E3\", \"startlinecolor\": \"#2a3f5f\"}}], \"mesh3d\": [{\"type\": \"mesh3d\", \"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}], \"contour\": [{\"type\": \"contour\", \"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}, \"autocolorscale\": true}], \"heatmap\": [{\"type\": \"heatmap\", \"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}, \"autocolorscale\": true}], \"scatter\": [{\"type\": \"scatter\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"surface\": [{\"type\": \"surface\", \"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}], \"heatmapgl\": [{\"type\": \"heatmapgl\", \"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}], \"histogram\": [{\"type\": \"histogram\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"parcoords\": [{\"line\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}, \"type\": \"parcoords\"}], \"scatter3d\": [{\"type\": \"scatter3d\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"scattergl\": [{\"type\": \"scattergl\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"choropleth\": [{\"type\": \"choropleth\", \"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}], \"scattergeo\": [{\"type\": \"scattergeo\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"histogram2d\": [{\"type\": \"histogram2d\", \"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}, \"autocolorscale\": true}], \"scatterpolar\": [{\"type\": \"scatterpolar\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"contourcarpet\": [{\"type\": \"contourcarpet\", \"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}], \"scattercarpet\": [{\"type\": \"scattercarpet\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"scattermapbox\": [{\"type\": \"scattermapbox\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"scatterpolargl\": [{\"type\": \"scatterpolargl\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"scatterternary\": [{\"type\": \"scatterternary\", \"marker\": {\"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}}}], \"histogram2dcontour\": [{\"type\": \"histogram2dcontour\", \"colorbar\": {\"ticks\": \"\", \"outlinewidth\": 0}, \"autocolorscale\": true}]}, \"layout\": {\"geo\": {\"bgcolor\": \"white\", \"showland\": true, \"lakecolor\": \"white\", \"landcolor\": \"white\", \"showlakes\": true, \"subunitcolor\": \"#C8D4E3\"}, \"font\": {\"color\": \"#2a3f5f\"}, \"polar\": {\"bgcolor\": \"white\", \"radialaxis\": {\"ticks\": \"\", \"gridcolor\": \"#EBF0F8\", \"linecolor\": \"#EBF0F8\"}, \"angularaxis\": {\"ticks\": \"\", \"gridcolor\": \"#EBF0F8\", \"linecolor\": \"#EBF0F8\"}}, \"scene\": {\"xaxis\": {\"ticks\": \"\", \"gridcolor\": \"#DFE8F3\", \"gridwidth\": 2, \"linecolor\": \"#EBF0F8\", \"zerolinecolor\": \"#EBF0F8\", \"showbackground\": true, \"backgroundcolor\": \"white\"}, \"yaxis\": {\"ticks\": \"\", \"gridcolor\": \"#DFE8F3\", \"gridwidth\": 2, \"linecolor\": \"#EBF0F8\", \"zerolinecolor\": \"#EBF0F8\", \"showbackground\": true, \"backgroundcolor\": \"white\"}, \"zaxis\": {\"ticks\": \"\", \"gridcolor\": \"#DFE8F3\", \"gridwidth\": 2, \"linecolor\": \"#EBF0F8\", \"zerolinecolor\": \"#EBF0F8\", \"showbackground\": true, \"backgroundcolor\": \"white\"}}, \"title\": {\"x\": 0.05}, \"xaxis\": {\"ticks\": \"\", \"gridcolor\": \"#EBF0F8\", \"linecolor\": \"#EBF0F8\", \"automargin\": true, \"zerolinecolor\": \"#EBF0F8\", \"zerolinewidth\": 2}, \"yaxis\": {\"ticks\": \"\", \"gridcolor\": \"#EBF0F8\", \"linecolor\": \"#EBF0F8\", \"automargin\": true, \"zerolinecolor\": \"#EBF0F8\", \"zerolinewidth\": 2}, \"ternary\": {\"aaxis\": {\"ticks\": \"\", \"gridcolor\": \"#DFE8F3\", \"linecolor\": \"#A2B1C6\"}, \"baxis\": {\"ticks\": \"\", \"gridcolor\": \"#DFE8F3\", \"linecolor\": \"#A2B1C6\"}, \"caxis\": {\"ticks\": \"\", \"gridcolor\": \"#DFE8F3\", \"linecolor\": \"#A2B1C6\"}, \"bgcolor\": \"white\"}, \"colorway\": [\"#636efa\", \"#EF553B\", \"#00cc96\", \"#ab63fa\", \"#19d3f3\", \"#e763fa\", \"#fecb52\", \"#ffa15a\", \"#ff6692\", \"#b6e880\"], \"hovermode\": \"closest\", \"colorscale\": {\"diverging\": [[0, \"#8e0152\"], [0.1, \"#c51b7d\"], [0.2, \"#de77ae\"], [0.3, \"#f1b6da\"], [0.4, \"#fde0ef\"], [0.5, \"#f7f7f7\"], [0.6, \"#e6f5d0\"], [0.7, \"#b8e186\"], [0.8, \"#7fbc41\"], [0.9, \"#4d9221\"], [1, \"#276419\"]], \"sequential\": [[0, \"#0508b8\"], [0.0893854748603352, \"#1910d8\"], [0.1787709497206704, \"#3c19f0\"], [0.2681564245810056, \"#6b1cfb\"], [0.3575418994413408, \"#981cfd\"], [0.44692737430167595, \"#bf1cfd\"], [0.5363128491620112, \"#dd2bfd\"], [0.6256983240223464, \"#f246fe\"], [0.7150837988826816, \"#fc67fd\"], [0.8044692737430168, \"#fe88fc\"], [0.8938547486033519, \"#fea5fd\"], [0.9832402234636871, \"#febefe\"], [1, \"#fec3fe\"]], \"sequentialminus\": [[0, \"#0508b8\"], [0.0893854748603352, \"#1910d8\"], [0.1787709497206704, \"#3c19f0\"], [0.2681564245810056, \"#6b1cfb\"], [0.3575418994413408, \"#981cfd\"], [0.44692737430167595, \"#bf1cfd\"], [0.5363128491620112, \"#dd2bfd\"], [0.6256983240223464, \"#f246fe\"], [0.7150837988826816, \"#fc67fd\"], [0.8044692737430168, \"#fe88fc\"], [0.8938547486033519, \"#fea5fd\"], [0.9832402234636871, \"#febefe\"], [1, \"#fec3fe\"]]}, \"plot_bgcolor\": \"white\", \"paper_bgcolor\": \"white\", \"shapedefaults\": {\"line\": {\"width\": 0}, \"opacity\": 0.4, \"fillcolor\": \"#506784\"}, \"annotationdefaults\": {\"arrowhead\": 0, \"arrowcolor\": \"#506784\", \"arrowwidth\": 1}}, \"themeRef\": \"PLOTLY_WHITE\"}},\n" + << " frames: [], \n" + << " config: {\"showLink\": true, \"linkText\": \"Export to plotly.com\", \"mapboxAccessToken\": \"pk.eyJ1IjoiY2hyaWRkeXAiLCJhIjoiY2lxMnVvdm5iMDA4dnhsbTQ5aHJzcGs0MyJ9.X9o_rzNLNesDxdra4neC_A\"}\n" + << "}"; + } + + template + void plot(Geometry const& geometry, plotly_color && c) + { + m_stream << ( first ? "" : ", " ); + first = false; + plotly_plot(m_stream, geometry, c); + } + +private: + Stream m_stream; + bool first; +}; + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_EXTENSIONS_IO_PLOT_LY_PLOTTER_HPP