• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

boostorg / geometry / 24470

13 May 2025 08:17PM UTC coverage: 94.253% (-0.03%) from 94.286%
24470

push

circleci

barendgehrels
fix: consider clusters in turn_in_piece_visitor

39085 of 41468 relevant lines covered (94.25%)

1274129.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.25
include/boost/geometry/io/wkt/read.hpp
1
// Boost.Geometry (aka GGL, Generic Geometry Library)
2

3
// Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands.
4
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
5
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
6
// Copyright (c) 2017-2023 Adam Wulkiewicz, Lodz, Poland.
7
// Copyright (c) 2020 Baidyanath Kundu, Haldia, India
8

9
// This file was modified by Oracle on 2014-2021.
10
// Modifications copyright (c) 2014-2021 Oracle and/or its affiliates.
11
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
12

13
// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
14
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
15

16
// Use, modification and distribution is subject to the Boost Software License,
17
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
18
// http://www.boost.org/LICENSE_1_0.txt)
19

20
#ifndef BOOST_GEOMETRY_IO_WKT_READ_HPP
21
#define BOOST_GEOMETRY_IO_WKT_READ_HPP
22

23
#include <cstddef>
24
#include <string>
25

26
#include <boost/algorithm/string/predicate.hpp>
27
#include <boost/lexical_cast.hpp>
28
#include <boost/range/begin.hpp>
29
#include <boost/range/end.hpp>
30
#include <boost/range/size.hpp>
31
#include <boost/range/value_type.hpp>
32
#include <boost/tokenizer.hpp>
33
#include <boost/throw_exception.hpp>
34

35
#include <boost/geometry/algorithms/assign.hpp>
36
#include <boost/geometry/algorithms/append.hpp>
37
#include <boost/geometry/algorithms/clear.hpp>
38
#include <boost/geometry/algorithms/detail/disjoint/point_point.hpp>
39

40
#include <boost/geometry/core/access.hpp>
41
#include <boost/geometry/core/coordinate_dimension.hpp>
42
#include <boost/geometry/core/exception.hpp>
43
#include <boost/geometry/core/exterior_ring.hpp>
44
#include <boost/geometry/core/geometry_id.hpp>
45
#include <boost/geometry/core/geometry_types.hpp>
46
#include <boost/geometry/core/interior_rings.hpp>
47
#include <boost/geometry/core/mutable_range.hpp>
48
#include <boost/geometry/core/point_type.hpp>
49
#include <boost/geometry/core/tag.hpp>
50
#include <boost/geometry/core/tags.hpp>
51

52
#include <boost/geometry/geometries/adapted/boost_variant.hpp> // For consistency with other functions
53
#include <boost/geometry/geometries/concepts/check.hpp>
54

55
#include <boost/geometry/io/wkt/detail/prefix.hpp>
56

57
#include <boost/geometry/strategies/io/cartesian.hpp>
58
#include <boost/geometry/strategies/io/geographic.hpp>
59
#include <boost/geometry/strategies/io/spherical.hpp>
60

61
#include <boost/geometry/util/coordinate_cast.hpp>
62
#include <boost/geometry/util/range.hpp>
63
#include <boost/geometry/util/sequence.hpp>
64
#include <boost/geometry/util/type_traits.hpp>
65

