File indexing completed on 2026-08-07 08:47:52
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020 #ifndef BOOST_GEOMETRY_ALGORITHMS_CONVERT_HPP
0021 #define BOOST_GEOMETRY_ALGORITHMS_CONVERT_HPP
0022
0023
0024 #include <cstddef>
0025 #include <type_traits>
0026
0027 #include <boost/range/begin.hpp>
0028 #include <boost/range/end.hpp>
0029 #include <boost/range/size.hpp>
0030 #include <boost/range/value_type.hpp>
0031 #include <boost/variant/static_visitor.hpp>
0032 #include <boost/variant/variant_fwd.hpp>
0033
0034 #include <boost/geometry/algorithms/clear.hpp>
0035 #include <boost/geometry/algorithms/num_points.hpp>
0036 #include <boost/geometry/algorithms/detail/assign_box_corners.hpp>
0037 #include <boost/geometry/algorithms/detail/assign_indexed_point.hpp>
0038 #include <boost/geometry/algorithms/detail/convert_point_to_point.hpp>
0039 #include <boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp>
0040 #include <boost/geometry/algorithms/not_implemented.hpp>
0041
0042 #include <boost/geometry/core/closure.hpp>
0043 #include <boost/geometry/core/point_order.hpp>
0044 #include <boost/geometry/core/tag_cast.hpp>
0045 #include <boost/geometry/core/tags.hpp>
0046
0047 #include <boost/geometry/geometries/concepts/check.hpp>
0048
0049 #include <boost/geometry/util/numeric_cast.hpp>
0050 #include <boost/geometry/util/range.hpp>
0051
0052 #include <boost/geometry/views/detail/closed_clockwise_view.hpp>
0053
0054
0055 namespace boost { namespace geometry
0056 {
0057
0058
0059
0060 #if defined(_MSC_VER)
0061 #pragma warning(push)
0062 #pragma warning(disable : 4127 4512)
0063 #endif
0064
0065
0066 #ifndef DOXYGEN_NO_DETAIL
0067 namespace detail { namespace conversion
0068 {
0069
0070 template
0071 <
0072 typename Point,
0073 typename Box,
0074 std::size_t Index,
0075 std::size_t Dimension,
0076 std::size_t DimensionCount
0077 >
0078 struct point_to_box
0079 {
0080 static inline void apply(Point const& point, Box& box)
0081 {
0082 set<Index, Dimension>(box,
0083 util::numeric_cast<coordinate_type_t<Box>>(get<Dimension>(point)));
0084 point_to_box
0085 <
0086 Point, Box,
0087 Index, Dimension + 1, DimensionCount
0088 >::apply(point, box);
0089 }
0090 };
0091
0092
0093 template
0094 <
0095 typename Point,
0096 typename Box,
0097 std::size_t Index,
0098 std::size_t DimensionCount
0099 >
0100 struct point_to_box<Point, Box, Index, DimensionCount, DimensionCount>
0101 {
0102 static inline void apply(Point const& , Box& )
0103 {}
0104 };
0105
0106 template <bool Close, bool Reverse>
0107 struct box_to_range
0108 {
0109 template <typename Box, typename Range>
0110 static inline void apply(Box const& box, Range& range)
0111 {
0112 traits::resize<Range>::apply(range, Close ? 5 : 4);
0113 assign_box_corners_oriented<Reverse>(box, range);
0114 if (Close)
0115 {
0116 range::at(range, 4) = range::at(range, 0);
0117 }
0118 }
0119 };
0120
0121 template <typename Segment, typename Range>
0122 struct segment_to_range
0123 {
0124 static inline void apply(Segment const& segment, Range& range)
0125 {
0126 traits::resize<Range>::apply(range, 2);
0127
0128 auto it = boost::begin(range);
0129
0130 assign_point_from_index<0>(segment, *it);
0131 ++it;
0132 assign_point_from_index<1>(segment, *it);
0133 }
0134 };
0135
0136 struct range_to_range
0137 {
0138 struct default_policy
0139 {
0140 template <typename Point1, typename Point2>
0141 static inline void apply(Point1 const& point1, Point2 & point2)
0142 {
0143 geometry::detail::conversion::convert_point_to_point(point1, point2);
0144 }
0145 };
0146
0147 template <typename Range1, typename Range2>
0148 static inline void apply(Range1 const& source, Range2& destination)
0149 {
0150 apply(source, destination, default_policy());
0151 }
0152
0153 template <typename ConvertPointPolicy, typename Range1, typename Range2>
0154 static inline ConvertPointPolicy apply(Range1 const& source, Range2& destination,
0155 ConvertPointPolicy convert_point)
0156 {
0157 geometry::clear(destination);
0158
0159 constexpr bool reverse = geometry::point_order<Range1>::value != geometry::point_order<Range2>::value;
0160
0161 using view_type = detail::closed_clockwise_view
0162 <
0163 Range1 const,
0164 geometry::closure<Range1>::value,
0165 reverse ? counterclockwise : clockwise
0166 >;
0167
0168
0169
0170 view_type const view(source);
0171
0172 auto n = boost::size(view);
0173 if (geometry::closure<Range2>::value == geometry::open)
0174 {
0175 n--;
0176 }
0177
0178
0179
0180
0181 decltype(n) i = 0;
0182 for (auto it = boost::begin(view);
0183 it != boost::end(view) && i < n;
0184 ++it, ++i)
0185 {
0186 typename boost::range_value<Range2>::type point;
0187 convert_point.apply(*it, point);
0188 range::push_back(destination, point);
0189 }
0190
0191 return convert_point;
0192 }
0193 };
0194
0195
0196
0197 struct polygon_to_multi_range
0198 {
0199 template <typename Polygon, typename OutputIterator>
0200 static inline void apply(Polygon const& source, OutputIterator& it)
0201 {
0202 range_to_range::apply(geometry::exterior_ring(source), *it++);
0203 for (auto const& ring : geometry::interior_rings(source))
0204 {
0205 range_to_range::apply(ring, *it++);
0206 }
0207 }
0208 };
0209
0210 template <typename Polygon1, typename Polygon2>
0211 struct polygon_to_polygon
0212 {
0213 static inline void apply(Polygon1 const& source, Polygon2& destination)
0214 {
0215 range_to_range::apply(geometry::exterior_ring(source),
0216 geometry::exterior_ring(destination));
0217
0218
0219 traits::resize
0220 <
0221 std::remove_reference_t
0222 <
0223 typename traits::interior_mutable_type<Polygon2>::type
0224 >
0225 >::apply(interior_rings(destination), num_interior_rings(source));
0226
0227 auto const& rings_source = interior_rings(source);
0228 auto&& rings_dest = interior_rings(destination);
0229
0230 auto it_source = boost::begin(rings_source);
0231 auto it_dest = boost::begin(rings_dest);
0232
0233 for ( ; it_source != boost::end(rings_source); ++it_source, ++it_dest)
0234 {
0235 range_to_range::apply(*it_source, *it_dest);
0236 }
0237 }
0238 };
0239
0240 struct range_to_multi_point
0241 {
0242 template <typename Range, typename Iterator>
0243 static inline void append_from_source(Range const& source, Iterator& it)
0244 {
0245 for (auto const& point : source)
0246 {
0247 detail::conversion::convert_point_to_point(point, *it);
0248 ++it;
0249 }
0250 }
0251
0252 template <typename Range, typename MultiPoint>
0253 static inline void apply(Range const& source, MultiPoint& destination)
0254 {
0255 traits::resize<MultiPoint>::apply(destination, boost::size(source));
0256 auto it = boost::begin(destination);
0257 append_from_source(source, it);
0258 }
0259 };
0260
0261 struct polygon_to_range
0262 {
0263 template <typename Polygon, typename Iterator>
0264 static inline void append_from_polygon(Polygon const& source, Iterator& it)
0265 {
0266 range_to_multi_point::append_from_source(geometry::exterior_ring(source), it);
0267 for (auto const& ring : geometry::interior_rings(source))
0268 {
0269 range_to_multi_point::append_from_source(ring, it);
0270 }
0271 }
0272
0273 template <typename Polygon, typename MultiPoint>
0274 static inline void apply(Polygon const& source, MultiPoint& destination)
0275 {
0276 traits::resize<MultiPoint>::apply(destination, geometry::num_points(source));
0277 auto it = boost::begin(destination);
0278 append_from_polygon(source, it);
0279 }
0280 };
0281
0282 struct multi_polygon_to_range
0283 {
0284 template <typename MultiPolygon, typename MultiPoint>
0285 static inline void apply(MultiPolygon const& source, MultiPoint& destination)
0286 {
0287 traits::resize<MultiPoint>::apply(destination, geometry::num_points(source));
0288 auto it = boost::begin(destination);
0289 for (auto const& polygon : source)
0290 {
0291 polygon_to_range::append_from_polygon(polygon, it);
0292 }
0293 }
0294 };
0295
0296 template <typename Single, typename Multi, typename ConversionAlgorithm>
0297 struct single_to_multi
0298 {
0299 static inline void apply(Single const& single, Multi& multi)
0300 {
0301 traits::resize<Multi>::apply(multi, 1);
0302 ConversionAlgorithm::apply(single, *boost::begin(multi));
0303 }
0304 };
0305
0306
0307 template <typename Multi1, typename Multi2, typename ConversionAlgorithm>
0308 struct multi_to_multi
0309 {
0310 static inline void apply(Multi1 const& multi1, Multi2& multi2)
0311 {
0312 traits::resize<Multi2>::apply(multi2, boost::size(multi1));
0313
0314 auto it1 = boost::begin(multi1);
0315 auto it2 = boost::begin(multi2);
0316
0317 for (; it1 != boost::end(multi1); ++it1, ++it2)
0318 {
0319 ConversionAlgorithm::apply(*it1, *it2);
0320 }
0321 }
0322 };
0323
0324
0325 }}
0326 #endif
0327
0328
0329 #ifndef DOXYGEN_NO_DISPATCH
0330 namespace dispatch
0331 {
0332
0333
0334
0335
0336
0337 template
0338 <
0339 typename Geometry1, typename Geometry2,
0340 typename Tag1 = tag_t<Geometry1>,
0341 typename Tag2 = tag_t<Geometry2>,
0342 std::size_t DimensionCount = dimension<Geometry1>::value,
0343 bool UseAssignment = std::is_same<Geometry1, Geometry2>::value
0344 && !std::is_array<Geometry1>::value
0345 >
0346 struct convert
0347 : not_implemented
0348 <
0349 Tag1, Tag2,
0350 std::integral_constant<std::size_t, DimensionCount>
0351 >
0352 {};
0353
0354
0355 template
0356 <
0357 typename Geometry1, typename Geometry2,
0358 typename Tag,
0359 std::size_t DimensionCount
0360 >
0361 struct convert<Geometry1, Geometry2, Tag, Tag, DimensionCount, true>
0362 {
0363
0364 static inline void apply(Geometry1 const& source, Geometry2& destination)
0365 {
0366 destination = source;
0367 }
0368 };
0369
0370
0371 template <typename Geometry1, typename Geometry2, std::size_t DimensionCount>
0372 struct convert<Geometry1, Geometry2, point_tag, point_tag, DimensionCount, false>
0373 : detail::conversion::point_to_point<Geometry1, Geometry2, 0, DimensionCount>
0374 {};
0375
0376
0377 template <typename Box1, typename Box2, std::size_t DimensionCount>
0378 struct convert<Box1, Box2, box_tag, box_tag, DimensionCount, false>
0379 : detail::conversion::indexed_to_indexed<Box1, Box2, 0, DimensionCount>
0380 {};
0381
0382
0383 template <typename Segment1, typename Segment2, std::size_t DimensionCount>
0384 struct convert<Segment1, Segment2, segment_tag, segment_tag, DimensionCount, false>
0385 : detail::conversion::indexed_to_indexed<Segment1, Segment2, 0, DimensionCount>
0386 {};
0387
0388
0389 template <typename Segment, typename LineString, std::size_t DimensionCount>
0390 struct convert<Segment, LineString, segment_tag, linestring_tag, DimensionCount, false>
0391 : detail::conversion::segment_to_range<Segment, LineString>
0392 {};
0393
0394
0395 template <typename Ring1, typename Ring2, std::size_t DimensionCount>
0396 struct convert<Ring1, Ring2, ring_tag, ring_tag, DimensionCount, false>
0397 : detail::conversion::range_to_range
0398 {};
0399
0400 template <typename LineString1, typename LineString2, std::size_t DimensionCount>
0401 struct convert<LineString1, LineString2, linestring_tag, linestring_tag, DimensionCount, false>
0402 : detail::conversion::range_to_range
0403 {};
0404
0405 template <typename Polygon1, typename Polygon2, std::size_t DimensionCount>
0406 struct convert<Polygon1, Polygon2, polygon_tag, polygon_tag, DimensionCount, false>
0407 : detail::conversion::polygon_to_polygon<Polygon1, Polygon2>
0408 {};
0409
0410 template <typename Box, typename LineString>
0411 struct convert<Box, LineString, box_tag, linestring_tag, 2, false>
0412 : detail::conversion::box_to_range<true, false>
0413 {};
0414
0415 template <typename Box, typename Ring>
0416 struct convert<Box, Ring, box_tag, ring_tag, 2, false>
0417 : detail::conversion::box_to_range
0418 <
0419 geometry::closure<Ring>::value == closed,
0420 geometry::point_order<Ring>::value == counterclockwise
0421 >
0422 {};
0423
0424 template <typename Box, typename MultiPoint>
0425 struct convert<Box, MultiPoint, box_tag, multi_point_tag, 2, false>
0426 : detail::conversion::box_to_range<false, false>
0427 {};
0428
0429
0430 template <typename Box, typename Polygon>
0431 struct convert<Box, Polygon, box_tag, polygon_tag, 2, false>
0432 {
0433 static inline void apply(Box const& box, Polygon& polygon)
0434 {
0435 convert
0436 <
0437 Box, ring_type_t<Polygon>,
0438 box_tag, ring_tag,
0439 2, false
0440 >::apply(box, exterior_ring(polygon));
0441 }
0442 };
0443
0444
0445 template <typename Point, typename Box, std::size_t DimensionCount>
0446 struct convert<Point, Box, point_tag, box_tag, DimensionCount, false>
0447 {
0448 static inline void apply(Point const& point, Box& box)
0449 {
0450 detail::conversion::point_to_box
0451 <
0452 Point, Box, min_corner, 0, DimensionCount
0453 >::apply(point, box);
0454 detail::conversion::point_to_box
0455 <
0456 Point, Box, max_corner, 0, DimensionCount
0457 >::apply(point, box);
0458 }
0459 };
0460
0461
0462 template <typename Ring, typename Polygon, std::size_t DimensionCount>
0463 struct convert<Ring, Polygon, ring_tag, polygon_tag, DimensionCount, false>
0464 {
0465 static inline void apply(Ring const& ring, Polygon& polygon)
0466 {
0467 convert
0468 <
0469 Ring, ring_type_t<Polygon>,
0470 ring_tag, ring_tag,
0471 DimensionCount, false
0472 >::apply(ring, exterior_ring(polygon));
0473 }
0474 };
0475
0476
0477 template <typename Polygon, typename Ring, std::size_t DimensionCount>
0478 struct convert<Polygon, Ring, polygon_tag, ring_tag, DimensionCount, false>
0479 {
0480 static inline void apply(Polygon const& polygon, Ring& ring)
0481 {
0482 convert
0483 <
0484 ring_type_t<Polygon>, Ring,
0485 ring_tag, ring_tag,
0486 DimensionCount, false
0487 >::apply(exterior_ring(polygon), ring);
0488 }
0489 };
0490
0491 template <typename LineString, typename MultiPoint, std::size_t DimensionCount>
0492 struct convert<LineString, MultiPoint, linestring_tag, multi_point_tag, DimensionCount, false>
0493 : detail::conversion::range_to_multi_point {};
0494
0495 template <typename Ring, typename MultiPoint, std::size_t DimensionCount>
0496 struct convert<Ring, MultiPoint, ring_tag, multi_point_tag, DimensionCount, false>
0497 : detail::conversion::range_to_multi_point {};
0498
0499 template <typename Polygon, typename MultiPoint, std::size_t DimensionCount>
0500 struct convert<Polygon, MultiPoint, polygon_tag, multi_point_tag, DimensionCount, false>
0501 : detail::conversion::polygon_to_range {};
0502
0503 template <typename MultiPolygon, typename MultiPoint, std::size_t DimensionCount>
0504 struct convert<MultiPolygon, MultiPoint, multi_polygon_tag, multi_point_tag, DimensionCount, false>
0505 : detail::conversion::multi_polygon_to_range {};
0506
0507 template <typename MultiLineString, typename MultiPoint, std::size_t DimensionCount>
0508 struct convert<MultiLineString, MultiPoint, multi_linestring_tag, multi_point_tag, DimensionCount, false>
0509 {
0510 static inline void apply(MultiLineString const& source, MultiPoint& destination)
0511 {
0512 traits::resize<MultiPoint>::apply(destination, geometry::num_points(source));
0513 auto it = boost::begin(destination);
0514 for (auto const& linestring : source)
0515 {
0516 detail::conversion::range_to_multi_point::append_from_source(linestring, it);
0517 }
0518 }
0519 };
0520
0521 template <typename Ring, typename MultiLinestring, std::size_t DimensionCount>
0522 struct convert<Ring, MultiLinestring, ring_tag, multi_linestring_tag, DimensionCount, false>
0523 {
0524 static inline void apply(Ring const& source, MultiLinestring& destination)
0525 {
0526 traits::resize<MultiLinestring>::apply(destination, 1);
0527 detail::conversion::range_to_range::apply(source, *boost::begin(destination));
0528 }
0529 };
0530
0531 template <typename Polygon, typename MultiLinestring, std::size_t DimensionCount>
0532 struct convert<Polygon, MultiLinestring, polygon_tag, multi_linestring_tag, DimensionCount, false>
0533 {
0534 static inline void apply(Polygon const& source, MultiLinestring& destination)
0535 {
0536 traits::resize<MultiLinestring>::apply(destination, 1 + geometry::num_interior_rings(source));
0537 auto it = boost::begin(destination);
0538 detail::conversion::polygon_to_multi_range::apply(source, it);
0539 }
0540 };
0541
0542 template <typename MultiPolygon, typename MultiLinestring, std::size_t DimensionCount>
0543 struct convert<MultiPolygon, MultiLinestring, multi_polygon_tag, multi_linestring_tag, DimensionCount, false>
0544 {
0545 static inline void apply(MultiPolygon const& source, MultiLinestring& destination)
0546 {
0547 std::size_t ring_count = boost::size(source);
0548 for (auto const& polygon : source)
0549 {
0550 ring_count += geometry::num_interior_rings(polygon);
0551 }
0552 traits::resize<MultiLinestring>::apply(destination, ring_count);
0553
0554 auto it = boost::begin(destination);
0555 for (auto const& polygon : source)
0556 {
0557 detail::conversion::polygon_to_multi_range::apply(polygon, it);
0558 }
0559 }
0560 };
0561
0562 template <typename Single, typename Multi, std::size_t DimensionCount>
0563 struct single_to_multi_convert
0564 : detail::conversion::single_to_multi
0565 <
0566 Single,
0567 Multi,
0568 convert
0569 <
0570 Single,
0571 typename boost::range_value<Multi>::type,
0572 tag_t<Single>,
0573 single_tag_of_t<tag_t<Multi>>,
0574 DimensionCount,
0575 false
0576 >
0577 >
0578 {};
0579
0580 template <typename Multi1, typename Multi2, std::size_t DimensionCount>
0581 struct multi_to_multi_convert
0582 : detail::conversion::multi_to_multi
0583 <
0584 Multi1,
0585 Multi2,
0586 convert
0587 <
0588 typename boost::range_value<Multi1>::type,
0589 typename boost::range_value<Multi2>::type,
0590 single_tag_of_t<tag_t<Multi1>>,
0591 single_tag_of_t<tag_t<Multi2>>,
0592 DimensionCount
0593 >
0594 >
0595 {};
0596
0597
0598 template <typename GeometryIn, typename GeometryOut, std::size_t DimensionCount>
0599 struct convert<GeometryIn, GeometryOut, multi_point_tag, multi_point_tag, DimensionCount, false>
0600 : multi_to_multi_convert<GeometryIn, GeometryOut, DimensionCount> {};
0601
0602 template <typename GeometryIn, typename GeometryOut, std::size_t DimensionCount>
0603 struct convert<GeometryIn, GeometryOut, multi_linestring_tag, multi_linestring_tag, DimensionCount, false>
0604 : multi_to_multi_convert<GeometryIn, GeometryOut, DimensionCount> {};
0605
0606 template <typename GeometryIn, typename GeometryOut, std::size_t DimensionCount>
0607 struct convert<GeometryIn, GeometryOut, multi_polygon_tag, multi_polygon_tag, DimensionCount, false>
0608 : multi_to_multi_convert<GeometryIn, GeometryOut, DimensionCount> {};
0609
0610
0611 template <typename GeometryIn, typename GeometryOut, std::size_t DimensionCount>
0612 struct convert<GeometryIn, GeometryOut, point_tag, multi_point_tag, DimensionCount, false>
0613 : single_to_multi_convert<GeometryIn, GeometryOut, DimensionCount> {};
0614
0615 template <typename GeometryIn, typename GeometryOut, std::size_t DimensionCount>
0616 struct convert<GeometryIn, GeometryOut, segment_tag, multi_linestring_tag, DimensionCount, false>
0617 : single_to_multi_convert<GeometryIn, GeometryOut, DimensionCount> {};
0618
0619 template <typename GeometryIn, typename GeometryOut, std::size_t DimensionCount>
0620 struct convert<GeometryIn, GeometryOut, linestring_tag, multi_linestring_tag, DimensionCount, false>
0621 : single_to_multi_convert<GeometryIn, GeometryOut, DimensionCount> {};
0622
0623
0624 template <typename GeometryIn, typename GeometryOut, std::size_t DimensionCount>
0625 struct convert<GeometryIn, GeometryOut, box_tag, multi_polygon_tag, DimensionCount, false>
0626 : single_to_multi_convert<GeometryIn, GeometryOut, DimensionCount> {};
0627
0628 template <typename GeometryIn, typename GeometryOut, std::size_t DimensionCount>
0629 struct convert<GeometryIn, GeometryOut, ring_tag, multi_polygon_tag, DimensionCount, false>
0630 : single_to_multi_convert<GeometryIn, GeometryOut, DimensionCount> {};
0631
0632 template <typename GeometryIn, typename GeometryOut, std::size_t DimensionCount>
0633 struct convert<GeometryIn, GeometryOut, polygon_tag, multi_polygon_tag, DimensionCount, false>
0634 : single_to_multi_convert<GeometryIn, GeometryOut, DimensionCount> {};
0635
0636
0637 }
0638 #endif
0639
0640
0641 namespace resolve_variant {
0642
0643 template <typename Geometry1, typename Geometry2>
0644 struct convert
0645 {
0646 static inline void apply(Geometry1 const& geometry1, Geometry2& geometry2)
0647 {
0648 concepts::check_concepts_and_equal_dimensions<Geometry1 const, Geometry2>();
0649 dispatch::convert<Geometry1, Geometry2>::apply(geometry1, geometry2);
0650 }
0651 };
0652
0653 template <BOOST_VARIANT_ENUM_PARAMS(typename T), typename Geometry2>
0654 struct convert<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>, Geometry2>
0655 {
0656 struct visitor: static_visitor<void>
0657 {
0658 Geometry2& m_geometry2;
0659
0660 visitor(Geometry2& geometry2)
0661 : m_geometry2(geometry2)
0662 {}
0663
0664 template <typename Geometry1>
0665 inline void operator()(Geometry1 const& geometry1) const
0666 {
0667 convert<Geometry1, Geometry2>::apply(geometry1, m_geometry2);
0668 }
0669 };
0670
0671 static inline void apply(
0672 boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry1,
0673 Geometry2& geometry2
0674 )
0675 {
0676 boost::apply_visitor(visitor(geometry2), geometry1);
0677 }
0678 };
0679
0680 }
0681
0682
0683
0684
0685
0686
0687
0688
0689
0690
0691
0692
0693
0694
0695
0696
0697
0698 template <typename Geometry1, typename Geometry2>
0699 inline void convert(Geometry1 const& geometry1, Geometry2& geometry2)
0700 {
0701 resolve_variant::convert<Geometry1, Geometry2>::apply(geometry1, geometry2);
0702 }
0703
0704 #if defined(_MSC_VER)
0705 #pragma warning(pop)
0706 #endif
0707
0708 }}
0709
0710 #endif