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