comparison DEPENDENCIES/generic/include/boost/geometry/io/wkt/read.hpp @ 16:2665513ce2d3

Add boost headers
author Chris Cannam
date Tue, 05 Aug 2014 11:11:38 +0100
parents
children c530137014c0
comparison
equal deleted inserted replaced
15:663ca0da4350 16:2665513ce2d3
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
5 // Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
6
7 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
8 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
9
10 // Use, modification and distribution is subject to the Boost Software License,
11 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
12 // http://www.boost.org/LICENSE_1_0.txt)
13
14 #ifndef BOOST_GEOMETRY_IO_WKT_READ_HPP
15 #define BOOST_GEOMETRY_IO_WKT_READ_HPP
16
17 #include <string>
18
19 #include <boost/lexical_cast.hpp>
20 #include <boost/tokenizer.hpp>
21
22 #include <boost/algorithm/string.hpp>
23 #include <boost/mpl/if.hpp>
24 #include <boost/range.hpp>
25
26 #include <boost/type_traits.hpp>
27
28 #include <boost/geometry/algorithms/assign.hpp>
29 #include <boost/geometry/algorithms/append.hpp>
30 #include <boost/geometry/algorithms/clear.hpp>
31
32 #include <boost/geometry/core/access.hpp>
33 #include <boost/geometry/core/coordinate_dimension.hpp>
34 #include <boost/geometry/core/exception.hpp>
35 #include <boost/geometry/core/exterior_ring.hpp>
36 #include <boost/geometry/core/geometry_id.hpp>
37 #include <boost/geometry/core/interior_rings.hpp>
38 #include <boost/geometry/core/mutable_range.hpp>
39
40 #include <boost/geometry/geometries/concepts/check.hpp>
41
42 #include <boost/geometry/util/coordinate_cast.hpp>
43
44 #include <boost/geometry/io/wkt/detail/prefix.hpp>
45
46 namespace boost { namespace geometry
47 {
48
49 /*!
50 \brief Exception showing things wrong with WKT parsing
51 \ingroup wkt
52 */
53 struct read_wkt_exception : public geometry::exception
54 {
55 template <typename Iterator>
56 read_wkt_exception(std::string const& msg,
57 Iterator const& it, Iterator const& end, std::string const& wkt)
58 : message(msg)
59 , wkt(wkt)
60 {
61 if (it != end)
62 {
63 source = " at '";
64 source += it->c_str();
65 source += "'";
66 }
67 complete = message + source + " in '" + wkt.substr(0, 100) + "'";
68 }
69
70 read_wkt_exception(std::string const& msg, std::string const& wkt)
71 : message(msg)
72 , wkt(wkt)
73 {
74 complete = message + "' in (" + wkt.substr(0, 100) + ")";
75 }
76
77 virtual ~read_wkt_exception() throw() {}
78
79 virtual const char* what() const throw()
80 {
81 return complete.c_str();
82 }
83 private :
84 std::string source;
85 std::string message;
86 std::string wkt;
87 std::string complete;
88 };
89
90
91 #ifndef DOXYGEN_NO_DETAIL
92 // (wkt: Well Known Text, defined by OGC for all geometries and implemented by e.g. databases (MySQL, PostGIS))
93 namespace detail { namespace wkt
94 {
95
96 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
97
98 template <typename Point, std::size_t Dimension, std::size_t DimensionCount>
99 struct parsing_assigner
100 {
101 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
102 Point& point, std::string const& wkt)
103 {
104 typedef typename coordinate_type<Point>::type coordinate_type;
105
106 // Stop at end of tokens, or at "," ot ")"
107 bool finished = (it == end || *it == "," || *it == ")");
108
109 try
110 {
111 // Initialize missing coordinates to default constructor (zero)
112 // OR
113 // Use lexical_cast for conversion to double/int
114 // Note that it is much slower than atof. However, it is more standard
115 // and in parsing the change in performance falls probably away against
116 // the tokenizing
117 set<Dimension>(point, finished
118 ? coordinate_type()
119 : coordinate_cast<coordinate_type>::apply(*it));
120 }
121 catch(boost::bad_lexical_cast const& blc)
122 {
123 throw read_wkt_exception(blc.what(), it, end, wkt);
124 }
125 catch(std::exception const& e)
126 {
127 throw read_wkt_exception(e.what(), it, end, wkt);
128 }
129 catch(...)
130 {
131 throw read_wkt_exception("", it, end, wkt);
132 }
133
134 parsing_assigner<Point, Dimension + 1, DimensionCount>::apply(
135 (finished ? it : ++it), end, point, wkt);
136 }
137 };
138
139 template <typename Point, std::size_t DimensionCount>
140 struct parsing_assigner<Point, DimensionCount, DimensionCount>
141 {
142 static inline void apply(tokenizer::iterator&, tokenizer::iterator, Point&,
143 std::string const&)
144 {
145 }
146 };
147
148
149
150 template <typename Iterator>
151 inline void handle_open_parenthesis(Iterator& it,
152 Iterator const& end, std::string const& wkt)
153 {
154 if (it == end || *it != "(")
155 {
156 throw read_wkt_exception("Expected '('", it, end, wkt);
157 }
158 ++it;
159 }
160
161
162 template <typename Iterator>
163 inline void handle_close_parenthesis(Iterator& it,
164 Iterator const& end, std::string const& wkt)
165 {
166 if (it != end && *it == ")")
167 {
168 ++it;
169 }
170 else
171 {
172 throw read_wkt_exception("Expected ')'", it, end, wkt);
173 }
174 }
175
176 template <typename Iterator>
177 inline void check_end(Iterator& it,
178 Iterator const& end, std::string const& wkt)
179 {
180 if (it != end)
181 {
182 throw read_wkt_exception("Too much tokens", it, end, wkt);
183 }
184 }
185
186 /*!
187 \brief Internal, parses coordinate sequences, strings are formated like "(1 2,3 4,...)"
188 \param it token-iterator, should be pre-positioned at "(", is post-positions after last ")"
189 \param end end-token-iterator
190 \param out Output itererator receiving coordinates
191 */
192 template <typename Point>
193 struct container_inserter
194 {
195 // Version with output iterator
196 template <typename OutputIterator>
197 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
198 std::string const& wkt, OutputIterator out)
199 {
200 handle_open_parenthesis(it, end, wkt);
201
202 Point point;
203
204 // Parse points until closing parenthesis
205
206 while (it != end && *it != ")")
207 {
208 parsing_assigner
209 <
210 Point,
211 0,
212 dimension<Point>::value
213 >::apply(it, end, point, wkt);
214 out = point;
215 ++out;
216 if (it != end && *it == ",")
217 {
218 ++it;
219 }
220 }
221
222 handle_close_parenthesis(it, end, wkt);
223 }
224 };
225
226
227 // Geometry is a value-type or reference-type
228 template <typename Geometry>
229 struct container_appender
230 {
231 typedef typename geometry::point_type
232 <
233 typename boost::remove_reference<Geometry>::type
234 >::type point_type;
235
236 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
237 std::string const& wkt, Geometry out)
238 {
239 handle_open_parenthesis(it, end, wkt);
240
241 point_type point;
242
243 // Parse points until closing parenthesis
244
245 while (it != end && *it != ")")
246 {
247 parsing_assigner
248 <
249 point_type,
250 0,
251 dimension<point_type>::value
252 >::apply(it, end, point, wkt);
253
254 geometry::append(out, point);
255 if (it != end && *it == ",")
256 {
257 ++it;
258 }
259 }
260
261 handle_close_parenthesis(it, end, wkt);
262 }
263 };
264
265 /*!
266 \brief Internal, parses a point from a string like this "(x y)"
267 \note used for parsing points and multi-points
268 */
269 template <typename P>
270 struct point_parser
271 {
272 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
273 std::string const& wkt, P& point)
274 {
275 handle_open_parenthesis(it, end, wkt);
276 parsing_assigner<P, 0, dimension<P>::value>::apply(it, end, point, wkt);
277 handle_close_parenthesis(it, end, wkt);
278 }
279 };
280
281
282 template <typename Geometry>
283 struct linestring_parser
284 {
285 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
286 std::string const& wkt, Geometry& geometry)
287 {
288 container_appender<Geometry&>::apply(it, end, wkt, geometry);
289 }
290 };
291
292
293 template <typename Ring>
294 struct ring_parser
295 {
296 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
297 std::string const& wkt, Ring& ring)
298 {
299 // A ring should look like polygon((x y,x y,x y...))
300 // So handle the extra opening/closing parentheses
301 // and in between parse using the container-inserter
302 handle_open_parenthesis(it, end, wkt);
303 container_appender<Ring&>::apply(it, end, wkt, ring);
304 handle_close_parenthesis(it, end, wkt);
305 }
306 };
307
308
309
310
311 /*!
312 \brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
313 \note used for parsing polygons and multi-polygons
314 */
315 template <typename Polygon>
316 struct polygon_parser
317 {
318 typedef typename ring_return_type<Polygon>::type ring_return_type;
319 typedef container_appender<ring_return_type> appender;
320
321 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
322 std::string const& wkt, Polygon& poly)
323 {
324
325 handle_open_parenthesis(it, end, wkt);
326
327 int n = -1;
328
329 // Stop at ")"
330 while (it != end && *it != ")")
331 {
332 // Parse ring
333 if (++n == 0)
334 {
335 appender::apply(it, end, wkt, exterior_ring(poly));
336 }
337 else
338 {
339 typename ring_type<Polygon>::type ring;
340 appender::apply(it, end, wkt, ring);
341 traits::push_back
342 <
343 typename boost::remove_reference
344 <
345 typename traits::interior_mutable_type<Polygon>::type
346 >::type
347 >::apply(interior_rings(poly), ring);
348 }
349
350 if (it != end && *it == ",")
351 {
352 // Skip "," after ring is parsed
353 ++it;
354 }
355 }
356
357 handle_close_parenthesis(it, end, wkt);
358 }
359 };
360
361 inline bool one_of(tokenizer::iterator const& it, std::string const& value,
362 bool& is_present)
363 {
364 if (boost::iequals(*it, value))
365 {
366 is_present = true;
367 return true;
368 }
369 return false;
370 }
371
372 inline bool one_of(tokenizer::iterator const& it, std::string const& value,
373 bool& present1, bool& present2)
374 {
375 if (boost::iequals(*it, value))
376 {
377 present1 = true;
378 present2 = true;
379 return true;
380 }
381 return false;
382 }
383
384
385 inline void handle_empty_z_m(tokenizer::iterator& it, tokenizer::iterator end,
386 bool& has_empty, bool& has_z, bool& has_m)
387 {
388 has_empty = false;
389 has_z = false;
390 has_m = false;
391
392 // WKT can optionally have Z and M (measured) values as in
393 // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
394 // GGL supports any of them as coordinate values, but is not aware
395 // of any Measured value.
396 while (it != end
397 && (one_of(it, "M", has_m)
398 || one_of(it, "Z", has_z)
399 || one_of(it, "EMPTY", has_empty)
400 || one_of(it, "MZ", has_m, has_z)
401 || one_of(it, "ZM", has_z, has_m)
402 )
403 )
404 {
405 ++it;
406 }
407 }
408
409 /*!
410 \brief Internal, starts parsing
411 \param tokens boost tokens, parsed with separator " " and keeping separator "()"
412 \param geometry string to compare with first token
413 */
414 template <typename Geometry>
415 inline bool initialize(tokenizer const& tokens,
416 std::string const& geometry_name, std::string const& wkt,
417 tokenizer::iterator& it)
418 {
419 it = tokens.begin();
420 if (it != tokens.end() && boost::iequals(*it++, geometry_name))
421 {
422 bool has_empty, has_z, has_m;
423
424 handle_empty_z_m(it, tokens.end(), has_empty, has_z, has_m);
425
426 // Silence warning C4127: conditional expression is constant
427 #if defined(_MSC_VER)
428 #pragma warning(push)
429 #pragma warning(disable : 4127)
430 #endif
431
432 if (has_z && dimension<Geometry>::type::value < 3)
433 {
434 throw read_wkt_exception("Z only allowed for 3 or more dimensions", wkt);
435 }
436
437 #if defined(_MSC_VER)
438 #pragma warning(pop)
439 #endif
440
441 if (has_empty)
442 {
443 check_end(it, tokens.end(), wkt);
444 return false;
445 }
446 // M is ignored at all.
447
448 return true;
449 }
450 throw read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt);
451 }
452
453
454 template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
455 struct geometry_parser
456 {
457 static inline void apply(std::string const& wkt, Geometry& geometry)
458 {
459 geometry::clear(geometry);
460
461 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
462 tokenizer::iterator it;
463 if (initialize<Geometry>(tokens, PrefixPolicy::apply(), wkt, it))
464 {
465 Parser<Geometry>::apply(it, tokens.end(), wkt, geometry);
466 check_end(it, tokens.end(), wkt);
467 }
468 }
469 };
470
471
472
473
474
475 /*!
476 \brief Supports box parsing
477 \note OGC does not define the box geometry, and WKT does not support boxes.
478 However, to be generic GGL supports reading and writing from and to boxes.
479 Boxes are outputted as a standard POLYGON. GGL can read boxes from
480 a standard POLYGON, from a POLYGON with 2 points of from a BOX
481 \tparam Box the box
482 */
483 template <typename Box>
484 struct box_parser
485 {
486 static inline void apply(std::string const& wkt, Box& box)
487 {
488 bool should_close = false;
489 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
490 tokenizer::iterator it = tokens.begin();
491 tokenizer::iterator end = tokens.end();
492 if (it != end && boost::iequals(*it, "POLYGON"))
493 {
494 ++it;
495 bool has_empty, has_z, has_m;
496 handle_empty_z_m(it, end, has_empty, has_z, has_m);
497 if (has_empty)
498 {
499 assign_zero(box);
500 return;
501 }
502 handle_open_parenthesis(it, end, wkt);
503 should_close = true;
504 }
505 else if (it != end && boost::iequals(*it, "BOX"))
506 {
507 ++it;
508 }
509 else
510 {
511 throw read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt);
512 }
513
514 typedef typename point_type<Box>::type point_type;
515 std::vector<point_type> points;
516 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
517
518 if (should_close)
519 {
520 handle_close_parenthesis(it, end, wkt);
521 }
522 check_end(it, end, wkt);
523
524 int index = 0;
525 int n = boost::size(points);
526 if (n == 2)
527 {
528 index = 1;
529 }
530 else if (n == 4 || n == 5)
531 {
532 // In case of 4 or 5 points, we do not check the other ones, just
533 // take the opposite corner which is always 2
534 index = 2;
535 }
536 else
537 {
538 throw read_wkt_exception("Box should have 2,4 or 5 points", wkt);
539 }
540
541 geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
542 geometry::detail::assign_point_to_index<max_corner>(points[index], box);
543 }
544 };
545
546
547 /*!
548 \brief Supports segment parsing
549 \note OGC does not define the segment, and WKT does not support segmentes.
550 However, it is useful to implement it, also for testing purposes
551 \tparam Segment the segment
552 */
553 template <typename Segment>
554 struct segment_parser
555 {
556 static inline void apply(std::string const& wkt, Segment& segment)
557 {
558 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
559 tokenizer::iterator it = tokens.begin();
560 tokenizer::iterator end = tokens.end();
561 if (it != end &&
562 (boost::iequals(*it, "SEGMENT")
563 || boost::iequals(*it, "LINESTRING") ))
564 {
565 ++it;
566 }
567 else
568 {
569 throw read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt);
570 }
571
572 typedef typename point_type<Segment>::type point_type;
573 std::vector<point_type> points;
574 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
575
576 check_end(it, end, wkt);
577
578 if (boost::size(points) == 2)
579 {
580 geometry::detail::assign_point_to_index<0>(points.front(), segment);
581 geometry::detail::assign_point_to_index<1>(points.back(), segment);
582 }
583 else
584 {
585 throw read_wkt_exception("Segment should have 2 points", wkt);
586 }
587
588 }
589 };
590
591
592
593 }} // namespace detail::wkt
594 #endif // DOXYGEN_NO_DETAIL
595
596 #ifndef DOXYGEN_NO_DISPATCH
597 namespace dispatch
598 {
599
600 template <typename Tag, typename Geometry>
601 struct read_wkt {};
602
603
604 template <typename Point>
605 struct read_wkt<point_tag, Point>
606 : detail::wkt::geometry_parser
607 <
608 Point,
609 detail::wkt::point_parser,
610 detail::wkt::prefix_point
611 >
612 {};
613
614
615 template <typename L>
616 struct read_wkt<linestring_tag, L>
617 : detail::wkt::geometry_parser
618 <
619 L,
620 detail::wkt::linestring_parser,
621 detail::wkt::prefix_linestring
622 >
623 {};
624
625 template <typename Ring>
626 struct read_wkt<ring_tag, Ring>
627 : detail::wkt::geometry_parser
628 <
629 Ring,
630 detail::wkt::ring_parser,
631 detail::wkt::prefix_polygon
632 >
633 {};
634
635 template <typename Geometry>
636 struct read_wkt<polygon_tag, Geometry>
637 : detail::wkt::geometry_parser
638 <
639 Geometry,
640 detail::wkt::polygon_parser,
641 detail::wkt::prefix_polygon
642 >
643 {};
644
645
646 // Box (Non-OGC)
647 template <typename Box>
648 struct read_wkt<box_tag, Box>
649 : detail::wkt::box_parser<Box>
650 {};
651
652 // Segment (Non-OGC)
653 template <typename Segment>
654 struct read_wkt<segment_tag, Segment>
655 : detail::wkt::segment_parser<Segment>
656 {};
657
658
659 } // namespace dispatch
660 #endif // DOXYGEN_NO_DISPATCH
661
662 /*!
663 \brief Parses OGC Well-Known Text (\ref WKT) into a geometry (any geometry)
664 \ingroup wkt
665 \param wkt string containing \ref WKT
666 \param geometry output geometry
667 \par Example:
668 \note It is case insensitive and can have the WKT forms "point", "point m", "point z", "point zm", "point mz"
669 \note Empty sequences can have forms as "LINESTRING ()" or "POLYGON(())"
670 Small example showing how to use read_wkt to build a point
671 \dontinclude doxygen_1.cpp
672 \skip example_from_wkt_point
673 \line {
674 \until }
675 \par Example:
676 Small example showing how to use read_wkt to build a linestring
677 \dontinclude doxygen_1.cpp
678 \skip example_from_wkt_linestring
679 \line {
680 \until }
681 \par Example:
682 Small example showing how to use read_wkt to build a polygon
683 \dontinclude doxygen_1.cpp
684 \skip example_from_wkt_polygon
685 \line {
686 \until }
687 */
688 template <typename Geometry>
689 inline void read_wkt(std::string const& wkt, Geometry& geometry)
690 {
691 geometry::concept::check<Geometry>();
692 dispatch::read_wkt<typename tag<Geometry>::type, Geometry>::apply(wkt, geometry);
693 }
694
695 }} // namespace boost::geometry
696
697 #endif // BOOST_GEOMETRY_IO_WKT_READ_HPP