File indexing completed on 2025-10-30 08:18:02
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018 #ifndef BOOST_GEOMETRY_IO_SVG_MAPPER_HPP
0019 #define BOOST_GEOMETRY_IO_SVG_MAPPER_HPP
0020
0021 #include <cstdio>
0022 #include <memory>
0023 #include <type_traits>
0024 #include <vector>
0025
0026 #include <boost/algorithm/string/classification.hpp>
0027 #include <boost/algorithm/string/split.hpp>
0028 #include <boost/config.hpp>
0029 #include <boost/noncopyable.hpp>
0030
0031 #include <boost/geometry/core/static_assert.hpp>
0032 #include <boost/geometry/core/tags.hpp>
0033 #include <boost/geometry/core/tag_cast.hpp>
0034 #include <boost/geometry/algorithms/envelope.hpp>
0035 #include <boost/geometry/algorithms/expand.hpp>
0036 #include <boost/geometry/algorithms/is_empty.hpp>
0037 #include <boost/geometry/algorithms/transform.hpp>
0038 #include <boost/geometry/strategies/transform/map_transformer.hpp>
0039 #include <boost/geometry/views/segment_view.hpp>
0040
0041 #include <boost/geometry/io/svg/write.hpp>
0042
0043
0044 #include <boost/geometry/geometries/geometries.hpp>
0045
0046
0047 namespace boost { namespace geometry
0048 {
0049
0050
0051 #ifndef DOXYGEN_NO_DISPATCH
0052 namespace dispatch
0053 {
0054
0055
0056 template <typename GeometryTag, typename Geometry, typename SvgPoint>
0057 struct svg_map
0058 {
0059 BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
0060 "Not or not yet implemented for this Geometry type.",
0061 GeometryTag, Geometry);
0062 };
0063
0064
0065 template <typename Point, typename SvgPoint>
0066 struct svg_map<point_tag, Point, SvgPoint>
0067 {
0068 template <typename TransformStrategy>
0069 static inline void apply(std::ostream& stream,
0070 std::string const& style, double size,
0071 Point const& point, TransformStrategy const& strategy)
0072 {
0073 SvgPoint ipoint;
0074 geometry::transform(point, ipoint, strategy);
0075 stream << geometry::svg(ipoint, style, size) << std::endl;
0076 }
0077 };
0078
0079 template <typename BoxSeg1, typename BoxSeg2, typename SvgPoint>
0080 struct svg_map_box_seg
0081 {
0082 template <typename TransformStrategy>
0083 static inline void apply(std::ostream& stream,
0084 std::string const& style, double size,
0085 BoxSeg1 const& box_seg, TransformStrategy const& strategy)
0086 {
0087 BoxSeg2 ibox_seg;
0088
0089
0090 #if defined(BOOST_GCC)
0091 geometry::assign_zero(ibox_seg);
0092 #endif
0093 geometry::transform(box_seg, ibox_seg, strategy);
0094
0095 stream << geometry::svg(ibox_seg, style, size) << std::endl;
0096 }
0097 };
0098
0099 template <typename Box, typename SvgPoint>
0100 struct svg_map<box_tag, Box, SvgPoint>
0101 : svg_map_box_seg<Box, model::box<SvgPoint>, SvgPoint>
0102 {};
0103
0104 template <typename Segment, typename SvgPoint>
0105 struct svg_map<segment_tag, Segment, SvgPoint>
0106 : svg_map_box_seg<Segment, model::segment<SvgPoint>, SvgPoint>
0107 {};
0108
0109
0110 template <typename Range1, typename Range2, typename SvgPoint>
0111 struct svg_map_range
0112 {
0113 template <typename TransformStrategy>
0114 static inline void apply(std::ostream& stream,
0115 std::string const& style, double size,
0116 Range1 const& range, TransformStrategy const& strategy)
0117 {
0118 Range2 irange;
0119 geometry::transform(range, irange, strategy);
0120 stream << geometry::svg(irange, style, size) << std::endl;
0121 }
0122 };
0123
0124 template <typename Ring, typename SvgPoint>
0125 struct svg_map<ring_tag, Ring, SvgPoint>
0126 : svg_map_range<Ring, model::ring<SvgPoint>, SvgPoint>
0127 {};
0128
0129
0130 template <typename Linestring, typename SvgPoint>
0131 struct svg_map<linestring_tag, Linestring, SvgPoint>
0132 : svg_map_range<Linestring, model::linestring<SvgPoint>, SvgPoint>
0133 {};
0134
0135
0136 template <typename Polygon, typename SvgPoint>
0137 struct svg_map<polygon_tag, Polygon, SvgPoint>
0138 {
0139 template <typename TransformStrategy>
0140 static inline void apply(std::ostream& stream,
0141 std::string const& style, double size,
0142 Polygon const& polygon, TransformStrategy const& strategy)
0143 {
0144 model::polygon<SvgPoint> ipoly;
0145 geometry::transform(polygon, ipoly, strategy);
0146 stream << geometry::svg(ipoly, style, size) << std::endl;
0147 }
0148 };
0149
0150
0151 template <typename Multi, typename SvgPoint>
0152 struct svg_map<multi_tag, Multi, SvgPoint>
0153 {
0154 typedef typename single_tag_of
0155 <
0156 geometry::tag_t<Multi>
0157 >::type stag;
0158
0159 template <typename TransformStrategy>
0160 static inline void apply(std::ostream& stream,
0161 std::string const& style, double size,
0162 Multi const& multi, TransformStrategy const& strategy)
0163 {
0164 for (auto it = boost::begin(multi); it != boost::end(multi); ++it)
0165 {
0166 svg_map
0167 <
0168 stag,
0169 typename boost::range_value<Multi>::type,
0170 SvgPoint
0171 >::apply(stream, style, size, *it, strategy);
0172 }
0173 }
0174 };
0175
0176
0177 template <typename SvgPoint, typename Geometry>
0178 struct devarianted_svg_map
0179 {
0180 template <typename TransformStrategy>
0181 static inline void apply(std::ostream& stream,
0182 std::string const& style,
0183 double size,
0184 Geometry const& geometry,
0185 TransformStrategy const& strategy)
0186 {
0187 svg_map
0188 <
0189 tag_cast_t<tag_t<Geometry>, multi_tag>,
0190 typename std::remove_const<Geometry>::type,
0191 SvgPoint
0192 >::apply(stream, style, size, geometry, strategy);
0193 }
0194 };
0195
0196 template <typename SvgPoint, BOOST_VARIANT_ENUM_PARAMS(typename T)>
0197 struct devarianted_svg_map<SvgPoint, variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
0198 {
0199 template <typename TransformStrategy>
0200 struct visitor: static_visitor<void>
0201 {
0202 std::ostream& m_os;
0203 std::string const& m_style;
0204 double m_size;
0205 TransformStrategy const& m_strategy;
0206
0207 visitor(std::ostream& os,
0208 std::string const& style,
0209 double size,
0210 TransformStrategy const& strategy)
0211 : m_os(os)
0212 , m_style(style)
0213 , m_size(size)
0214 , m_strategy(strategy)
0215 {}
0216
0217 template <typename Geometry>
0218 inline void operator()(Geometry const& geometry) const
0219 {
0220 devarianted_svg_map<SvgPoint, Geometry>::apply(m_os, m_style, m_size, geometry, m_strategy);
0221 }
0222 };
0223
0224 template <typename TransformStrategy>
0225 static inline void apply(std::ostream& stream,
0226 std::string const& style,
0227 double size,
0228 variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry,
0229 TransformStrategy const& strategy)
0230 {
0231 boost::apply_visitor(visitor<TransformStrategy>(stream, style, size, strategy), geometry);
0232 }
0233 };
0234
0235
0236 }
0237 #endif
0238
0239
0240 template <typename SvgPoint, typename Geometry, typename TransformStrategy>
0241 inline void svg_map(std::ostream& stream,
0242 std::string const& style, double size,
0243 Geometry const& geometry, TransformStrategy const& strategy)
0244 {
0245 dispatch::devarianted_svg_map<SvgPoint, Geometry>::apply(stream,
0246 style, size, geometry, strategy);
0247 }
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261 template
0262 <
0263 typename Point,
0264 bool SameScale = true,
0265 typename SvgCoordinateType = double
0266 >
0267 class svg_mapper : boost::noncopyable
0268 {
0269 typedef model::point<SvgCoordinateType, 2, cs::cartesian> svg_point_type;
0270
0271 typedef typename geometry::select_most_precise
0272 <
0273 coordinate_type_t<Point>,
0274 double
0275 >::type calculation_type;
0276
0277 typedef strategy::transform::map_transformer
0278 <
0279 calculation_type,
0280 geometry::dimension<Point>::type::value,
0281 geometry::dimension<Point>::type::value,
0282 true,
0283 SameScale
0284 > transformer_type;
0285
0286 model::box<Point> m_bounding_box;
0287 std::unique_ptr<transformer_type> m_matrix;
0288 std::ostream& m_stream;
0289
0290 SvgCoordinateType const m_width;
0291 SvgCoordinateType const m_height;
0292 calculation_type const m_scale{1.0};
0293
0294 void scale_bounding_box()
0295 {
0296 if (m_scale != 1.0 && m_scale > 0)
0297 {
0298
0299
0300 auto& b = m_bounding_box;
0301 auto const w = geometry::get<1, 0>(b) - geometry::get<0, 0>(b);
0302 auto const h = geometry::get<1, 1>(b) - geometry::get<0, 1>(b);
0303
0304 auto const& m = (std::max)(w, h) * (1.0 - m_scale);
0305 geometry::set<0, 0>(b, geometry::get<0, 0>(b) - m);
0306 geometry::set<0, 1>(b, geometry::get<0, 1>(b) - m);
0307 geometry::set<1, 0>(b, geometry::get<1, 0>(b) + m);
0308 geometry::set<1, 1>(b, geometry::get<1, 1>(b) + m);
0309 }
0310 }
0311
0312 void init_matrix()
0313 {
0314 if (! m_matrix)
0315 {
0316 scale_bounding_box();
0317 m_matrix.reset(new transformer_type(m_bounding_box,
0318 m_width, m_height));
0319 }
0320 }
0321
0322 void write_header(std::string const& width_height)
0323 {
0324 m_stream << "<?xml version=\"1.0\" standalone=\"no\"?>"
0325 << std::endl
0326 << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\""
0327 << std::endl
0328 << "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">"
0329 << std::endl
0330 << "<svg " << width_height << " version=\"1.1\""
0331 << std::endl
0332 << "xmlns=\"http://www.w3.org/2000/svg\""
0333 << std::endl
0334 << "xmlns:xlink=\"http://www.w3.org/1999/xlink\""
0335 << ">"
0336 << std::endl;
0337 }
0338
0339 public :
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354 svg_mapper(std::ostream& stream
0355 , SvgCoordinateType width
0356 , SvgCoordinateType height
0357 , calculation_type scale
0358 , std::string const& width_height = "width=\"100%\" height=\"100%\"")
0359 : m_stream(stream)
0360 , m_width(width)
0361 , m_height(height)
0362 , m_scale(scale)
0363 {
0364 assign_inverse(m_bounding_box);
0365 write_header(width_height);
0366 }
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376 svg_mapper(std::ostream& stream
0377 , SvgCoordinateType width
0378 , SvgCoordinateType height
0379 , std::string const& width_height = "width=\"100%\" height=\"100%\"")
0380 : m_stream(stream)
0381 , m_width(width)
0382 , m_height(height)
0383 {
0384 assign_inverse(m_bounding_box);
0385 write_header(width_height);
0386 }
0387
0388
0389
0390
0391 virtual ~svg_mapper()
0392 {
0393 m_stream << "</svg>" << std::endl;
0394 }
0395
0396
0397
0398
0399
0400
0401
0402 template <typename Geometry>
0403 void add(Geometry const& geometry)
0404 {
0405 if (! geometry::is_empty(geometry))
0406 {
0407 expand(m_bounding_box,
0408 return_envelope
0409 <
0410 model::box<Point>
0411 >(geometry));
0412 }
0413 }
0414
0415
0416
0417
0418
0419
0420
0421
0422
0423 template <typename Geometry>
0424 void map(Geometry const& geometry, std::string const& style,
0425 double size = -1.0)
0426 {
0427 init_matrix();
0428 svg_map<svg_point_type>(m_stream, style, size, geometry, *m_matrix);
0429 }
0430
0431
0432
0433
0434
0435
0436
0437
0438
0439
0440
0441 template <typename TextPoint>
0442 void text(TextPoint const& point, std::string const& s,
0443 std::string const& style,
0444 double offset_x = 0.0, double offset_y = 0.0,
0445 double lineheight = 10.0)
0446 {
0447 init_matrix();
0448 svg_point_type map_point;
0449 transform(point, map_point, *m_matrix);
0450 m_stream
0451 << "<text style=\"" << style << "\""
0452 << " x=\"" << get<0>(map_point) + offset_x << "\""
0453 << " y=\"" << get<1>(map_point) + offset_y << "\""
0454 << ">";
0455 if (s.find('\n') == std::string::npos)
0456 {
0457 m_stream << s;
0458 }
0459 else
0460 {
0461
0462
0463 std::vector<std::string> split;
0464 boost::split(split, s, boost::is_any_of("\n"));
0465 for (auto const& item : split)
0466 {
0467 m_stream
0468 << "<tspan x=\"" << get<0>(map_point) + offset_x
0469 << "\""
0470 << " y=\"" << get<1>(map_point) + offset_y
0471 << "\""
0472 << ">" << item << "</tspan>";
0473 offset_y += lineheight;
0474 }
0475 }
0476 m_stream << "</text>" << std::endl;
0477 }
0478 };
0479
0480
0481 }}
0482
0483
0484 #endif