1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2009-2015 Barend Gehrels, Amsterdam, the Netherlands.
4 
5 // This file was modified by Oracle on 2015.
6 // Modifications copyright (c) 2015, Oracle and/or its affiliates.
7 
8 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
9 
10 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
11 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
12 
13 // Use, modification and distribution is subject to the Boost Software License,
14 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
15 // http://www.boost.org/LICENSE_1_0.txt)
16 
17 #ifndef BOOST_GEOMETRY_IO_SVG_MAPPER_HPP
18 #define BOOST_GEOMETRY_IO_SVG_MAPPER_HPP
19 
20 #include <cstdio>
21 
22 #include <vector>
23 
24 #include <boost/config.hpp>
25 #include <boost/mpl/assert.hpp>
26 #include <boost/noncopyable.hpp>
27 #include <boost/scoped_ptr.hpp>
28 #include <boost/type_traits/is_same.hpp>
29 #include <boost/type_traits/remove_const.hpp>
30 
31 #include <boost/algorithm/string/split.hpp>
32 #include <boost/algorithm/string/classification.hpp>
33 
34 
35 #include <boost/geometry/core/tags.hpp>
36 #include <boost/geometry/core/tag_cast.hpp>
37 
38 #include <boost/geometry/algorithms/envelope.hpp>
39 #include <boost/geometry/algorithms/expand.hpp>
40 #include <boost/geometry/algorithms/is_empty.hpp>
41 #include <boost/geometry/algorithms/transform.hpp>
42 #include <boost/geometry/strategies/transform/map_transformer.hpp>
43 #include <boost/geometry/views/segment_view.hpp>
44 
45 #include <boost/geometry/io/svg/write_svg.hpp>
46 
47 // Helper geometries (all points are transformed to integer-points)
48 #include <boost/geometry/geometries/geometries.hpp>
49 
50 
51 namespace boost { namespace geometry
52 {
53 
54 #ifndef DOXYGEN_NO_DETAIL
55 namespace detail { namespace svg
56 {
57     typedef model::point<int, 2, cs::cartesian> svg_point_type;
58 }}
59 #endif
60 
61 
62 #ifndef DOXYGEN_NO_DISPATCH
63 namespace dispatch
64 {
65 
66 
67 
68 template <typename GeometryTag, typename Geometry>
69 struct svg_map
70 {
71     BOOST_MPL_ASSERT_MSG
72         (
73             false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE
74             , (Geometry)
75         );
76 };
77 
78 
79 template <typename Point>
80 struct svg_map<point_tag, Point>
81 {
82     template <typename TransformStrategy>
applyboost::geometry::dispatch::svg_map83     static inline void apply(std::ostream& stream,
84                     std::string const& style, int size,
85                     Point const& point, TransformStrategy const& strategy)
86     {
87         detail::svg::svg_point_type ipoint;
88         geometry::transform(point, ipoint, strategy);
89         stream << geometry::svg(ipoint, style, size) << std::endl;
90     }
91 };
92 
93 template <typename Box>
94 struct svg_map<box_tag, Box>
95 {
96     template <typename TransformStrategy>
applyboost::geometry::dispatch::svg_map97     static inline void apply(std::ostream& stream,
98                     std::string const& style, int size,
99                     Box const& box, TransformStrategy const& strategy)
100     {
101         model::box<detail::svg::svg_point_type> ibox;
102 
103         // Fix bug in gcc compiler warning for possible uninitialation
104 #if defined(BOOST_GCC)
105         geometry::assign_zero(ibox);
106 #endif
107         geometry::transform(box, ibox, strategy);
108 
109         stream << geometry::svg(ibox, style, size) << std::endl;
110     }
111 };
112 
113 
114 template <typename Range1, typename Range2>
115 struct svg_map_range
116 {
117     template <typename TransformStrategy>
applyboost::geometry::dispatch::svg_map_range118     static inline void apply(std::ostream& stream,
119                 std::string const& style, int size,
120                 Range1 const& range, TransformStrategy const& strategy)
121     {
122         Range2 irange;
123         geometry::transform(range, irange, strategy);
124         stream << geometry::svg(irange, style, size) << std::endl;
125     }
126 };
127 
128 template <typename Segment>
129 struct svg_map<segment_tag, Segment>
130 {
131     template <typename TransformStrategy>
applyboost::geometry::dispatch::svg_map132     static inline void apply(std::ostream& stream,
133                     std::string const& style, int size,
134                     Segment const& segment, TransformStrategy const& strategy)
135     {
136         typedef segment_view<Segment> view_type;
137         view_type range(segment);
138         svg_map_range
139             <
140                 view_type,
141                 model::linestring<detail::svg::svg_point_type>
142             >::apply(stream, style, size, range, strategy);
143     }
144 };
145 
146 
147 template <typename Ring>
148 struct svg_map<ring_tag, Ring>
149     : svg_map_range<Ring, model::ring<detail::svg::svg_point_type> >
150 {};
151 
152 
153 template <typename Linestring>
154 struct svg_map<linestring_tag, Linestring>
155     : svg_map_range<Linestring, model::linestring<detail::svg::svg_point_type> >
156 {};
157 
158 
159 template <typename Polygon>
160 struct svg_map<polygon_tag, Polygon>
161 {
162     template <typename TransformStrategy>
applyboost::geometry::dispatch::svg_map163     static inline void apply(std::ostream& stream,
164                     std::string const& style, int size,
165                     Polygon const& polygon, TransformStrategy const& strategy)
166     {
167         model::polygon<detail::svg::svg_point_type> ipoly;
168         geometry::transform(polygon, ipoly, strategy);
169         stream << geometry::svg(ipoly, style, size) << std::endl;
170     }
171 };
172 
173 
174 template <typename Multi>
175 struct svg_map<multi_tag, Multi>
176 {
177     typedef typename single_tag_of
178       <
179           typename geometry::tag<Multi>::type
180       >::type stag;
181 
182     template <typename TransformStrategy>
applyboost::geometry::dispatch::svg_map183     static inline void apply(std::ostream& stream,
184                     std::string const& style, int size,
185                     Multi const& multi, TransformStrategy const& strategy)
186     {
187         for (typename boost::range_iterator<Multi const>::type it
188             = boost::begin(multi);
189             it != boost::end(multi);
190             ++it)
191         {
192             svg_map
193                 <
194                     stag,
195                     typename boost::range_value<Multi>::type
196                 >::apply(stream, style, size, *it, strategy);
197         }
198     }
199 };
200 
201 
202 } // namespace dispatch
203 #endif
204 
205 
206 template <typename Geometry, typename TransformStrategy>
svg_map(std::ostream & stream,std::string const & style,int size,Geometry const & geometry,TransformStrategy const & strategy)207 inline void svg_map(std::ostream& stream,
208             std::string const& style, int size,
209             Geometry const& geometry, TransformStrategy const& strategy)
210 {
211     dispatch::svg_map
212         <
213             typename tag_cast
214                 <
215                     typename tag<Geometry>::type,
216                     multi_tag
217                 >::type,
218             typename boost::remove_const<Geometry>::type
219         >::apply(stream, style, size, geometry, strategy);
220 }
221 
222 
223 /*!
224 \brief Helper class to create SVG maps
225 \tparam Point Point type, for input geometries.
226 \tparam SameScale Boolean flag indicating if horizontal and vertical scale should
227     be the same. The default value is true
228 \ingroup svg
229 
230 \qbk{[include reference/io/svg.qbk]}
231 */
232 template <typename Point, bool SameScale = true>
233 class svg_mapper : boost::noncopyable
234 {
235     typedef typename geometry::select_most_precise
236         <
237             typename coordinate_type<Point>::type,
238             double
239         >::type calculation_type;
240 
241     typedef strategy::transform::map_transformer
242         <
243             calculation_type,
244             geometry::dimension<Point>::type::value,
245             geometry::dimension<Point>::type::value,
246             true,
247             SameScale
248         > transformer_type;
249 
250     model::box<Point> m_bounding_box;
251     boost::scoped_ptr<transformer_type> m_matrix;
252     std::ostream& m_stream;
253     int m_width, m_height;
254     std::string m_width_height; // for <svg> tag only, defaults to 2x 100%
255 
init_matrix()256     void init_matrix()
257     {
258         if (! m_matrix)
259         {
260             m_matrix.reset(new transformer_type(m_bounding_box,
261                             m_width, m_height));
262 
263 
264             m_stream << "<?xml version=\"1.0\" standalone=\"no\"?>"
265                 << std::endl
266                 << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\""
267                 << std::endl
268                 << "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">"
269                 << std::endl
270                 << "<svg " << m_width_height << " version=\"1.1\""
271                 << std::endl
272                 << "xmlns=\"http://www.w3.org/2000/svg\""
273                 << std::endl
274                 << "xmlns:xlink=\"http://www.w3.org/1999/xlink\""
275                 << ">"
276                 << std::endl;
277         }
278     }
279 
280 public :
281 
282     /*!
283     \brief Constructor, initializing the SVG map. Opens and initializes the SVG.
284          Should be called explicitly.
285     \param stream Output stream, should be a stream already open
286     \param width Width of the SVG map (in SVG pixels)
287     \param height Height of the SVG map (in SVG pixels)
288     \param width_height Optional information to increase width and/or height
289     */
svg_mapper(std::ostream & stream,int width,int height,std::string const & width_height="width=\\"100%\\" height=\\"100%\\"")290     explicit svg_mapper(std::ostream& stream, int width, int height
291         , std::string const& width_height = "width=\"100%\" height=\"100%\"")
292         : m_stream(stream)
293         , m_width(width)
294         , m_height(height)
295         , m_width_height(width_height)
296     {
297         assign_inverse(m_bounding_box);
298     }
299 
300     /*!
301     \brief Destructor, called automatically. Closes the SVG by streaming <\/svg>
302     */
~svg_mapper()303     virtual ~svg_mapper()
304     {
305         m_stream << "</svg>" << std::endl;
306     }
307 
308     /*!
309     \brief Adds a geometry to the transformation matrix. After doing this,
310         the specified geometry can be mapped fully into the SVG map
311     \tparam Geometry \tparam_geometry
312     \param geometry \param_geometry
313     */
314     template <typename Geometry>
add(Geometry const & geometry)315     void add(Geometry const& geometry)
316     {
317         if (! geometry::is_empty(geometry))
318         {
319             expand(m_bounding_box,
320                 return_envelope
321                     <
322                         model::box<Point>
323                     >(geometry));
324         }
325     }
326 
327     /*!
328     \brief Maps a geometry into the SVG map using the specified style
329     \tparam Geometry \tparam_geometry
330     \param geometry \param_geometry
331     \param style String containing verbatim SVG style information
332     \param size Optional size (used for SVG points) in SVG pixels. For linestrings,
333         specify linewidth in the SVG style information
334     */
335     template <typename Geometry>
map(Geometry const & geometry,std::string const & style,int size=-1)336     void map(Geometry const& geometry, std::string const& style,
337                 int size = -1)
338     {
339         init_matrix();
340         svg_map(m_stream, style, size, geometry, *m_matrix);
341     }
342 
343     /*!
344     \brief Adds a text to the SVG map
345     \tparam TextPoint \tparam_point
346     \param point Location of the text (in map units)
347     \param s The text itself
348     \param style String containing verbatim SVG style information, of the text
349     \param offset_x Offset in SVG pixels, defaults to 0
350     \param offset_y Offset in SVG pixels, defaults to 0
351     \param lineheight Line height in SVG pixels, in case the text contains \n
352     */
353     template <typename TextPoint>
text(TextPoint const & point,std::string const & s,std::string const & style,int offset_x=0,int offset_y=0,int lineheight=10)354     void text(TextPoint const& point, std::string const& s,
355                 std::string const& style,
356                 int offset_x = 0, int offset_y = 0, int lineheight = 10)
357     {
358         init_matrix();
359         detail::svg::svg_point_type map_point;
360         transform(point, map_point, *m_matrix);
361         m_stream
362             << "<text style=\"" << style << "\""
363             << " x=\"" << get<0>(map_point) + offset_x << "\""
364             << " y=\"" << get<1>(map_point) + offset_y << "\""
365             << ">";
366         if (s.find("\n") == std::string::npos)
367         {
368              m_stream  << s;
369         }
370         else
371         {
372             // Multi-line modus
373 
374             std::vector<std::string> splitted;
375             boost::split(splitted, s, boost::is_any_of("\n"));
376             for (std::vector<std::string>::const_iterator it
377                 = splitted.begin();
378                 it != splitted.end();
379                 ++it, offset_y += lineheight)
380             {
381                  m_stream
382                     << "<tspan x=\"" << get<0>(map_point) + offset_x
383                     << "\""
384                     << " y=\"" << get<1>(map_point) + offset_y
385                     << "\""
386                     << ">" << *it << "</tspan>";
387             }
388         }
389         m_stream << "</text>" << std::endl;
390     }
391 };
392 
393 
394 }} // namespace boost::geometry
395 
396 
397 #endif // BOOST_GEOMETRY_IO_SVG_MAPPER_HPP
398