66
namespace boost { namespace geometry
67
{
68

69
/*!
70
\brief Exception showing things wrong with WKT parsing
71
\ingroup wkt
72
*/
73
struct read_wkt_exception : public geometry::exception
74
{
75
    template <typename Iterator>
76
    read_wkt_exception(std::string const& msg,
64✔
77
                       Iterator const& it,
78
                       Iterator const& end,
79
                       std::string const& wkt)
80
        : message(msg)
81
        , wkt(wkt)
64✔
82
    {
83
        if (it != end)
64✔
84
        {
85
            source = " at '";
56✔
86
            source += it->c_str();
56✔
87
            source += "'";
56✔
88
        }
89
        complete = message + source + " in '" + wkt.substr(0, 100) + "'";
64✔
90
    }
64✔
91

92
    read_wkt_exception(std::string const& msg, std::string const& wkt)
12✔
93
        : message(msg)
12✔
94
        , wkt(wkt)
12✔
95
    {
96
        complete = message + "' in (" + wkt.substr(0, 100) + ")";
12✔
97
    }
12✔
98

99
    const char* what() const noexcept override
76✔
100
    {
101
        return complete.c_str();
76✔
102
    }
103
private :
104
    std::string source;
105
    std::string message;
106
    std::string wkt;
107
    std::string complete;
108
};
109

110

111
#ifndef DOXYGEN_NO_DETAIL
112
// (wkt: Well Known Text, defined by OGC for all geometries and implemented by e.g. databases (MySQL, PostGIS))
113
namespace detail { namespace wkt
114
{
115

116
inline auto make_tokenizer(std::string const& wkt)
42,298✔
117
{
118
    using separator = boost::char_separator<char>;
119
    using tokenizer = boost::tokenizer<separator>;
120
    const tokenizer tokens(wkt, separator(" \n\t\r", ",()"));
42,298✔
121
    return tokens;
42,298✔
122
}
123

124
template <typename Point,
125
          std::size_t Dimension = 0,
126
          std::size_t DimensionCount = geometry::dimension<Point>::value>
127
struct parsing_assigner
128
{
129
    template <typename TokenizerIterator>
130
    static inline void apply(TokenizerIterator& it,
546,513✔
131
                             TokenizerIterator const& end,
132
                             Point& point,
133
                             std::string const& wkt)
134
    {
135
        using coordinate_type = coordinate_type_t<Point>;
136

137
        // Stop at end of tokens, or at "," ot ")"
138
        bool finished = (it == end || *it == "," || *it == ")");
546,513✔
139

140
        try
141
        {
142
            // Initialize missing coordinates to default constructor (zero)
143
            // OR
144
            // Use lexical_cast for conversion to double/int
145
            // Note that it is much slower than atof. However, it is more standard
146
            // and in parsing the change in performance falls probably away against
147
            // the tokenizing
148
            set<Dimension>(point, finished
546,501✔
149
                    ? coordinate_type()
1,090,835✔
150
                    : coordinate_cast<coordinate_type>::apply(*it));
546,244✔
151
        }
152
        catch(boost::bad_lexical_cast const& blc)
24✔
153
        {
154
            BOOST_THROW_EXCEPTION(read_wkt_exception(blc.what(), it, end, wkt));
48✔
155
        }
156
        catch(std::exception const& e)
×
157
        {
158
            BOOST_THROW_EXCEPTION(read_wkt_exception(e.what(), it, end, wkt));
×
159
        }
160
        catch(...)
×
161
        {
162
            BOOST_THROW_EXCEPTION(read_wkt_exception("", it, end, wkt));
×
163
        }
164

165
        parsing_assigner<Point, Dimension + 1, DimensionCount>::apply(
1,092,733✔
166
                        (finished ? it : ++it), end, point, wkt);
546,232✔
167
    }
546,501✔
168
};
169

170
template <typename Point, std::size_t DimensionCount>
171
struct parsing_assigner<Point, DimensionCount, DimensionCount>
172
{
173
    template <typename TokenizerIterator>
174
    static inline void apply(TokenizerIterator&,
272,779✔
175
                             TokenizerIterator const&,
176
                             Point&,
177
                             std::string const&)
178
    {
179
    }
272,779✔
180
};
181

182

183

184
template <typename Iterator>
185
inline void handle_open_parenthesis(Iterator& it,
87,351✔
186
                                    Iterator const& end,
187
                                    std::string const& wkt)
188
{
189
    if (it == end || *it != "(")
87,351✔
190
    {
191
        BOOST_THROW_EXCEPTION(read_wkt_exception("Expected '('", it, end, wkt));
64✔
192
    }
193
    ++it;
87,335✔
194
}
87,335✔
195

196

197
template <typename Iterator>
198
inline void handle_close_parenthesis(Iterator& it,
87,307✔
199
                                     Iterator const& end,
200
                                     std::string const& wkt)
201
{
202
    if (it != end && *it == ")")
87,307✔
203
    {
204
        ++it;
87,291✔
205
    }
206
    else
207
    {
208
        BOOST_THROW_EXCEPTION(read_wkt_exception("Expected ')'", it, end, wkt));
64✔
209
    }
210
}
87,291✔
211

212
template <typename Iterator>
213
inline void check_end(Iterator& it,
42,242✔
214
                      Iterator const& end,
215
                      std::string const& wkt)
216
{
217
    if (it != end)
42,242✔
218
    {
219
        BOOST_THROW_EXCEPTION(read_wkt_exception("Too many tokens", it, end, wkt));
80✔
220
    }
221
}
42,222✔
222

223
/*!
224
\brief Internal, parses coordinate sequences, strings are formated like "(1 2,3 4,...)"
225
\param it token-iterator, should be pre-positioned at "(", is post-positions after last ")"
226
\param end end-token-iterator
227
\param out Output itererator receiving coordinates
228
*/
229
template <typename Point>
230
struct container_inserter
231
{
232
    // Version with output iterator
233
    template <typename TokenizerIterator, typename OutputIterator>
234
    static inline void apply(TokenizerIterator& it,
8,172✔
235
                             TokenizerIterator const& end,
236
                             std::string const& wkt,
237
                             OutputIterator out)
238
    {
239
        handle_open_parenthesis(it, end, wkt);
8,172✔
240

241
        Point point;
34✔
242

243
        // Parse points until closing parenthesis
244

245
        while (it != end && *it != ")")
24,605✔
246
        {
247
            parsing_assigner<Point>::apply(it, end, point, wkt);
16,433✔
248
            out = point;
16,433✔
249
            ++out;
16,433✔
250
            if (it != end && *it == ",")
16,433✔
251
            {
252
                ++it;
8,261✔
253
            }
254
        }
255

256
        handle_close_parenthesis(it, end, wkt);
8,172✔
257
    }
8,172✔
258
};
259

260

261
template <typename Geometry,
262
          closure_selector Closure = closure<Geometry>::value>
263
struct stateful_range_appender
264
{
265
    // NOTE: Geometry is a reference
266
    inline void append(Geometry geom, geometry::point_type_t<Geometry> const& point, bool)
232,213✔
267
    {
268
        geometry::append(geom, point);
232,213✔
269
    }
232,213✔
270
};
271

272
template <typename Geometry>
273
struct stateful_range_appender<Geometry, open>
274
{
275
    using point_type = geometry::point_type_t<Geometry>;
276
    using size_type = typename boost::range_size<util::remove_cptrref_t<Geometry>>::type;
277

278
    BOOST_STATIC_ASSERT((util::is_ring<Geometry>::value));
279

280
    inline stateful_range_appender()
2,325✔
281
        : pt_index(0)
2,325✔
282
    {}
2,325✔
283

284
    // NOTE: Geometry is a reference
285
    inline void append(Geometry geom, point_type const& point, bool is_next_expected)
10,588✔
286
    {
287
        bool should_append = true;
10,588✔
288

289
        if (pt_index == 0)
10,588✔
290
        {
291
            first_point = point;
2,186✔
292
        }
293
        else
294
        {
295
            // NOTE: if there are not enough Points, they're always appended
296
            should_append
8,402✔
297
                = is_next_expected
298
                || pt_index < core_detail::closure::minimum_ring_size<open>::value
2,148✔
299
                || disjoint(point, first_point);
10,550✔
300
        }
301
        ++pt_index;
10,588✔
302

303
        if (should_append)
10,588✔
304
        {
305
            geometry::append(geom, point);
10,125✔
306
        }
307
    }
10,588✔
308

309
private:
310
    static inline bool disjoint(point_type const& p1, point_type const& p2)
1,662✔
311
    {
312
        // TODO: pass strategy
313
        using strategy_type = typename strategies::io::services::default_strategy
314
            <
315
                point_type
316
            >::type;
317

318
        return detail::disjoint::disjoint_point_point(p1, p2, strategy_type());
1,662✔
319
    }
320

321
    size_type pt_index;
322
    point_type first_point;
323
};
324

325
// Geometry is a value-type or reference-type
326
template <typename Geometry>
327
struct container_appender
328
{
329
    using point_type = geometry::point_type_t<Geometry>;
330

331
    template <typename TokenizerIterator>
332
    static inline void apply(TokenizerIterator& it,
40,442✔
333
                             TokenizerIterator const& end,
334
                             std::string const& wkt,
335
                             Geometry out)
336
    {
337
        handle_open_parenthesis(it, end, wkt);
40,442✔
338

339
        stateful_range_appender<Geometry> appender;
2,325✔
340

341
        // Parse points until closing parenthesis
342
        while (it != end && *it != ")")
283,239✔
343
        {
344
            point_type point;
1,526✔
345

346
            parsing_assigner<point_type>::apply(it, end, point, wkt);
242,805✔
347

348
            bool const is_next_expected = it != end && *it == ",";
242,801✔
349

350
            appender.append(out, point, is_next_expected);
242,801✔
351

352
            if (is_next_expected)
242,801✔
353
            {
354
                ++it;
202,994✔
355
            }
356
        }
357

358
        handle_close_parenthesis(it, end, wkt);
40,434✔
359
    }
40,434✔
360
};
361

362
/*!
363
\brief Internal, parses a point from a string like this "(x y)"
364
\note used for parsing points and multi-points
365
*/
366
template <typename P>
367
struct point_parser
368
{
369
    template <typename TokenizerIterator>
370
    static inline void apply(TokenizerIterator& it,
8,664✔
371
                             TokenizerIterator const& end,
372
                             std::string const& wkt,
373
                             P& point)
374
    {
375
        handle_open_parenthesis(it, end, wkt);
8,664✔
376
        parsing_assigner<P>::apply(it, end, point, wkt);
8,652✔
377
        handle_close_parenthesis(it, end, wkt);
8,648✔
378
    }
8,636✔
379
};
380

381

382
template <typename Geometry>
383
struct linestring_parser
384
{
385
    template <typename TokenizerIterator>
386
    static inline void apply(TokenizerIterator& it,
15,629✔
387
                             TokenizerIterator const& end,
388
                             std::string const& wkt,
389
                             Geometry& geometry)
390
    {
391
        container_appender<Geometry&>::apply(it, end, wkt, geometry);
15,629✔
392
    }
15,629✔
393
};
394

395

396
template <typename Ring>
397
struct ring_parser
398
{
399
    template <typename TokenizerIterator>
400
    static inline void apply(TokenizerIterator& it,
1,505✔
401
                             TokenizerIterator const& end,
402
                             std::string const& wkt,
403
                             Ring& ring)
404
    {
405
        // A ring should look like polygon((x y,x y,x y...))
406
        // So handle the extra opening/closing parentheses
407
        // and in between parse using the container-inserter
408
        handle_open_parenthesis(it, end, wkt);
1,505✔
409
        container_appender<Ring&>::apply(it, end, wkt, ring);
1,505✔
410
        handle_close_parenthesis(it, end, wkt);
1,505✔
411
    }
1,505✔
412
};
413

414

415
/*!
416
\brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
417
\note used for parsing polygons and multi-polygons
418
*/
419
template <typename Polygon>
420
struct polygon_parser
421
{
422
    using appender = container_appender<ring_return_type_t<Polygon>>;
423

424
    template <typename TokenizerIterator>
425
    static inline void apply(TokenizerIterator& it,
18,513✔
426
                             TokenizerIterator const& end,
427
                             std::string const& wkt,
428
                             Polygon& poly)
429
    {
430

431
        handle_open_parenthesis(it, end, wkt);
18,513✔
432

433
        int n = -1;
18,513✔
434

435
        // Stop at ")"
436
        while (it != end && *it != ")")
41,813✔
437
        {
438
            // Parse ring
439
            if (++n == 0)
23,308✔
440
            {
441
                appender::apply(it, end, wkt, exterior_ring(poly));
18,502✔
442
            }
443
            else
444
            {
445
                ring_type_t<Polygon> ring;
9,608✔
446
                appender::apply(it, end, wkt, ring);
4,806✔
447
                range::push_back(geometry::interior_rings(poly), std::move(ring));
4,802✔
448
            }
449

450
            if (it != end && *it == ",")
23,300✔
451
            {
452
                // Skip "," after ring is parsed
453
                ++it;
4,579✔
454
            }
455
        }
456

457
        handle_close_parenthesis(it, end, wkt);
18,505✔
458
    }
18,505✔
459
};
460

461
template<typename PolyhedralSurface>
462
struct polyhedral_surface_parser
463
{
464
    using polygon_t = typename PolyhedralSurface::polygon_type;
465

466
    template <typename TokenizerIterator>
467
    static inline void apply(TokenizerIterator& it,
1✔
468
                             TokenizerIterator const& end,
469
                             std::string const& wkt,
470
                             PolyhedralSurface& polyhedral)
471
    {
472
        handle_open_parenthesis(it, end, wkt);
1✔
473

474
        // Parse polygons
475
        while (it != end && *it != ")")
3✔
476
        {
477
            traits::resize<PolyhedralSurface>::apply(polyhedral, boost::size(polyhedral) + 1);
2✔
478
            polygon_parser<polygon_t>::apply(it, end, wkt, *(boost::end(polyhedral) - 1));
2✔
479
            if (it != end && *it == ",")
2✔
480
            {
481
                // Skip "," after multi-element is parsed
482
                ++it;
1✔
483
            }
484
        }
485

486
        handle_close_parenthesis(it, end, wkt);
1✔
487
    }
1✔
488
};
489

490

491
template <typename TokenizerIterator>
492
inline bool one_of(TokenizerIterator const& it,
104,346✔
493
                   std::string const& value,
494
                   bool& is_present)
495
{
496
    if (boost::iequals(*it, value))
104,346✔
497
    {
498
        is_present = true;
46✔
499
        return true;
46✔
500
    }
501
    return false;
104,300✔
502
}
503

504
template <typename TokenizerIterator>
505
inline bool one_of(TokenizerIterator const& it,
69,480✔
506
                   std::string const& value,
507
                   bool& present1,
508
                   bool& present2)
509
{
510
    if (boost::iequals(*it, value))
69,480✔
511
    {
512
        present1 = true;
×
513
        present2 = true;
×
514
        return true;
×
515
    }
516
    return false;
69,480✔
517
}
518

519

520
template <typename TokenizerIterator>
521
inline void handle_empty_z_m(TokenizerIterator& it,
34,778✔
522
                             TokenizerIterator const& end,
523
                             bool& has_empty,
524
                             bool& has_z,
525
                             bool& has_m)
526
{
527
    has_empty = false;
34,778✔
528
    has_z = false;
34,778✔
529
    has_m = false;
34,778✔
530

531
    // WKT can optionally have Z and M (measured) values as in
532
    // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
533
    // GGL supports any of them as coordinate values, but is not aware
534
    // of any Measured value.
535
    while (it != end
46✔
536
           && (one_of(it, "M", has_m)
104,430✔
537
               || one_of(it, "Z", has_z)
69,606✔
538
               || one_of(it, "EMPTY", has_empty)
69,602✔
539
               || one_of(it, "MZ", has_m, has_z)
69,564✔
540
               || one_of(it, "ZM", has_z, has_m)
69,564✔
541
               )
542
           )
543
    {
544
        ++it;
46✔
545
    }
546
}
34,778✔
547

548

549
template <typename Geometry, typename Tag = geometry::tag_t<Geometry>>
550
struct dimension
551
    : geometry::dimension<Geometry>
552
{};
553

554
// TODO: For now assume the dimension of the first type defined for GC
555
//       This should probably be unified for all algorithms
556
template <typename Geometry>
557
struct dimension<Geometry, geometry_collection_tag>
558
    : geometry::dimension
559
        <
560
            typename util::sequence_front
561
                <
562
                    typename traits::geometry_types<Geometry>::type
563
                >::type
564
        >
565
{};
566

567

568
/*!
569
\brief Internal, starts parsing
570
\param geometry_name string to compare with first token
571
*/
572
template <typename Geometry, typename TokenizerIterator>
573
inline bool initialize(TokenizerIterator& it,
34,626✔
574
                       TokenizerIterator const& end,
575
                       std::string const& wkt,
576
                       std::string const& geometry_name)
577
{
578
    if (it == end || ! boost::iequals(*it++, geometry_name))
34,626✔
579
    {
580
        BOOST_THROW_EXCEPTION(read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt));
24✔
581
    }
582

583
    bool has_empty, has_z, has_m;
584

585
    handle_empty_z_m(it, end, has_empty, has_z, has_m);
34,622✔
586

587
// Silence warning C4127: conditional expression is constant
588
#if defined(_MSC_VER)
589
#pragma warning(push)
590
#pragma warning(disable : 4127)
591
#endif
592

593
    if (has_z && dimension<Geometry>::value < 3)
34,207✔
594
    {
595
        BOOST_THROW_EXCEPTION(read_wkt_exception("Z only allowed for 3 or more dimensions", wkt));
16✔
596
    }
597

598
#if defined(_MSC_VER)
599
#pragma warning(pop)
600
#endif
601

602
    if (has_empty)
34,618✔
603
    {
604
        return false;
38✔
605
    }
606
    // M is ignored at all.
607

608
    return true;
34,580✔
609
}
610

611

612
template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
613
struct geometry_parser
614
{
615
    static inline void apply(std::string const& wkt, Geometry& geometry)
24,312✔
616
    {
617
        geometry::clear(geometry);
24,312✔
618

619
        auto const tokens{make_tokenizer(wkt)};
48,624✔
620
        auto it = tokens.begin();
48,624✔
621
        auto const end = tokens.end();
48,624✔
622

623
        apply(it, end, wkt, geometry);
24,312✔
624

625
        check_end(it, end, wkt);
24,276✔
626
    }
24,268✔
627

628
    template <typename TokenizerIterator>
629
    static inline void apply(TokenizerIterator& it,
24,716✔
630
                             TokenizerIterator const& end,
631
                             std::string const& wkt,
632
                             Geometry& geometry)
633
    {
634
        if (initialize<Geometry>(it, end, wkt, PrefixPolicy::apply()))
24,724✔
635
        {
636
            Parser<Geometry>::apply(it, end, wkt, geometry);
24,682✔
637
        }
638
    }
24,680✔
639
};
640

641

642
template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy>
643
struct multi_parser
644
{
645
    static inline void apply(std::string const& wkt, MultiGeometry& geometry)
7,281✔
646
    {
647
        traits::clear<MultiGeometry>::apply(geometry);
7,281✔
648

649
        auto const tokens{make_tokenizer(wkt)};
14,562✔
650
        auto it = tokens.begin();
14,562✔
651
        auto const end = tokens.end();
14,562✔
652

653
        apply(it, end, wkt, geometry);
7,281✔
654

655
        check_end(it, end, wkt);
7,277✔
656
    }
7,273✔
657

658
    template <typename TokenizerIterator>
659
    static inline void apply(TokenizerIterator& it,
7,392✔
660
                             TokenizerIterator const& end,
661
                             std::string const& wkt,
662
                             MultiGeometry& geometry)
663
    {
664
        if (initialize<MultiGeometry>(it, end, wkt, PrefixPolicy::apply()))
7,392✔
665
        {
666
            handle_open_parenthesis(it, end, wkt);
7,384✔
667

668
            // Parse sub-geometries
669
            while(it != end && *it != ")")
25,741✔
670
            {
671
                traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
18,361✔
672
                Parser
673
                    <
674
                        typename boost::range_value<MultiGeometry>::type
675
                    >::apply(it, end, wkt, *(boost::end(geometry) - 1));
18,361✔
676
                if (it != end && *it == ",")
18,357✔
677
                {
678
                    // Skip "," after multi-element is parsed
679
                    ++it;
11,140✔
680
                }
681
            }
682

683
            handle_close_parenthesis(it, end, wkt);
7,380✔
684
        }
685
    }
7,388✔
686
};
687

688
template <typename P>
689
struct noparenthesis_point_parser
690
{
691
    template <typename TokenizerIterator>
692
    static inline void apply(TokenizerIterator& it,
4,901✔
693
                             TokenizerIterator const& end,
694
                             std::string const& wkt,
695
                             P& point)
696
    {
697
        parsing_assigner<P>::apply(it, end, point, wkt);
4,901✔
698
    }
4,897✔
699
};
700

701
template <typename MultiGeometry, typename PrefixPolicy>
702
struct multi_point_parser
703
{
704
    static inline void apply(std::string const& wkt, MultiGeometry& geometry)
2,237✔
705
    {
706
        traits::clear<MultiGeometry>::apply(geometry);
2,237✔
707

708
        auto const tokens{make_tokenizer(wkt)};
4,474✔
709
        auto it = tokens.begin();
4,474✔
710
        auto const end = tokens.end();
4,474✔
711

712
        apply(it, end, wkt, geometry);
2,237✔
713

714
        check_end(it, end, wkt);
2,225✔
715
    }
2,221✔
716

717
    template <typename TokenizerIterator>
718
    static inline void apply(TokenizerIterator& it,
2,279✔
719
                             TokenizerIterator const& end,
720
                             std::string const& wkt,
721
                             MultiGeometry& geometry)
722
    {
723
        if (initialize<MultiGeometry>(it, end, wkt, PrefixPolicy::apply()))
2,279✔
724
        {
725
            handle_open_parenthesis(it, end, wkt);
2,275✔
726

727
            // If first point definition starts with "(" then parse points as (x y)
728
            // otherwise as "x y"
729
            bool using_brackets = (it != end && *it == "(");
2,275✔
730

731
            while(it != end && *it != ")")
8,435✔
732
            {
733
                traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
6,168✔
734

735
                if (using_brackets)
6,168✔
736
                {
737
                    point_parser
738
                        <
739
                            typename boost::range_value<MultiGeometry>::type
740
                        >::apply(it, end, wkt, *(boost::end(geometry) - 1));
1,267✔
741
                }
742
                else
743
                {
744
                    noparenthesis_point_parser
745
                        <
746
                            typename boost::range_value<MultiGeometry>::type
747
                        >::apply(it, end, wkt, *(boost::end(geometry) - 1));
4,901✔
748
                }
749

750
                if (it != end && *it == ",")
6,160✔
751
                {
752
                    // Skip "," after point is parsed
753
                    ++it;
4,192✔
754
                }
755
            }
756

757
            handle_close_parenthesis(it, end, wkt);
2,267✔
758
        }
759
    }
2,267✔
760
};
761

762

763
/*!
764
\brief Supports box parsing
765
\note OGC does not define the box geometry, and WKT does not support boxes.
766
    However, to be generic GGL supports reading and writing from and to boxes.
767
    Boxes are outputted as a standard POLYGON. GGL can read boxes from
768
    a standard POLYGON, from a POLYGON with 2 points of from a BOX
769
\tparam Box the box
770
*/
771
template <typename Box>
772
struct box_parser
773
{
774
    static inline void apply(std::string const& wkt, Box& box)
3,550✔
775
    {
776
        auto const tokens{make_tokenizer(wkt)};
7,100✔
777
        auto it = tokens.begin();
7,100✔
778
        auto const end = tokens.end();
7,100✔
779

780
        apply(it, end, wkt, box);
3,550✔
781

782
        check_end(it, end, wkt);
3,546✔
783
    }
3,542✔
784

785
    template <typename TokenizerIterator>
786
    static inline void apply(TokenizerIterator& it,
3,550✔
787
                             TokenizerIterator const& end,
788
                             std::string const& wkt,
789
                             Box& box)
790
    {
791
        bool should_close = false;
3,550✔
792
        if (it != end && boost::iequals(*it, "POLYGON"))
3,550✔
793
        {
794
            ++it;
156✔
795
            bool has_empty, has_z, has_m;
796
            handle_empty_z_m(it, end, has_empty, has_z, has_m);
156✔
797
            if (has_empty)
156✔
798
            {
799
                assign_zero(box);
×
800
                return;
×
801
            }
802
            handle_open_parenthesis(it, end, wkt);
156✔
803
            should_close = true;
156✔
804
        }
805
        else if (it != end && boost::iequals(*it, "BOX"))
3,394✔
806
        {
807
            ++it;
3,394✔
808
        }
809
        else
810
        {
811
            BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt));
×
812
        }
813

814
        using point_type = point_type_t<Box>;
815
        std::vector<point_type> points;
7,100✔
816
        container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
3,550✔
817

818
        if (should_close)
3,550✔
819
        {
820
            handle_close_parenthesis(it, end, wkt);
156✔
821
        }
822

823
        unsigned int index = 0;
3,550✔
824
        std::size_t n = boost::size(points);
3,550✔
825
        if (n == 2)
3,550✔
826
        {
827
            index = 1;
3,511✔
828
        }
829
        else if (n == 4 || n == 5)
39✔
830
        {
831
            // In case of 4 or 5 points, we do not check the other ones, just
832
            // take the opposite corner which is always 2
833
            index = 2;
35✔
834
        }
835
        else
836
        {
837
            BOOST_THROW_EXCEPTION(read_wkt_exception("Box should have 2,4 or 5 points", wkt));
16✔
838
        }
839

840
        geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
3,546✔
841
        geometry::detail::assign_point_to_index<max_corner>(points[index], box);
3,546✔
842
    }
843
};
844

845

846
/*!
847
\brief Supports segment parsing
848
\note OGC does not define the segment, and WKT does not support segmentes.
849
    However, it is useful to implement it, also for testing purposes
850
\tparam Segment the segment
851
*/
852
template <typename Segment>
853
struct segment_parser
854
{
855
    static inline void apply(std::string const& wkt, Segment& segment)
4,622✔
856
    {
857
        auto const tokens{make_tokenizer(wkt)};
9,244✔
858
        auto it = tokens.begin();
9,244✔
859
        auto const end = tokens.end();
9,244✔
860

861
        apply(it, end, wkt, segment);
4,622✔
862

863
        check_end(it, end, wkt);
4,622✔
864
    }
4,622✔
865

866
    template <typename TokenizerIterator>
867
    static inline void apply(TokenizerIterator& it,
4,622✔
868
                             TokenizerIterator const& end,
869
                             std::string const& wkt,
870
                             Segment& segment)
871
    {
872
        if (it != end
13,866✔
873
            && (boost::iequals(*it, prefix_segment::apply())
9,388✔
874
                || boost::iequals(*it, prefix_linestring::apply())))
4,766✔
875
        {
876
            ++it;
4,622✔
877
        }
878
        else
879
        {
880
            BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt));
×
881
        }
882

883
        using point_type = point_type_t<Segment>;
884
        std::vector<point_type> points;
9,244✔
885
        container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
4,622✔
886

887
        if (boost::size(points) == 2)
4,622✔
888
        {
889
            geometry::detail::assign_point_to_index<0>(points.front(), segment);
4,622✔
890
            geometry::detail::assign_point_to_index<1>(points.back(), segment);
4,622✔
891
        }
892
        else
893
        {
894
            BOOST_THROW_EXCEPTION(read_wkt_exception("Segment should have 2 points", wkt));
×
895
        }
896
    }
