diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 7a35a52162..2f6d206ef8 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -1,7 +1,7 @@ [/============================================================================ Boost.Geometry (aka GGL, Generic Geometry Library) - Copyright (c) 2009-2022 Barend Gehrels, Geodan, Amsterdam, the Netherlands. + Copyright (c) 2009-2023 Barend Gehrels, Geodan, Amsterdam, the Netherlands. Copyright (c) 2009-2017 Bruno Lalande, Paris, France. Copyright (c) 2009-2017 Mateusz Loskot , London, UK. Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. @@ -19,6 +19,22 @@ [section:release_notes Release Notes] +[/=================] +[heading Boost 1.82] +[/=================] + +[*Major improvements] + +* [@https://github.com/boostorg/geometry/pull/1045 1045] Support geographic buffer for (multi)linestrings and (multi)polygons + +[*Solved issues] + +* [@https://github.com/boostorg/geometry/issues/705 705] WKT: allow tabs and new lines + +[*Breaking changes] + +* The WKT output presentation of an empty polygon is now POLYGON() to make it consistent with other geometries + [/=================] [heading Boost 1.81] [/=================] diff --git a/include/boost/geometry/io/wkt/detail/prefix.hpp b/include/boost/geometry/io/wkt/detail/prefix.hpp index b566e02aa6..a61b8ae0f6 100644 --- a/include/boost/geometry/io/wkt/detail/prefix.hpp +++ b/include/boost/geometry/io/wkt/detail/prefix.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. @@ -57,6 +57,20 @@ struct prefix_multipolygon static inline const char* apply() { return "MULTIPOLYGON"; } }; +struct prefix_segment +{ + static inline const char* apply() { return "SEGMENT"; } +}; +struct prefix_box +{ + static inline const char* apply() { return "BOX"; } +}; +struct prefix_geometrycollection +{ + static inline const char* apply() { return "GEOMETRYCOLLECTION"; } +}; + + }} // namespace wkt::impl #endif diff --git a/include/boost/geometry/io/wkt/detail/wkt_multi.hpp b/include/boost/geometry/io/wkt/detail/wkt_multi.hpp index 2b2d1946ad..f332752160 100644 --- a/include/boost/geometry/io/wkt/detail/wkt_multi.hpp +++ b/include/boost/geometry/io/wkt/detail/wkt_multi.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. @@ -11,46 +11,13 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_GEOMETRY_DOMAINS_GIS_IO_WKT_DETAIL_WKT_MULTI_HPP -#define BOOST_GEOMETRY_DOMAINS_GIS_IO_WKT_DETAIL_WKT_MULTI_HPP +#ifndef BOOST_GEOMETRY_IO_WKT_MULTI_HPP +#define BOOST_GEOMETRY_IO_WKT_MULTI_HPP #include #include +#include +BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.") -namespace boost { namespace geometry -{ - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace wkt -{ - -struct prefix_null -{ - static inline const char* apply() { return ""; } -}; - -struct prefix_multipoint -{ - static inline const char* apply() { return "MULTIPOINT"; } -}; - -struct prefix_multilinestring -{ - static inline const char* apply() { return "MULTILINESTRING"; } -}; - -struct prefix_multipolygon -{ - static inline const char* apply() { return "MULTIPOLYGON"; } -}; - - - -}} // namespace wkt::impl -#endif - - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_DOMAINS_GIS_IO_WKT_DETAIL_WKT_MULTI_HPP +#endif // BOOST_GEOMETRY_IO_WKT_MULTI_HPP diff --git a/include/boost/geometry/io/wkt/read.hpp b/include/boost/geometry/io/wkt/read.hpp index ccfb4352cb..eae3a1d89f 100644 --- a/include/boost/geometry/io/wkt/read.hpp +++ b/include/boost/geometry/io/wkt/read.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. @@ -117,19 +117,26 @@ private : namespace detail { namespace wkt { -typedef boost::tokenizer > tokenizer; +inline auto make_tokenizer(std::string const& wkt) +{ + using separator = boost::char_separator; + using tokenizer = boost::tokenizer; + const tokenizer tokens(wkt, separator(" \n\t\r", ",()")); + return tokens; +} template ::value> struct parsing_assigner { - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, Point& point, std::string const& wkt) { - typedef typename coordinate_type::type coordinate_type; + using coordinate_type = typename coordinate_type::type; // Stop at end of tokens, or at "," ot ")" bool finished = (it == end || *it == "," || *it == ")"); @@ -167,8 +174,9 @@ struct parsing_assigner template struct parsing_assigner { - static inline void apply(tokenizer::iterator&, - tokenizer::iterator const&, + template + static inline void apply(TokenizerIterator&, + TokenizerIterator const&, Point&, std::string const&) { @@ -226,9 +234,9 @@ template struct container_inserter { // Version with output iterator - template - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, OutputIterator out) { @@ -258,10 +266,8 @@ template ::value> struct stateful_range_appender { - typedef typename geometry::point_type::type point_type; - // NOTE: Geometry is a reference - inline void append(Geometry geom, point_type const& point, bool) + inline void append(Geometry geom, typename geometry::point_type::type const& point, bool) { geometry::append(geom, point); } @@ -270,13 +276,13 @@ struct stateful_range_appender template struct stateful_range_appender { - typedef typename geometry::point_type::type point_type; - typedef typename boost::range_size + using point_type = typename geometry::point_type::type; + using size_type = typename boost::range_size < typename util::remove_cptrref::type - >::type size_type; + >::type; - BOOST_STATIC_ASSERT(( util::is_ring::value )); + BOOST_STATIC_ASSERT((util::is_ring::value)); inline stateful_range_appender() : pt_index(0) @@ -290,11 +296,10 @@ struct stateful_range_appender if (pt_index == 0) { first_point = point; - //should_append = true; } else { - // NOTE: if there is not enough Points, they're always appended + // NOTE: if there are not enough Points, they're always appended should_append = is_next_expected || pt_index < core_detail::closure::minimum_ring_size::value @@ -312,10 +317,10 @@ struct stateful_range_appender static inline bool disjoint(point_type const& p1, point_type const& p2) { // TODO: pass strategy - typedef typename strategies::io::services::default_strategy + using strategy_type = typename strategies::io::services::default_strategy < point_type - >::type strategy_type; + >::type; return detail::disjoint::disjoint_point_point(p1, p2, strategy_type()); } @@ -328,10 +333,11 @@ struct stateful_range_appender template struct container_appender { - typedef typename geometry::point_type::type point_type; + using point_type = typename geometry::point_type::type; - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Geometry out) { @@ -367,8 +373,9 @@ struct container_appender template struct point_parser { - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, P& point) { @@ -382,8 +389,9 @@ struct point_parser template struct linestring_parser { - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Geometry& geometry) { @@ -395,8 +403,9 @@ struct linestring_parser template struct ring_parser { - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Ring& ring) { @@ -417,11 +426,12 @@ struct ring_parser template struct polygon_parser { - typedef typename ring_return_type::type ring_return_type; - typedef container_appender appender; + using ring_return_type = typename ring_return_type::type; + using appender = container_appender; - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Polygon& poly) { @@ -457,7 +467,8 @@ struct polygon_parser }; -inline bool one_of(tokenizer::iterator const& it, +template +inline bool one_of(TokenizerIterator const& it, std::string const& value, bool& is_present) { @@ -469,7 +480,8 @@ inline bool one_of(tokenizer::iterator const& it, return false; } -inline bool one_of(tokenizer::iterator const& it, +template +inline bool one_of(TokenizerIterator const& it, std::string const& value, bool& present1, bool& present2) @@ -484,8 +496,9 @@ inline bool one_of(tokenizer::iterator const& it, } -inline void handle_empty_z_m(tokenizer::iterator& it, - tokenizer::iterator const& end, +template +inline void handle_empty_z_m(TokenizerIterator& it, + TokenizerIterator const& end, bool& has_empty, bool& has_z, bool& has_m) @@ -535,9 +548,9 @@ struct dimension \brief Internal, starts parsing \param geometry_name string to compare with first token */ -template -inline bool initialize(tokenizer::iterator& it, - tokenizer::iterator const& end, +template +inline bool initialize(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, std::string const& geometry_name) { @@ -552,8 +565,8 @@ inline bool initialize(tokenizer::iterator& it, // Silence warning C4127: conditional expression is constant #if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4127) +#pragma warning(push) +#pragma warning(disable : 4127) #endif if (has_z && dimension::value < 3) @@ -570,7 +583,7 @@ inline bool initialize(tokenizer::iterator& it, return false; } // M is ignored at all. - + return true; } @@ -582,17 +595,18 @@ struct geometry_parser { geometry::clear(geometry); - tokenizer tokens(wkt, boost::char_separator(" ", ",()")); - tokenizer::iterator it = tokens.begin(); - tokenizer::iterator const end = tokens.end(); + auto const tokens{make_tokenizer(wkt)}; + auto it = tokens.begin(); + auto const end = tokens.end(); apply(it, end, wkt, geometry); check_end(it, end, wkt); } - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Geometry& geometry) { @@ -611,17 +625,18 @@ struct multi_parser { traits::clear::apply(geometry); - tokenizer tokens(wkt, boost::char_separator(" ", ",()")); - tokenizer::iterator it = tokens.begin(); - tokenizer::iterator const end = tokens.end(); + auto const tokens{make_tokenizer(wkt)}; + auto it = tokens.begin(); + auto const end = tokens.end(); apply(it, end, wkt, geometry); check_end(it, end, wkt); } - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, MultiGeometry& geometry) { @@ -652,8 +667,9 @@ struct multi_parser template struct noparenthesis_point_parser { - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, P& point) { @@ -668,17 +684,18 @@ struct multi_point_parser { traits::clear::apply(geometry); - tokenizer tokens(wkt, boost::char_separator(" ", ",()")); - tokenizer::iterator it = tokens.begin(); - tokenizer::iterator const end = tokens.end(); + auto const tokens{make_tokenizer(wkt)}; + auto it = tokens.begin(); + auto const end = tokens.end(); apply(it, end, wkt, geometry); check_end(it, end, wkt); } - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, MultiGeometry& geometry) { @@ -735,17 +752,18 @@ struct box_parser { static inline void apply(std::string const& wkt, Box& box) { - tokenizer tokens(wkt, boost::char_separator(" ", ",()")); - tokenizer::iterator it = tokens.begin(); - tokenizer::iterator end = tokens.end(); + auto const tokens{make_tokenizer(wkt)}; + auto it = tokens.begin(); + auto end = tokens.end(); apply(it, end, wkt, box); check_end(it, end, wkt); } - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Box& box) { @@ -772,7 +790,7 @@ struct box_parser BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt)); } - typedef typename point_type::type point_type; + using point_type = typename point_type::type; std::vector points; container_inserter::apply(it, end, wkt, std::back_inserter(points)); @@ -815,21 +833,24 @@ struct segment_parser { static inline void apply(std::string const& wkt, Segment& segment) { - tokenizer tokens(wkt, boost::char_separator(" ", ",()")); - tokenizer::iterator it = tokens.begin(); - tokenizer::iterator end = tokens.end(); + auto const tokens{make_tokenizer(wkt)}; + auto it = tokens.begin(); + auto end = tokens.end(); apply(it, end, wkt, segment); check_end(it, end, wkt); } - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Segment& segment) { - if (it != end && (boost::iequals(*it, "SEGMENT") || boost::iequals(*it, "LINESTRING"))) + if (it != end + && (boost::iequals(*it, prefix_segment::apply()) + || boost::iequals(*it, prefix_linestring::apply()))) { ++it; } @@ -838,7 +859,7 @@ struct segment_parser BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt)); } - typedef typename point_type::type point_type; + using point_type = typename point_type::type; std::vector points; container_inserter::apply(it, end, wkt, std::back_inserter(points)); @@ -881,54 +902,67 @@ template > struct dynamic_readwkt_caller { - static inline void apply(tokenizer::iterator& it, - tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Geometry& geometry) { - if (boost::iequals(*it, "POINT")) + static const char* tag_point = prefix_point::apply(); + static const char* tag_linestring = prefix_linestring::apply(); + static const char* tag_polygon = prefix_polygon::apply(); + + static const char* tag_multi_point = prefix_multipoint::apply(); + static const char* tag_multi_linestring = prefix_multilinestring::apply(); + static const char* tag_multi_polygon = prefix_multipolygon::apply(); + + static const char* tag_segment = prefix_segment::apply(); + static const char* tag_box = prefix_box::apply(); + static const char* tag_gc = prefix_geometrycollection::apply(); + + if (boost::iequals(*it, tag_point)) { - parse_geometry("POINT", it, end, wkt, geometry); + parse_geometry(tag_point, it, end, wkt, geometry); } - else if (boost::iequals(*it, "MULTIPOINT")) + else if (boost::iequals(*it, tag_multi_point)) { - parse_geometry("MULTIPOINT", it, end, wkt, geometry); + parse_geometry(tag_multi_point, it, end, wkt, geometry); } - else if (boost::iequals(*it, "SEGMENT")) + else if (boost::iequals(*it, tag_segment)) { - parse_geometry("SEGMENT", it, end, wkt, geometry); + parse_geometry(tag_segment, it, end, wkt, geometry); } - else if (boost::iequals(*it, "LINESTRING")) + else if (boost::iequals(*it, tag_linestring)) { - parse_geometry("LINESTRING", it, end, wkt, geometry, false) - || parse_geometry("LINESTRING", it, end, wkt, geometry); + parse_geometry(tag_linestring, it, end, wkt, geometry, false) + || parse_geometry(tag_linestring, it, end, wkt, geometry); } - else if (boost::iequals(*it, "MULTILINESTRING")) + else if (boost::iequals(*it, tag_multi_linestring)) { - parse_geometry("MULTILINESTRING", it, end, wkt, geometry); + parse_geometry(tag_multi_linestring, it, end, wkt, geometry); } - else if (boost::iequals(*it, "BOX")) + else if (boost::iequals(*it, tag_box)) { - parse_geometry("BOX", it, end, wkt, geometry); + parse_geometry(tag_box, it, end, wkt, geometry); } - else if (boost::iequals(*it, "POLYGON")) + else if (boost::iequals(*it, tag_polygon)) { - parse_geometry("POLYGON", it, end, wkt, geometry, false) - || parse_geometry("POLYGON", it, end, wkt, geometry, false) - || parse_geometry("POLYGON", it, end, wkt, geometry); + parse_geometry(tag_polygon, it, end, wkt, geometry, false) + || parse_geometry(tag_polygon, it, end, wkt, geometry, false) + || parse_geometry(tag_polygon, it, end, wkt, geometry); } - else if (boost::iequals(*it, "MULTIPOLYGON")) + else if (boost::iequals(*it, tag_multi_polygon)) { - parse_geometry("MULTIPOLYGON", it, end, wkt, geometry); + parse_geometry(tag_multi_polygon, it, end, wkt, geometry); } - else if (boost::iequals(*it, "GEOMETRYCOLLECTION")) + else if (boost::iequals(*it, tag_gc)) { - parse_geometry("GEOMETRYCOLLECTION", it, end, wkt, geometry); + parse_geometry(tag_gc, it, end, wkt, geometry); } else { BOOST_THROW_EXCEPTION(read_wkt_exception( - "Should start with geometry's name, e.g. 'POINT', 'LINESTRING', 'POLYGON', etc.", + "Should start with geometry's type, for example 'POINT', 'LINESTRING', 'POLYGON'", wkt)); } } @@ -937,6 +971,7 @@ struct dynamic_readwkt_caller template < template class UnaryPred, + typename TokenizerIterator, typename Geom = typename util::sequence_find_if < typename traits::geometry_types::type, UnaryPred @@ -944,8 +979,8 @@ struct dynamic_readwkt_caller std::enable_if_t::value, int> = 0 > static bool parse_geometry(const char * , - tokenizer::iterator& it, - tokenizer::iterator const& end, + TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Geometry& geometry, bool = true) @@ -959,6 +994,7 @@ struct dynamic_readwkt_caller template < template class UnaryPred, + typename TokenizerIterator, typename Geom = typename util::sequence_find_if < typename traits::geometry_types::type, UnaryPred @@ -966,8 +1002,8 @@ struct dynamic_readwkt_caller std::enable_if_t::value, int> = 0 > static bool parse_geometry(const char * name, - tokenizer::iterator& , - tokenizer::iterator const& , + TokenizerIterator& , + TokenizerIterator const& , std::string const& wkt, Geometry& , bool throw_on_misfit = true) @@ -1084,13 +1120,13 @@ struct read_wkt { static inline void apply(std::string const& wkt, DynamicGeometry& dynamic_geometry) { - detail::wkt::tokenizer tokens(wkt, boost::char_separator(" ", ",()")); - detail::wkt::tokenizer::iterator it = tokens.begin(); - detail::wkt::tokenizer::iterator end = tokens.end(); + auto tokens{detail::wkt::make_tokenizer(wkt)}; + auto it = tokens.begin(); + auto end = tokens.end(); if (it == end) { BOOST_THROW_EXCEPTION(read_wkt_exception( - "Should start with geometry's name, e.g. 'POINT', 'LINESTRING', 'POLYGON', etc.", + "Should start with geometry's type, for example 'POINT', 'LINESTRING', 'POLYGON'", wkt)); } @@ -1111,21 +1147,23 @@ struct read_wkt { range::clear(geometry); - detail::wkt::tokenizer tokens(wkt, boost::char_separator(" ", ",()")); - detail::wkt::tokenizer::iterator it = tokens.begin(); - detail::wkt::tokenizer::iterator const end = tokens.end(); + auto tokens{detail::wkt::make_tokenizer(wkt)}; + auto it = tokens.begin(); + auto const end = tokens.end(); apply(it, end, wkt, geometry); detail::wkt::check_end(it, end, wkt); } - static inline void apply(detail::wkt::tokenizer::iterator& it, - detail::wkt::tokenizer::iterator const& end, + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, std::string const& wkt, Geometry& geometry) { - if (detail::wkt::initialize(it, end, wkt, "GEOMETRYCOLLECTION")) + if (detail::wkt::initialize(it, end, wkt, + detail::wkt::prefix_geometrycollection::apply())) { detail::wkt::handle_open_parenthesis(it, end, wkt); diff --git a/include/boost/geometry/io/wkt/write.hpp b/include/boost/geometry/io/wkt/write.hpp index 73b374c9eb..f57fb7b35d 100644 --- a/include/boost/geometry/io/wkt/write.hpp +++ b/include/boost/geometry/io/wkt/write.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2017 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2017 Bruno Lalande, Paris, France. // Copyright (c) 2009-2017 Mateusz Loskot, London, UK. // Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland. @@ -88,32 +88,6 @@ struct stream_coordinate {} }; -struct prefix_linestring_par -{ - static inline const char* apply() { return "LINESTRING("; } -}; - -struct prefix_ring_par_par -{ - // Note, double parentheses are intentional, indicating WKT ring begin/end - static inline const char* apply() { return "POLYGON(("; } -}; - -struct opening_parenthesis -{ - static inline const char* apply() { return "("; } -}; - -struct closing_parenthesis -{ - static inline const char* apply() { return ")"; } -}; - -struct double_closing_parenthesis -{ - static inline const char* apply() { return "))"; } -}; - /*! \brief Stream points as \ref WKT */ @@ -131,14 +105,13 @@ struct wkt_point /*! \brief Stream ranges as WKT -\note policy is used to stream prefix/postfix, enabling derived classes to override this */ template < typename Range, - bool ForceClosurePossible, typename PrefixPolicy, - typename SuffixPolicy + bool ForceClosurePossible = false, + bool WriteDoubleBrackets = false > struct wkt_range { @@ -146,52 +119,60 @@ struct wkt_range static inline void apply(std::basic_ostream& os, Range const& range, bool force_closure = ForceClosurePossible) { - typedef typename boost::range_iterator::type iterator_type; - - typedef stream_coordinate + using stream_type = stream_coordinate < point_type, 0, dimension::type::value - > stream_type; + >; bool first = true; os << PrefixPolicy::apply(); + os << "("; - // TODO: check EMPTY here - - iterator_type begin = boost::begin(range); - iterator_type end = boost::end(range); - for (iterator_type it = begin; it != end; ++it) + if (boost::size(range) > 0) { - os << (first ? "" : ","); - stream_type::apply(os, *it); - first = false; - } + if (WriteDoubleBrackets) + { + os << "("; + } + auto begin = boost::begin(range); + auto end = boost::end(range); + for (auto it = begin; it != end; ++it) + { + os << (first ? "" : ","); + stream_type::apply(os, *it); + first = false; + } - // optionally, close range to ring by repeating the first point - if (BOOST_GEOMETRY_CONDITION(ForceClosurePossible) - && force_closure - && boost::size(range) > 1 - && wkt_range::disjoint(*begin, *(end - 1))) - { - os << ","; - stream_type::apply(os, *begin); + // optionally, close range to ring by repeating the first point + if (BOOST_GEOMETRY_CONDITION(ForceClosurePossible) + && force_closure + && boost::size(range) > 1 + && wkt_range::disjoint(*begin, *(end - 1))) + { + os << ","; + stream_type::apply(os, *begin); + } + if (WriteDoubleBrackets) + { + os << ")"; + } } - os << SuffixPolicy::apply(); + os << ")"; } private: - typedef typename boost::range_value::type point_type; + using point_type = typename boost::range_value::type; static inline bool disjoint(point_type const& p1, point_type const& p2) { // TODO: pass strategy - typedef typename strategies::io::services::default_strategy + using strategy_type = typename strategies::io::services::default_strategy < point_type - >::type strategy_type; + >::type; return detail::disjoint::disjoint_point_point(p1, p2, strategy_type()); } @@ -206,9 +187,8 @@ struct wkt_sequence : wkt_range < Range, - ForceClosurePossible, - opening_parenthesis, - closing_parenthesis + prefix_null, + ForceClosurePossible > {}; @@ -219,20 +199,29 @@ struct wkt_poly static inline void apply(std::basic_ostream& os, Polygon const& poly, bool force_closure) { - typedef typename ring_type::type ring; + using ring = typename ring_type::type; + + auto const exterior = exterior_ring(poly); + auto const rings = interior_rings(poly); + + std::size_t point_count = boost::size(exterior); + for (auto it = boost::begin(rings); it != boost::end(rings); ++it) + { + point_count += boost::size(*it); + } os << PrefixPolicy::apply(); - // TODO: check EMPTY here - os << "("; - wkt_sequence::apply(os, exterior_ring(poly), force_closure); - typename interior_return_type::type - rings = interior_rings(poly); - for (typename detail::interior_iterator::type - it = boost::begin(rings); it != boost::end(rings); ++it) + os << "("; + if (point_count > 0) { - os << ","; - wkt_sequence::apply(os, *it, force_closure); + wkt_sequence::apply(os, exterior, force_closure); + + for (auto it = boost::begin(rings); it != boost::end(rings); ++it) + { + os << ","; + wkt_sequence::apply(os, *it, force_closure); + } } os << ")"; } @@ -247,13 +236,9 @@ struct wkt_multi Multi const& geometry, bool force_closure) { os << PrefixPolicy::apply(); - // TODO: check EMPTY here os << "("; - for (typename boost::range_iterator::type - it = boost::begin(geometry); - it != boost::end(geometry); - ++it) + for (auto it = boost::begin(geometry); it != boost::end(geometry); ++it) { if (it != boost::begin(geometry)) { @@ -269,7 +254,7 @@ struct wkt_multi template struct wkt_box { - typedef typename point_type::type point_type; + using point_type = typename point_type::type; template static inline void apply(std::basic_ostream& os, @@ -313,14 +298,14 @@ struct wkt_box template struct wkt_segment { - typedef typename point_type::type point_type; + using point_type = typename point_type::type; template static inline void apply(std::basic_ostream& os, Segment const& segment, bool) { // Convert to two points, then stream - typedef boost::array sequence; + using sequence = boost::array; sequence points; geometry::detail::assign_point_from_index<0>(segment, points[0]); @@ -363,9 +348,7 @@ struct wkt : detail::wkt::wkt_range < Linestring, - false, - detail::wkt::prefix_linestring_par, - detail::wkt::closing_parenthesis + detail::wkt::prefix_linestring > {}; @@ -395,9 +378,9 @@ struct wkt : detail::wkt::wkt_range < Ring, + detail::wkt::prefix_polygon, true, - detail::wkt::prefix_ring_par_par, - detail::wkt::double_closing_parenthesis + true > {}; diff --git a/include/boost/geometry/multi/io/wkt/detail/prefix.hpp b/include/boost/geometry/multi/io/wkt/detail/prefix.hpp index 34e2b241d0..ee6b37e592 100644 --- a/include/boost/geometry/multi/io/wkt/detail/prefix.hpp +++ b/include/boost/geometry/multi/io/wkt/detail/prefix.hpp @@ -14,8 +14,7 @@ #ifndef BOOST_GEOMETRY_MULTI_IO_WKT_DETAIL_PREFIX_HPP #define BOOST_GEOMETRY_MULTI_IO_WKT_DETAIL_PREFIX_HPP - -#include - +#include +BOOST_PRAGMA_MESSAGE("This include file is deprecated and will be removed in the future.") #endif // BOOST_GEOMETRY_MULTI_IO_WKT_DETAIL_PREFIX_HPP diff --git a/test/algorithms/for_each.cpp b/test/algorithms/for_each.cpp index 922225674c..968646ae77 100644 --- a/test/algorithms/for_each.cpp +++ b/test/algorithms/for_each.cpp @@ -87,12 +87,12 @@ void test_all() "POLYGON EMPTY" , 0 - , "POLYGON(())" - , "POLYGON(())" + , "POLYGON()" + , "POLYGON()" , "" , 0 - , "POLYGON(())" + , "POLYGON()" ); test_geometry > // open ring ( diff --git a/test/io/wkt/wkt.cpp b/test/io/wkt/wkt.cpp index da1dd6d15d..24da7c8b8a 100644 --- a/test/io/wkt/wkt.cpp +++ b/test/io/wkt/wkt.cpp @@ -1,7 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. @@ -51,9 +51,8 @@ void check_wkt(G const& geometry, std::string const& expected) template void check_to_wkt(G const& geometry, std::string const& expected) { - std::string out_string; - out_string = bg::to_wkt(geometry); - BOOST_CHECK_EQUAL(boost::to_upper_copy(out_string), + std::string const out = bg::to_wkt(geometry); + BOOST_CHECK_EQUAL(boost::to_upper_copy(out), boost::to_upper_copy(expected)); } @@ -156,7 +155,6 @@ void test_wkt(std::string const& wkt, template void test_relaxed_wkt_read_write(std::string const& wkt, std::string const& expected) { - std::string e; G geometry; bg::read_wkt(wkt, geometry); std::ostringstream out; @@ -168,11 +166,9 @@ void test_relaxed_wkt_read_write(std::string const& wkt, std::string const& expe template void test_relaxed_wkt_to_from(std::string const& wkt, std::string const& expected) { - std::string e; G geometry; geometry = bg::from_wkt(wkt); - std::string out; - out = bg::to_wkt(geometry); + std::string const out = bg::to_wkt(geometry); BOOST_CHECK_EQUAL(boost::to_upper_copy(out), boost::to_upper_copy(expected)); } @@ -328,29 +324,32 @@ void test_all() //test_wkt >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 4, 0, 1, 4); test_wkt >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 5, 0, 1, 4); - // We accept empty sequences as well (much better than EMPTY)... - // ...or even POINT() (see below) - test_wkt >("LINESTRING()", 0, 0); - test_wkt >("POLYGON(())", 0); - // ... or even with empty holes - test_wkt >("POLYGON((),(),())", 0); - // which all make no valid geometries, but they can exist. + test_relaxed_wkt >("LINESTRING EMPTY", "LINESTRING()"); + test_relaxed_wkt >("POLYGON EMPTY", "POLYGON()"); + test_relaxed_wkt >("POLYGON EMPTY", "POLYGON()"); + + // Accept empty sequences as well + test_relaxed_wkt >("LINESTRING()", "LINESTRING()"); + test_relaxed_wkt >("POLYGON()", "POLYGON()"); + test_relaxed_wkt >("POLYGON(())", "POLYGON()"); + test_relaxed_wkt >("POLYGON((),(),())", "POLYGON()"); + // Invalid polygon with an inner ring coordinate is outputted as such + test_relaxed_wkt >("POLYGON((),(),(1 2))", "POLYGON((),(),(1 2))"); + // Non OGC: tabs and returns are allowed and handled as normal white space. + test_relaxed_wkt

("POINT(1\n2)", "POINT(1 2)"); + test_relaxed_wkt

("POINT(1\t2)", "POINT(1 2)"); + test_relaxed_wkt

("POINT(1\r2)", "POINT(1 2)"); // These WKT's are incomplete or abnormal but they are considered OK test_relaxed_wkt

("POINT(1)", "POINT(1 0)"); test_relaxed_wkt

("POINT()", "POINT(0 0)"); - test_relaxed_wkt >("LINESTRING(1,2,3)", - "LINESTRING(1 0,2 0,3 0)"); + test_relaxed_wkt >("LINESTRING(1,2,3)", "LINESTRING(1 0,2 0,3 0)"); test_relaxed_wkt

("POINT ( 1 2) ", "POINT(1 2)"); test_relaxed_wkt

("POINT M ( 1 2)", "POINT(1 2)"); test_relaxed_wkt >("BOX(1 1,2 2)", "POLYGON((1 1,1 2,2 2,2 1,1 1))"); - - test_relaxed_wkt >("LINESTRING EMPTY", "LINESTRING()"); - - test_relaxed_wkt >("POLYGON( ( ) , ( ) , ( ) )", - "POLYGON((),(),())"); + test_relaxed_wkt >("POLYGON( ( ) , ( ) , ( ) )", "POLYGON()"); // Wrong WKT's test_wrong_wkt

("POINT(1 2", "expected ')'"); @@ -398,33 +397,3 @@ int test_main(int, char* []) return 0; } - -/* - -Results can be checked in PostGIS by query below, -or by MySQL (but replace length by glength and remove the perimeter) - -Note: -- PostGIS gives "3" for a numpoints of a multi-linestring of 6 points in total (!) - --> "npoints" should be taken for all geometries -- SQL Server 2008 gives "6" - select geometry::STGeomFromText('MULTILINESTRING((1 1,2 2,3 3),(4 4,5 5,6 6))',0).STNumPoints() -- MySQL gives "NULL" - -select 1 as code,'np p' as header,npoints(geomfromtext('POINT(1 2)')) as contents -union select 2,'length point', length(geomfromtext('POINT(1 2)')) -union select 3,'peri point', perimeter(geomfromtext('POINT(1 2)')) -union select 4,'area point',area(geomfromtext('POINT(1 2)')) - - -union select 5,'# ls',npoints(geomfromtext('LINESTRING(1 1,2 2,3 3)')) -union select 6,'length ls',length(geomfromtext('LINESTRING(1 1,2 2,3 3)')) -union select 7,'peri ls',perimeter(geomfromtext('LINESTRING(1 1,2 2,3 3)')) -union select 8,'aera ls',area(geomfromtext('LINESTRING(1 1,2 2,3 3)')) - -union select 9,'# poly',npoints(geomfromtext('POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))')) -union select 10,'length poly',length(geomfromtext('POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))')) -union select 11,'peri poly',perimeter(geomfromtext('POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))')) -union select 12,'area poly',area(geomfromtext('POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))')) - -*/ diff --git a/test/io/wkt/wkt_multi.cpp b/test/io/wkt/wkt_multi.cpp index 8147174567..867c947da8 100644 --- a/test/io/wkt/wkt_multi.cpp +++ b/test/io/wkt/wkt_multi.cpp @@ -1,7 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. @@ -76,6 +76,18 @@ void test_all() test_wkt > >("multilinestring((1 1,2 2,3 3),(4 4,5 5,6 6))", 6, 4 * sqrt(2.0)); test_wkt > >("multipolygon(((0 0,0 2,2 2,2 0,0 0),(1 1,1 2,2 2,2 1,1 1)),((0 0,0 4,4 4,4 0,0 0)))", 15, 0, 21, 28); + // Support tabs, and new lines. + test_relaxed_wkt >("multipoint((1\t2),\n(3\r4))", "multipoint((1 2),(3 4))"); + + // Verify empty multi geometries + test_relaxed_wkt >("multipoint()", "multipoint()"); + test_relaxed_wkt >>("multilinestring()", "multilinestring()"); + test_relaxed_wkt > >("multipolygon()", "multipolygon()"); + + test_relaxed_wkt >("multipoint empty", "multipoint()"); + test_relaxed_wkt >>("multilinestring empty", "multilinestring()"); + test_relaxed_wkt > >("multipolygon empty", "multipolygon()"); + // Support for the official alternative syntax for multipoint // (provided by Aleksey Tulinov): test_relaxed_wkt >("multipoint(1 2,3 4)", "multipoint((1 2),(3 4))"); @@ -103,24 +115,3 @@ void test_all() test_order_closure(); } - -/* - -... see comments in "wkt.cpp" - -union select 13,'# mpoint',npoints(geomfromtext('MULTIPOINT((1 2),(3 4))')) -union select 14,'length mpoint',length(geomfromtext('MULTIPOINT((1 2),(3 4))')) -union select 15,'peri mpoint',perimeter(geomfromtext('MULTIPOINT((1 2),(3 4))')) -union select 16,'area mpoint',area(geomfromtext('MULTIPOINT((1 2),(3 4))')) - -union select 17,'# mls',npoints(geomfromtext('MULTILINESTRING((1 1,2 2,3 3),(4 4,5 5,6 6))')) -union select 18,'length mls',length(geomfromtext('MULTILINESTRING((1 1,2 2,3 3),(4 4,5 5,6 6))')) -union select 19,'peri mls',perimeter(geomfromtext('MULTILINESTRING((1 1,2 2,3 3),(4 4,5 5,6 6))')) -union select 20,'area mls',area(geomfromtext('MULTILINESTRING((1 1,2 2,3 3),(4 4,5 5,6 6))')) - -union select 21,'# mpoly',npoints(geomfromtext('MULTIPOLYGON(((0 0,0 2,2 2,2 0,0 0),(1 1,1 2,2 2,2 1,1 1)),((0 0,0 4,4 4,4 0,0 0)))')) -union select 22,'length mpoly',length(geomfromtext('MULTIPOLYGON(((0 0,0 2,2 2,2 0,0 0),(1 1,1 2,2 2,2 1,1 1)),((0 0,0 4,4 4,4 0,0 0)))')) -union select 23,'peri mpoly',perimeter(geomfromtext('MULTIPOLYGON(((0 0,0 2,2 2,2 0,0 0),(1 1,1 2,2 2,2 1,1 1)),((0 0,0 4,4 4,4 0,0 0)))')) -union select 24,'area mpoly',area(geomfromtext('MULTIPOLYGON(((0 0,0 2,2 2,2 0,0 0),(1 1,1 2,2 2,2 1,1 1)),((0 0,0 4,4 4,4 0,0 0)))')) - -*/