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