4,622✔
897
};
898

899

900
struct dynamic_move_assign
901
{
902
    template <typename DynamicGeometry, typename Geometry>
903
    static void apply(DynamicGeometry& dynamic_geometry, Geometry & geometry)
57✔
904
    {
905
        dynamic_geometry = std::move(geometry);
57✔
906
    }
57✔
907
};
908

909
struct dynamic_move_emplace_back
910
{
911
    template <typename GeometryCollection, typename Geometry>
912
    static void apply(GeometryCollection& geometry_collection, Geometry & geometry)
500✔
913
    {
914
        traits::emplace_back<GeometryCollection>::apply(geometry_collection, std::move(geometry));
500✔
915
    }
500✔
916
};
917

918
template
919
<
920
    typename Geometry,
921
    template <typename, typename> class ReadWkt,
922
    typename AppendPolicy
923
>
924
struct dynamic_readwkt_caller
925
{
926
    template <typename TokenizerIterator>
927
    static inline void apply(TokenizerIterator& it,
557✔
928
                             TokenizerIterator const& end,
929
                             std::string const& wkt,
930
                             Geometry& geometry)
931
    {
932
        static const char* tag_point = prefix_point::apply();
557✔
933
        static const char* tag_linestring = prefix_linestring::apply();
557✔
934
        static const char* tag_polygon = prefix_polygon::apply();
557✔
935

936
        static const char* tag_multi_point = prefix_multipoint::apply();
557✔
937
        static const char* tag_multi_linestring = prefix_multilinestring::apply();
557✔
938
        static const char* tag_multi_polygon = prefix_multipolygon::apply();
557✔
939

940
        static const char* tag_segment = prefix_segment::apply();
557✔
941
        static const char* tag_box = prefix_box::apply();
557✔
942
        static const char* tag_gc = prefix_geometrycollection::apply();
557✔
943

944
        if (boost::iequals(*it, tag_point))
557✔
945
        {
946
            parse_geometry<util::is_point>(tag_point, it, end, wkt, geometry);
75✔
947
        }
948
        else if (boost::iequals(*it, tag_multi_point))
482✔
949
        {
950
            parse_geometry<util::is_multi_point>(tag_multi_point, it, end, wkt, geometry);
42✔
951
        }
952
        else if (boost::iequals(*it, tag_segment))
440✔
953
        {
954
            parse_geometry<util::is_segment>(tag_segment, it, end, wkt, geometry);
×
955
        }
956
        else if (boost::iequals(*it, tag_linestring))
440✔
957
        {
958
            parse_geometry<util::is_linestring>(tag_linestring, it, end, wkt, geometry, false)
87✔
959
            || parse_geometry<util::is_segment>(tag_linestring, it, end, wkt, geometry);
87✔
960
        }
961
        else if (boost::iequals(*it, tag_multi_linestring))
353✔
962
        {
963
            parse_geometry<util::is_multi_linestring>(tag_multi_linestring, it, end, wkt, geometry);
36✔
964
        }
965
        else if (boost::iequals(*it, tag_box))
317✔
966
        {
967
            parse_geometry<util::is_box>(tag_box, it, end, wkt, geometry);
×
968
        }
969
        else if (boost::iequals(*it, tag_polygon))
317✔
970
        {
971
            parse_geometry<util::is_polygon>(tag_polygon, it, end, wkt, geometry, false)
242✔
972
            || parse_geometry<util::is_ring>(tag_polygon, it, end, wkt, geometry, false)
8✔
973
            || parse_geometry<util::is_box>(tag_polygon, it, end, wkt, geometry);
250✔
974
        }
975
        else if (boost::iequals(*it, tag_multi_polygon))
75✔
976
        {
977
            parse_geometry<util::is_multi_polygon>(tag_multi_polygon, it, end, wkt, geometry);
75✔
978
        }
979
        else if (boost::iequals(*it, tag_gc))
×
980
        {
981
            parse_geometry<util::is_geometry_collection>(tag_gc, it, end, wkt, geometry);
×
982
        }
983
        else
984
        {
985
            BOOST_THROW_EXCEPTION(read_wkt_exception(
×
986
                "Should start with geometry's type, for example 'POINT', 'LINESTRING', 'POLYGON'",
987
                wkt));
988
        }
989
    }
557✔
990

991
private:
992
    template
993
    <
994
        template <typename> class UnaryPred,
995
        typename TokenizerIterator,
996
        typename Geom = typename util::sequence_find_if
997
            <
998
                typename traits::geometry_types<Geometry>::type, UnaryPred
999
            >::type,
1000
        std::enable_if_t<! std::is_void<Geom>::value, int> = 0
1001
    >
1002
    static bool parse_geometry(const char * ,
557✔
1003
                               TokenizerIterator& it,
1004
                               TokenizerIterator const& end,
1005
                               std::string const& wkt,
1006
                               Geometry& geometry,
1007
                               bool = true)
1008
    {
1009
        Geom g;
482✔
1010
        ReadWkt<Geom, tag_t<Geom>>::apply(it, end, wkt, g);
557✔
1011
        AppendPolicy::apply(geometry, g);
557✔
1012
        return true;
1,039✔
1013
    }
1014

1015
    template
1016
    <
1017
        template <typename> class UnaryPred,
1018
        typename TokenizerIterator,
1019
        typename Geom = typename util::sequence_find_if
1020
            <
1021
                typename traits::geometry_types<Geometry>::type, UnaryPred
1022
            >::type,
1023
        std::enable_if_t<std::is_void<Geom>::value, int> = 0
1024
    >
1025
    static bool parse_geometry(const char * name,
8✔
1026
                               TokenizerIterator& ,
1027
                               TokenizerIterator const& ,
1028
                               std::string const& wkt,
1029
                               Geometry& ,
1030
                               bool throw_on_misfit = true)
1031
    {
1032
        if (throw_on_misfit)
8✔
1033
        {
1034
            std::string msg = std::string("Unable to store '") + name + "' in this geometry";
×
1035
            BOOST_THROW_EXCEPTION(read_wkt_exception(msg, wkt));
×
1036
        }
1037

1038
        return false;
8✔
1039
    }
1040
};
1041

