]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Boost.Geometry (aka GGL, Generic Geometry Library) |
2 | ||
3 | // Copyright (c) 2009-2012 Barend Gehrels, Amsterdam, the Netherlands. | |
4 | // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. | |
5 | ||
20effc67 TL |
6 | // This file was modified by Oracle on 2016-2020. |
7 | // Modifications copyright (c) 2016-2020, Oracle and/or its affiliates. | |
7c673cae FG |
8 | |
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_WRITE_HPP | |
19 | #define BOOST_GEOMETRY_IO_SVG_WRITE_HPP | |
20 | ||
21 | #include <ostream> | |
22 | #include <string> | |
23 | ||
24 | #include <boost/config.hpp> | |
20effc67 TL |
25 | #include <boost/range/begin.hpp> |
26 | #include <boost/range/end.hpp> | |
27 | #include <boost/range/value_type.hpp> | |
7c673cae FG |
28 | #include <boost/variant/apply_visitor.hpp> |
29 | #include <boost/variant/static_visitor.hpp> | |
30 | #include <boost/variant/variant_fwd.hpp> | |
31 | ||
32 | #include <boost/geometry/algorithms/detail/interior_iterator.hpp> | |
33 | ||
34 | #include <boost/geometry/core/exterior_ring.hpp> | |
35 | #include <boost/geometry/core/interior_rings.hpp> | |
36 | #include <boost/geometry/core/ring_type.hpp> | |
20effc67 | 37 | #include <boost/geometry/core/static_assert.hpp> |
7c673cae FG |
38 | |
39 | #include <boost/geometry/geometries/concepts/check.hpp> | |
40 | ||
41 | ||
42 | namespace boost { namespace geometry | |
43 | { | |
44 | ||
45 | ||
46 | #ifndef DOXYGEN_NO_DETAIL | |
47 | namespace detail { namespace svg | |
48 | { | |
49 | ||
50 | ||
51 | template <typename Point> | |
52 | struct svg_point | |
53 | { | |
54 | template <typename Char, typename Traits> | |
55 | static inline void apply(std::basic_ostream<Char, Traits>& os, | |
56 | Point const& p, std::string const& style, double size) | |
57 | { | |
58 | os << "<circle cx=\"" << geometry::get<0>(p) | |
59 | << "\" cy=\"" << geometry::get<1>(p) | |
60 | << "\" r=\"" << (size < 0 ? 5 : size) | |
61 | << "\" style=\"" << style << "\"/>"; | |
62 | } | |
63 | }; | |
64 | ||
65 | ||
66 | template <typename Box> | |
67 | struct svg_box | |
68 | { | |
69 | template <typename Char, typename Traits> | |
70 | static inline void apply(std::basic_ostream<Char, Traits>& os, | |
71 | Box const& box, std::string const& style, double) | |
72 | { | |
73 | // Prevent invisible boxes, making them >=1, using "max" | |
74 | BOOST_USING_STD_MAX(); | |
75 | ||
76 | typedef typename coordinate_type<Box>::type ct; | |
77 | ct x = geometry::get<geometry::min_corner, 0>(box); | |
78 | ct y = geometry::get<geometry::min_corner, 1>(box); | |
79 | ct width = max BOOST_PREVENT_MACRO_SUBSTITUTION (ct(1), | |
80 | geometry::get<geometry::max_corner, 0>(box) - x); | |
81 | ct height = max BOOST_PREVENT_MACRO_SUBSTITUTION (ct(1), | |
82 | geometry::get<geometry::max_corner, 1>(box) - y); | |
83 | ||
84 | os << "<rect x=\"" << x << "\" y=\"" << y | |
85 | << "\" width=\"" << width << "\" height=\"" << height | |
86 | << "\" style=\"" << style << "\"/>"; | |
87 | } | |
88 | }; | |
89 | ||
90 | template <typename Segment> | |
91 | struct svg_segment | |
92 | { | |
93 | template <typename Char, typename Traits> | |
94 | static inline void apply(std::basic_ostream<Char, Traits>& os, | |
95 | Segment const& segment, std::string const& style, double) | |
96 | { | |
97 | typedef typename coordinate_type<Segment>::type ct; | |
98 | ct x1 = geometry::get<0, 0>(segment); | |
99 | ct y1 = geometry::get<0, 1>(segment); | |
100 | ct x2 = geometry::get<1, 0>(segment); | |
101 | ct y2 = geometry::get<1, 1>(segment); | |
102 | ||
103 | os << "<line x1=\"" << x1 << "\" y1=\"" << y1 | |
104 | << "\" x2=\"" << x2 << "\" y2=\"" << y2 | |
105 | << "\" style=\"" << style << "\"/>"; | |
106 | } | |
107 | }; | |
108 | ||
109 | /*! | |
110 | \brief Stream ranges as SVG | |
111 | \note policy is used to select type (polyline/polygon) | |
112 | */ | |
113 | template <typename Range, typename Policy> | |
114 | struct svg_range | |
115 | { | |
116 | template <typename Char, typename Traits> | |
117 | static inline void apply(std::basic_ostream<Char, Traits>& os, | |
118 | Range const& range, std::string const& style, double) | |
119 | { | |
120 | typedef typename boost::range_iterator<Range const>::type iterator; | |
121 | ||
122 | bool first = true; | |
123 | ||
124 | os << "<" << Policy::prefix() << " points=\""; | |
125 | ||
126 | for (iterator it = boost::begin(range); | |
127 | it != boost::end(range); | |
128 | ++it, first = false) | |
129 | { | |
130 | os << (first ? "" : " " ) | |
131 | << geometry::get<0>(*it) | |
132 | << "," | |
133 | << geometry::get<1>(*it); | |
134 | } | |
135 | os << "\" style=\"" << style << Policy::style() << "\"/>"; | |
136 | } | |
137 | }; | |
138 | ||
139 | ||
140 | ||
141 | template <typename Polygon> | |
142 | struct svg_poly | |
143 | { | |
144 | template <typename Char, typename Traits> | |
145 | static inline void apply(std::basic_ostream<Char, Traits>& os, | |
146 | Polygon const& polygon, std::string const& style, double) | |
147 | { | |
148 | typedef typename geometry::ring_type<Polygon>::type ring_type; | |
149 | typedef typename boost::range_iterator<ring_type const>::type iterator_type; | |
150 | ||
151 | bool first = true; | |
152 | os << "<g fill-rule=\"evenodd\"><path d=\""; | |
153 | ||
154 | ring_type const& ring = geometry::exterior_ring(polygon); | |
155 | for (iterator_type it = boost::begin(ring); | |
156 | it != boost::end(ring); | |
157 | ++it, first = false) | |
158 | { | |
159 | os << (first ? "M" : " L") << " " | |
160 | << geometry::get<0>(*it) | |
161 | << "," | |
162 | << geometry::get<1>(*it); | |
163 | } | |
164 | ||
165 | // Inner rings: | |
166 | { | |
167 | typename interior_return_type<Polygon const>::type | |
168 | rings = interior_rings(polygon); | |
169 | for (typename detail::interior_iterator<Polygon const>::type | |
170 | rit = boost::begin(rings); rit != boost::end(rings); ++rit) | |
171 | { | |
172 | first = true; | |
173 | for (typename detail::interior_ring_iterator<Polygon const>::type | |
174 | it = boost::begin(*rit); it != boost::end(*rit); | |
175 | ++it, first = false) | |
176 | { | |
177 | os << (first ? "M" : " L") << " " | |
178 | << geometry::get<0>(*it) | |
179 | << "," | |
180 | << geometry::get<1>(*it); | |
181 | } | |
182 | } | |
183 | } | |
184 | os << " z \" style=\"" << style << "\"/></g>"; | |
185 | ||
186 | } | |
187 | }; | |
188 | ||
189 | ||
190 | ||
191 | struct prefix_linestring | |
192 | { | |
193 | static inline const char* prefix() { return "polyline"; } | |
194 | static inline const char* style() { return ";fill:none"; } | |
195 | }; | |
196 | ||
197 | ||
198 | struct prefix_ring | |
199 | { | |
200 | static inline const char* prefix() { return "polygon"; } | |
201 | static inline const char* style() { return ""; } | |
202 | }; | |
203 | ||
204 | ||
205 | template <typename MultiGeometry, typename Policy> | |
206 | struct svg_multi | |
207 | { | |
208 | template <typename Char, typename Traits> | |
209 | static inline void apply(std::basic_ostream<Char, Traits>& os, | |
210 | MultiGeometry const& multi, std::string const& style, double size) | |
211 | { | |
212 | for (typename boost::range_iterator<MultiGeometry const>::type | |
213 | it = boost::begin(multi); | |
214 | it != boost::end(multi); | |
215 | ++it) | |
216 | { | |
217 | Policy::apply(os, *it, style, size); | |
218 | } | |
219 | ||
220 | } | |
221 | ||
222 | }; | |
223 | ||
224 | ||
225 | }} // namespace detail::svg | |
226 | #endif // DOXYGEN_NO_DETAIL | |
227 | ||
228 | ||
229 | #ifndef DOXYGEN_NO_DISPATCH | |
230 | namespace dispatch | |
231 | { | |
232 | ||
233 | /*! | |
234 | \brief Dispatching base struct for SVG streaming, specialized below per geometry type | |
235 | \details Specializations should implement a static method "stream" to stream a geometry | |
236 | The static method should have the signature: | |
237 | ||
238 | template <typename Char, typename Traits> | |
239 | static inline void apply(std::basic_ostream<Char, Traits>& os, G const& geometry) | |
240 | */ | |
241 | template <typename Geometry, typename Tag = typename tag<Geometry>::type> | |
242 | struct svg | |
243 | { | |
20effc67 TL |
244 | BOOST_GEOMETRY_STATIC_ASSERT_FALSE( |
245 | "Not or not yet implemented for this Geometry type.", | |
246 | Geometry, Tag); | |
7c673cae FG |
247 | }; |
248 | ||
249 | template <typename Point> | |
250 | struct svg<Point, point_tag> : detail::svg::svg_point<Point> {}; | |
251 | ||
252 | template <typename Segment> | |
253 | struct svg<Segment, segment_tag> : detail::svg::svg_segment<Segment> {}; | |
254 | ||
255 | template <typename Box> | |
256 | struct svg<Box, box_tag> : detail::svg::svg_box<Box> {}; | |
257 | ||
258 | template <typename Linestring> | |
259 | struct svg<Linestring, linestring_tag> | |
260 | : detail::svg::svg_range<Linestring, detail::svg::prefix_linestring> {}; | |
261 | ||
262 | template <typename Ring> | |
263 | struct svg<Ring, ring_tag> | |
264 | : detail::svg::svg_range<Ring, detail::svg::prefix_ring> {}; | |
265 | ||
266 | template <typename Polygon> | |
267 | struct svg<Polygon, polygon_tag> | |
268 | : detail::svg::svg_poly<Polygon> {}; | |
269 | ||
270 | template <typename MultiPoint> | |
271 | struct svg<MultiPoint, multi_point_tag> | |
272 | : detail::svg::svg_multi | |
273 | < | |
274 | MultiPoint, | |
275 | detail::svg::svg_point | |
276 | < | |
277 | typename boost::range_value<MultiPoint>::type | |
278 | > | |
279 | ||
280 | > | |
281 | {}; | |
282 | ||
283 | template <typename MultiLinestring> | |
284 | struct svg<MultiLinestring, multi_linestring_tag> | |
285 | : detail::svg::svg_multi | |
286 | < | |
287 | MultiLinestring, | |
288 | detail::svg::svg_range | |
289 | < | |
290 | typename boost::range_value<MultiLinestring>::type, | |
291 | detail::svg::prefix_linestring | |
292 | > | |
293 | ||
294 | > | |
295 | {}; | |
296 | ||
297 | template <typename MultiPolygon> | |
298 | struct svg<MultiPolygon, multi_polygon_tag> | |
299 | : detail::svg::svg_multi | |
300 | < | |
301 | MultiPolygon, | |
302 | detail::svg::svg_poly | |
303 | < | |
304 | typename boost::range_value<MultiPolygon>::type | |
305 | > | |
306 | ||
307 | > | |
308 | {}; | |
309 | ||
310 | ||
311 | template <typename Geometry> | |
312 | struct devarianted_svg | |
313 | { | |
314 | template <typename OutputStream> | |
315 | static inline void apply(OutputStream& os, | |
316 | Geometry const& geometry, | |
317 | std::string const& style, | |
318 | double size) | |
319 | { | |
320 | svg<Geometry>::apply(os, geometry, style, size); | |
321 | } | |
322 | }; | |
323 | ||
324 | template <BOOST_VARIANT_ENUM_PARAMS(typename T)> | |
325 | struct devarianted_svg<variant<BOOST_VARIANT_ENUM_PARAMS(T)> > | |
326 | { | |
327 | template <typename OutputStream> | |
328 | struct visitor: static_visitor<void> | |
329 | { | |
330 | OutputStream& m_os; | |
331 | std::string const& m_style; | |
332 | double m_size; | |
333 | ||
334 | visitor(OutputStream& os, std::string const& style, double size) | |
335 | : m_os(os) | |
336 | , m_style(style) | |
337 | , m_size(size) | |
338 | {} | |
339 | ||
340 | template <typename Geometry> | |
341 | inline void operator()(Geometry const& geometry) const | |
342 | { | |
343 | devarianted_svg<Geometry>::apply(m_os, geometry, m_style, m_size); | |
344 | } | |
345 | }; | |
346 | ||
347 | template <typename OutputStream> | |
348 | static inline void apply( | |
349 | OutputStream& os, | |
350 | variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry, | |
351 | std::string const& style, | |
352 | double size | |
353 | ) | |
354 | { | |
355 | boost::apply_visitor(visitor<OutputStream>(os, style, size), geometry); | |
356 | } | |
357 | }; | |
358 | ||
359 | } // namespace dispatch | |
360 | #endif // DOXYGEN_NO_DISPATCH | |
361 | ||
362 | ||
363 | /*! | |
364 | \brief Generic geometry template manipulator class, takes corresponding output class from traits class | |
365 | \ingroup svg | |
366 | \details Stream manipulator, streams geometry classes as SVG (Scalable Vector Graphics) | |
367 | */ | |
368 | template <typename Geometry> | |
369 | class svg_manipulator | |
370 | { | |
371 | public: | |
372 | ||
373 | inline svg_manipulator(Geometry const& g, std::string const& style, double size) | |
374 | : m_geometry(g) | |
375 | , m_style(style) | |
376 | , m_size(size) | |
377 | {} | |
378 | ||
379 | template <typename Char, typename Traits> | |
380 | inline friend std::basic_ostream<Char, Traits>& operator<<( | |
381 | std::basic_ostream<Char, Traits>& os, svg_manipulator const& m) | |
382 | { | |
383 | dispatch::devarianted_svg<Geometry>::apply(os, | |
384 | m.m_geometry, | |
385 | m.m_style, | |
386 | m.m_size); | |
387 | os.flush(); | |
388 | return os; | |
389 | } | |
390 | ||
391 | private: | |
392 | Geometry const& m_geometry; | |
393 | std::string const& m_style; | |
394 | double m_size; | |
395 | }; | |
396 | ||
397 | /*! | |
398 | \brief Manipulator to stream geometries as SVG | |
399 | \tparam Geometry \tparam_geometry | |
400 | \param geometry \param_geometry | |
401 | \param style String containing verbatim SVG style information | |
402 | \param size Optional size (used for SVG points) in SVG pixels. For linestrings, | |
403 | specify linewidth in the SVG style information | |
404 | \ingroup svg | |
405 | */ | |
406 | template <typename Geometry> | |
407 | inline svg_manipulator<Geometry> svg(Geometry const& geometry, | |
408 | std::string const& style, double size = -1.0) | |
409 | { | |
410 | concepts::check<Geometry const>(); | |
411 | ||
412 | return svg_manipulator<Geometry>(geometry, style, size); | |
413 | } | |
414 | ||
415 | }} // namespace boost::geometry | |
416 | ||
417 | #endif // BOOST_GEOMETRY_IO_SVG_WRITE_HPP |