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