1042

1043
}} // namespace detail::wkt
1044
#endif // DOXYGEN_NO_DETAIL
1045

1046
#ifndef DOXYGEN_NO_DISPATCH
1047
namespace dispatch
1048
{
1049

1050
template <typename Geometry, typename Tag = tag_t<Geometry>>
1051
struct read_wkt {};
1052

1053

1054
template <typename Point>
1055
struct read_wkt<Point, point_tag>
1056
    : detail::wkt::geometry_parser
1057
        <
1058
            Point,
1059
            detail::wkt::point_parser,
1060
            detail::wkt::prefix_point
1061
        >
1062
{};
1063

1064

1065
template <typename L>
1066
struct read_wkt<L, linestring_tag>
1067
    : detail::wkt::geometry_parser
1068
        <
1069
            L,
1070
            detail::wkt::linestring_parser,
1071
            detail::wkt::prefix_linestring
1072
        >
1073
{};
1074

1075
template <typename Ring>
1076
struct read_wkt<Ring, ring_tag>
1077
    : detail::wkt::geometry_parser
1078
        <
1079
            Ring,
1080
            detail::wkt::ring_parser,
1081
            detail::wkt::prefix_polygon
1082
        >
1083
{};
1084

1085
template <typename Geometry>
1086
struct read_wkt<Geometry, polygon_tag>
1087
    : detail::wkt::geometry_parser
1088
        <
1089
            Geometry,
1090
            detail::wkt::polygon_parser,
1091
            detail::wkt::prefix_polygon
1092
        >
1093
{};
1094

1095
template <typename Geometry>
1096
struct read_wkt<Geometry, polyhedral_surface_tag>
1097
    : detail::wkt::geometry_parser
1098
        <
1099
            Geometry,
1100
            detail::wkt::polyhedral_surface_parser,
1101
            detail::wkt::prefix_polyhedral_surface
1102
        >
1103
{};
1104

1105
template <typename MultiGeometry>
1106
struct read_wkt<MultiGeometry, multi_point_tag>
1107
    : detail::wkt::multi_point_parser
1108
            <
1109
                MultiGeometry,
1110
                detail::wkt::prefix_multipoint
1111
            >
1112
{};
1113

1114
template <typename MultiGeometry>
1115
struct read_wkt<MultiGeometry, multi_linestring_tag>
1116
    : detail::wkt::multi_parser
1117
            <
1118
                MultiGeometry,
1119
                detail::wkt::linestring_parser,
1120
                detail::wkt::prefix_multilinestring
1121
            >
1122
{};
1123

1124
template <typename MultiGeometry>
1125
struct read_wkt<MultiGeometry, multi_polygon_tag>
1126
    : detail::wkt::multi_parser
1127
            <
1128
                MultiGeometry,
1129
                detail::wkt::polygon_parser,
1130
                detail::wkt::prefix_multipolygon
1131
            >
1132
{};
1133

1134

1135
// Box (Non-OGC)
1136
template <typename Box>
1137
struct read_wkt<Box, box_tag>
1138
    : detail::wkt::box_parser<Box>
1139
{};
1140

1141
// Segment (Non-OGC)
1142
template <typename Segment>
1143
struct read_wkt<Segment, segment_tag>
1144
    : detail::wkt::segment_parser<Segment>
1145
{};
1146

1147

1148
template <typename DynamicGeometry>
1149
struct read_wkt<DynamicGeometry, dynamic_geometry_tag>
1150
{
1151
    static inline void apply(std::string const& wkt, DynamicGeometry& dynamic_geometry)
57✔
1152
    {
1153
        auto tokens{detail::wkt::make_tokenizer(wkt)};
114✔
1154
        auto it = tokens.begin();
114✔
1155
        auto const end = tokens.end();
114✔
1156
        if (it == end)
57✔
1157
        {
1158
            BOOST_THROW_EXCEPTION(read_wkt_exception(
×
1159
                "Should start with geometry's type, for example 'POINT', 'LINESTRING', 'POLYGON'",
1160
                wkt));
1161
        }
1162

1163
        detail::wkt::dynamic_readwkt_caller
1164
            <
1165
                DynamicGeometry, dispatch::read_wkt, detail::wkt::dynamic_move_assign
1166
            >::apply(it, end, wkt, dynamic_geometry);
57✔
1167

1168
        detail::wkt::check_end(it, end, wkt);
57✔
1169
    }
57✔
1170
};
1171

1172

1173
template <typename Geometry>
1174
struct read_wkt<Geometry, geometry_collection_tag>
1175
{
1176
    static inline void apply(std::string const& wkt, Geometry& geometry)
239✔
1177
    {
1178
        range::clear(geometry);
239✔
1179

1180
        auto tokens{detail::wkt::make_tokenizer(wkt)};
478✔
1181
        auto it = tokens.begin();
478✔
1182
        auto const end = tokens.end();
478✔
1183

1184
        apply(it, end, wkt, geometry);
239✔
1185

1186
        detail::wkt::check_end(it, end, wkt);
239✔
1187
    }
239✔
1188

1189
    template <typename TokenizerIterator>
1190
    static inline void apply(TokenizerIterator& it,
239✔
1191
                             TokenizerIterator const& end,
1192
                             std::string const& wkt,
1193
                             Geometry& geometry)
1194
    {
1195
        if (detail::wkt::initialize<Geometry>(it, end, wkt,
239✔
1196
                detail::wkt::prefix_geometrycollection::apply()))
1197
        {
1198
            detail::wkt::handle_open_parenthesis(it, end, wkt);
239✔
1199

1200
            // Stop at ")"
1201
            while (it != end && *it != ")")
739✔
1202
            {
1203
                detail::wkt::dynamic_readwkt_caller
1204
                    <
1205
                        Geometry, dispatch::read_wkt, detail::wkt::dynamic_move_emplace_back
1206
                    >::apply(it, end, wkt, geometry);
500✔
1207

1208
                if (it != end && *it == ",")
500✔
1209
                {
1210
                    // Skip "," after geometry is parsed
1211
                    ++it;
260✔
1212
                }
1213
            }
1214

1215
            detail::wkt::handle_close_parenthesis(it, end, wkt);
239✔
1216
        }
1217
    }
239✔
1218
};
1219

1220

1221
} // namespace dispatch
1222
#endif // DOXYGEN_NO_DISPATCH
1223

