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