1224
/*!
1225
\brief Parses OGC \well_known_text (\wkt) into a geometry (any geometry)
1226
\ingroup wkt
1227
\tparam Geometry \tparam_geometry
1228
\param wkt string containing \wkt
1229
\param geometry \param_geometry output geometry
1230
\ingroup wkt
1231
\qbk{[include reference/io/read_wkt.qbk]}
1232
*/
1233
template <typename Geometry>
1234
inline void read_wkt(std::string const& wkt, Geometry& geometry)
42,157✔
1235
{
1236
    geometry::concepts::check<Geometry>();
42,157✔
1237
    dispatch::read_wkt<Geometry>::apply(wkt, geometry);
42,157✔
1238
}
42,119✔
1239

1240
/*!
1241
\brief Parses OGC \well_known_text (\wkt) into a geometry (any geometry) and returns it
1242
\ingroup wkt
1243
\tparam Geometry \tparam_geometry
1244
\param wkt string containing \wkt
1245
\ingroup wkt
1246
\qbk{[include reference/io/from_wkt.qbk]}
1247
*/
1248
template <typename Geometry>
1249
inline Geometry from_wkt(std::string const& wkt)
141✔
1250
{
1251
    Geometry geometry;
100✔
1252
    geometry::concepts::check<Geometry>();
141✔
1253
    dispatch::read_wkt<Geometry>::apply(wkt, geometry);
141✔
1254
    return geometry;
103✔
1255
}
1256

1257
}} // namespace boost::geometry
1258

1259
#endif // BOOST_GEOMETRY_IO_WKT_READ_HPP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc