From 76772cd0d131f6832f53af3576eadf7146711136 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 7 Feb 2019 00:52:04 +0100 Subject: [PATCH 01/49] [extensions][io] Add read_shapefile() implementation. --- .../extensions/gis/io/shapefile/read.hpp | 592 ++++++++++++++++++ 1 file changed, 592 insertions(+) create mode 100644 include/boost/geometry/extensions/gis/io/shapefile/read.hpp diff --git a/include/boost/geometry/extensions/gis/io/shapefile/read.hpp b/include/boost/geometry/extensions/gis/io/shapefile/read.hpp new file mode 100644 index 000000000..6b4107396 --- /dev/null +++ b/include/boost/geometry/extensions/gis/io/shapefile/read.hpp @@ -0,0 +1,592 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_EXTENSIONS_GIS_IO_SHAPEFILE_READ_HPP +#define BOOST_GEOMETRY_EXTENSIONS_GIS_IO_SHAPEFILE_READ_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + +class read_shapefile_exception : public geometry::exception +{ +public: + explicit read_shapefile_exception(const char * msg) + : m_msg(msg) + {} + + explicit read_shapefile_exception(std::string const& msg) + : m_msg(msg) + {} + + virtual char const* what() const throw() + { + //return "Shapefile read error"; + return m_msg.what(); + } + +private: + std::runtime_error m_msg; +}; + +namespace detail { namespace shapefile +{ + +template +inline void read_native(IStream & is, T & v) +{ + is.read(reinterpret_cast(&v), sizeof(T)); +} + +template +inline void read_big(IStream & is, T & v) +{ + is.read(reinterpret_cast(&v), sizeof(T)); + v = boost::endian::big_to_native(v); +} + +template +inline void read_little(IStream & is, T & v) +{ + is.read(reinterpret_cast(&v), sizeof(T)); + v = boost::endian::little_to_native(v); +} + +template +inline boost::int32_t reset_and_read_header(IStream & is) +{ + is.clear(); + is.seekg(0); + + boost::int32_t code = 0; + read_big(is, code); + + if (code != 9994) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Invalid header code")); + } + + // 5 unused, length, version + is.seekg(7 * sizeof(boost::int32_t), IStream::cur); + + boost::int32_t type = 0; + read_little(is, type); + + // TODO: support filtering + /* + double min_x, min_y, max_x, max_y; + double min_z, max_z; + double min_m, max_m; + read_little(is, min_x); + read_little(is, min_y); + read_little(is, max_x); + read_little(is, max_y); + read_little(is, min_z); + read_little(is, max_z); + read_little(is, min_m); + read_little(is, max_m); + */ + is.seekg(8 * sizeof(double), IStream::cur); + + if (!is.good()) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Unreadable header")); + } + + return type; +} + +template +inline bool read_record_header(IStream & is) +{ + //read_big(is, number); + //read_big(is, length); + + is.seekg(sizeof(boost::int32_t), IStream::cur); + // only to set flags + boost::int32_t foo; + read_native(is, foo); + + return is.good(); +} + +struct shape_type +{ + enum enum_t + { + null_shape = 0, + point = 1, + polyline = 3, + polygon = 5, + multipoint = 8 + // TODO: support the rest + }; +}; + +template +inline typename boost::range_reference::type +push_back(Range & rng, typename boost::range_value::type const& v) +{ + range::push_back(rng, v); + return range::back(rng); +} + +template +inline void read_and_push_back_point(IStream & is, Range & rng) +{ + typedef typename boost::range_value::type pt_type; + + double x, y; + read_little(is, x); + read_little(is, y); + + pt_type pt; + geometry::set<0>(pt, x); + geometry::set<1>(pt, y); + + range::push_back(rng, pt); +} + +template +inline void read_and_set_point_at(IStream & is, Range & rng, std::size_t index) +{ + typedef typename boost::range_value::type pt_type; + + double x, y; + read_little(is, x); + read_little(is, y); + + pt_type & pt = range::at(rng, index); + geometry::set<0>(pt, x); + geometry::set<1>(pt, y); +} + +template +inline void read_parts(IStream & is, + std::vector & parts, + boost::int32_t num_parts) +{ + parts.resize(num_parts); + for (boost::int32_t i = 0 ; i < num_parts ; ++i) + { + read_little(is, parts[i]); + } +} + +// Range of Points +template +inline void read_point(IStream & is, Points & points) +{ + boost::int32_t type; + read_little(is, type); + if (type != shape_type::point) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Point expected")); + } + + read_and_push_back_point(is, points); + + if (!is.good()) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); + } +} + +// Range of Points +template +inline void read_multipoint(IStream & is, Points & points) +{ + typedef typename boost::range_value::type pt_type; + + boost::int32_t type; + read_little(is, type); + if (type != shape_type::multipoint) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Multipoint expected")); + } + + is.seekg(4 * sizeof(double), IStream::cur); + + boost::int32_t num_points; + read_little(is, num_points); + + if (num_points < 0) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Points number lesser than 0")); + } + + std::size_t old_size = boost::size(points); + range::resize(points, old_size + num_points); + for (boost::int32_t i = 0; i < num_points; ++i) + { + read_and_set_point_at(is, points, old_size + i); + } + + if (!is.good()) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); + } +} + +// Range of Linestrings +template +inline void read_polyline(IStream &is, Linestrings & linestrings) +{ + typedef typename boost::range_value::type ls_type; + typedef typename boost::range_value::type pt_type; + + boost::int32_t type; + //double min_x, min_y, max_x, max_y; + boost::int32_t num_parts; + boost::int32_t num_points; + std::vector parts; + + read_little(is, type); + if (type != shape_type::polyline) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Polyline expected")); + } + + // TODO: support filtering + //read_little(is, min_x); + //read_little(is, min_y); + //read_little(is, max_x); + //read_little(is, max_y); + is.seekg(4 * sizeof(double), IStream::cur); + read_little(is, num_parts); + read_little(is, num_points); + + if (num_parts < 0 || num_points < 0) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Parts or points number lesser than 0")); + } + + read_parts(is, parts, num_parts); + + for (boost::int32_t i = 0; i < num_parts; ++i) + { + boost::int32_t f = parts[i]; + boost::int32_t l = (i + 1) < num_parts ? parts[i + 1] : num_points; + + if (f >= num_points || l > num_points || f > l) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Invalid part number")); + } + + ls_type & ls = push_back(linestrings, ls_type()); + + std::size_t ls_size = l - f; + + range::resize(ls, ls_size); + + for (std::size_t j = 0; j < ls_size; ++j) + { + read_and_set_point_at(is, ls, j); + } + } + + if (!is.good()) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); + } +} + +// Range of Polygons +template +inline void read_polygon(IStream &is, Polygons & polygons) +{ + typedef typename boost::range_value::type poly_type; + typedef typename geometry::point_type::type pt_type; + typedef typename geometry::ring_type::type ring_type; + + static const bool is_ccw = geometry::point_order::value == geometry::counterclockwise; + static const bool is_open = geometry::closure::value == geometry::open; + + boost::int32_t type; + //double min_x, min_y, max_x, max_y; + boost::int32_t num_parts; + boost::int32_t num_points; + std::vector parts; + + read_little(is, type); + if (type != shape_type::polygon) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Polygon expected")); + } + + // TODO: support filtering + //read_little(is, min_x); + //read_little(is, min_y); + //read_little(is, max_x); + //read_little(is, max_y); + is.seekg(4 * sizeof(double), IStream::cur); + read_little(is, num_parts); + read_little(is, num_points); + + if (num_parts < 0 || num_points < 0) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Parts or points number lesser than 0")); + } + + read_parts(is, parts, num_parts); + + poly_type & poly = push_back(polygons, poly_type()); + + for (boost::int32_t i = 0; i < num_parts; ++i) + { + boost::int32_t f = parts[i]; + boost::int32_t l = (i + 1) < num_parts ? parts[i + 1] : num_points; + + if (f >= num_points || l > num_points || f > l) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Invalid part number")); + } + + ring_type & ring = (i == 0) + ? geometry::exterior_ring(poly) + : push_back(geometry::interior_rings(poly), ring_type()); + + std::size_t ring_size = l - f - (is_open ? 1 : 0); + + range::resize(ring, ring_size); + + for (std::size_t j = 0; j < ring_size; ++j) + { + read_and_set_point_at(is, ring, j); + } + + // if ring is open ignore the last point + if (is_open) + { + is.seekg(2 * sizeof(double), IStream::cur); + } + + // if ring is ccw reverse leaving the first point untouched + if (is_ccw) + { + typename boost::range_iterator::type + b = boost::begin(ring), + e = boost::end(ring); + std::reverse(++b, is_open ? e : (--e)); + } + } + + if (!is.good()) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); + } +} + +}} // namespace detail::shapefile + +namespace dispatch +{ + +template +< + typename Geometry, + typename Tag = typename geometry::tag::type +> +struct read_shapefile + : not_implemented +{}; + +template +struct read_shapefile +{ + template + static inline void apply(IStream &is, Points & points) + { + boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + + if (type == detail::shapefile::shape_type::point) + { + for (;;) + { + if (! detail::shapefile::read_record_header(is)) + break; + + detail::shapefile::read_point(is, points); + } + } + else if (type == detail::shapefile::shape_type::multipoint) + { + for (;;) + { + if (! detail::shapefile::read_record_header(is)) + break; + + detail::shapefile::read_multipoint(is, points); + } + } + } +}; + +template +struct read_shapefile +{ + template + static inline void apply(IStream &is, MultiPoints & multi_points) + { + typedef typename boost::range_value::type mpt_type; + + boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + + if (type == detail::shapefile::shape_type::point) + { + mpt_type & mpt = detail::shapefile::push_back(multi_points, mpt_type()); + + for (;;) + { + if (! detail::shapefile::read_record_header(is)) + break; + + detail::shapefile::read_point(is, mpt); + } + } + else if (type == detail::shapefile::shape_type::multipoint) + { + for (;;) + { + if (! detail::shapefile::read_record_header(is)) + break; + + mpt_type & mpt = detail::shapefile::push_back(multi_points, mpt_type()); + + detail::shapefile::read_multipoint(is, mpt); + } + } + } +}; + +template +struct read_shapefile +{ + template + static inline void apply(IStream &is, Linestrings & linestrings) + { + boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + + if (type == detail::shapefile::shape_type::polyline) + { + for (;;) + { + if (! detail::shapefile::read_record_header(is)) + break; + + detail::shapefile::read_polyline(is, linestrings); + } + } + } +}; + +template +struct read_shapefile +{ + template + static inline void apply(IStream &is, MultiLinestrings & multi_linestrings) + { + typedef typename boost::range_value::type mls_type; + + boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + + if (type == detail::shapefile::shape_type::polyline) + { + for (;;) + { + if (! detail::shapefile::read_record_header(is)) + break; + + mls_type & mls = detail::shapefile::push_back(multi_linestrings, mls_type()); + + detail::shapefile::read_polyline(is, mls); + } + } + } +}; + +template +struct read_shapefile +{ + template + static inline void apply(IStream &is, Polygons & polygons) + { + boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + + if (type == detail::shapefile::shape_type::polygon) + { + for (;;) + { + if (! detail::shapefile::read_record_header(is)) + break; + + detail::shapefile::read_polygon(is, polygons); + } + } + } +}; + +template +struct read_shapefile +{ + template + static inline void apply(IStream &is, MultiPolygons & multi_polygons) + { + typedef typename boost::range_value::type mpoly_type; + + boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + + if (type == detail::shapefile::shape_type::polygon) + { + for (;;) + { + if (! detail::shapefile::read_record_header(is)) + break; + + mpoly_type & mpoly = detail::shapefile::push_back(multi_polygons, mpoly_type()); + + detail::shapefile::read_polygon(is, mpoly); + } + } + } +}; + + +} // namespace dispatch + + +template +inline void read_shapefile(IStream &is, RangeOfGeometries & range_of_geometries) +{ + typedef typename boost::range_value::type geometry_type; + geometry::concepts::check(); + dispatch::read_shapefile::apply(is, range_of_geometries); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_EXTENSIONS_GIS_IO_SHAPEFILE_READ_HPP From 784e5635220207abff40473974ba4fed134b3b3c Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 7 Feb 2019 15:18:08 +0100 Subject: [PATCH 02/49] [extensions][io] Make the code less repeatable. --- .../extensions/gis/io/shapefile/read.hpp | 488 +++++++++--------- 1 file changed, 244 insertions(+), 244 deletions(-) diff --git a/include/boost/geometry/extensions/gis/io/shapefile/read.hpp b/include/boost/geometry/extensions/gis/io/shapefile/read.hpp index 6b4107396..cf9cfcb01 100644 --- a/include/boost/geometry/extensions/gis/io/shapefile/read.hpp +++ b/include/boost/geometry/extensions/gis/io/shapefile/read.hpp @@ -195,207 +195,256 @@ inline void read_parts(IStream & is, } } -// Range of Points -template -inline void read_point(IStream & is, Points & points) +struct read_point_policy { - boost::int32_t type; - read_little(is, type); - if (type != shape_type::point) + template + static inline void apply(IStream & is, Points & points) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Point expected")); + boost::int32_t type; + read_little(is, type); + if (type != shape_type::point) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Point expected")); + } + + read_and_push_back_point(is, points); + + if (!is.good()) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); + } } +}; - read_and_push_back_point(is, points); - - if (!is.good()) - { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); - } -} - -// Range of Points -template -inline void read_multipoint(IStream & is, Points & points) +struct read_multipoint_policy { - typedef typename boost::range_value::type pt_type; - - boost::int32_t type; - read_little(is, type); - if (type != shape_type::multipoint) + template + static inline void apply(IStream & is, Points & points) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Multipoint expected")); + typedef typename boost::range_value::type pt_type; + + boost::int32_t type; + read_little(is, type); + if (type != shape_type::multipoint) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Multipoint expected")); + } + + is.seekg(4 * sizeof(double), IStream::cur); + + boost::int32_t num_points; + read_little(is, num_points); + + if (num_points < 0) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Points number lesser than 0")); + } + + std::size_t old_size = boost::size(points); + range::resize(points, old_size + num_points); + for (boost::int32_t i = 0; i < num_points; ++i) + { + read_and_set_point_at(is, points, old_size + i); + } + + if (!is.good()) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); + } } +}; - is.seekg(4 * sizeof(double), IStream::cur); - - boost::int32_t num_points; - read_little(is, num_points); - - if (num_points < 0) - { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Points number lesser than 0")); - } - - std::size_t old_size = boost::size(points); - range::resize(points, old_size + num_points); - for (boost::int32_t i = 0; i < num_points; ++i) - { - read_and_set_point_at(is, points, old_size + i); - } - - if (!is.good()) - { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); - } -} - -// Range of Linestrings -template -inline void read_polyline(IStream &is, Linestrings & linestrings) +struct read_polyline_policy { - typedef typename boost::range_value::type ls_type; - typedef typename boost::range_value::type pt_type; - - boost::int32_t type; - //double min_x, min_y, max_x, max_y; - boost::int32_t num_parts; - boost::int32_t num_points; - std::vector parts; - - read_little(is, type); - if (type != shape_type::polyline) + template + static inline void apply(IStream &is, Linestrings & linestrings) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Polyline expected")); - } + typedef typename boost::range_value::type ls_type; + typedef typename boost::range_value::type pt_type; - // TODO: support filtering - //read_little(is, min_x); - //read_little(is, min_y); - //read_little(is, max_x); - //read_little(is, max_y); - is.seekg(4 * sizeof(double), IStream::cur); - read_little(is, num_parts); - read_little(is, num_points); + boost::int32_t type; + //double min_x, min_y, max_x, max_y; + boost::int32_t num_parts; + boost::int32_t num_points; + std::vector parts; - if (num_parts < 0 || num_points < 0) - { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Parts or points number lesser than 0")); - } + read_little(is, type); + if (type != shape_type::polyline) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Polyline expected")); + } - read_parts(is, parts, num_parts); + // TODO: support filtering + //read_little(is, min_x); + //read_little(is, min_y); + //read_little(is, max_x); + //read_little(is, max_y); + is.seekg(4 * sizeof(double), IStream::cur); + read_little(is, num_parts); + read_little(is, num_points); + + if (num_parts < 0 || num_points < 0) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Parts or points number lesser than 0")); + } + + read_parts(is, parts, num_parts); - for (boost::int32_t i = 0; i < num_parts; ++i) - { - boost::int32_t f = parts[i]; - boost::int32_t l = (i + 1) < num_parts ? parts[i + 1] : num_points; - - if (f >= num_points || l > num_points || f > l) + for (boost::int32_t i = 0; i < num_parts; ++i) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Invalid part number")); + boost::int32_t f = parts[i]; + boost::int32_t l = (i + 1) < num_parts ? parts[i + 1] : num_points; + + if (f >= num_points || l > num_points || f > l) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Invalid part number")); + } + + ls_type & ls = push_back(linestrings, ls_type()); + + std::size_t ls_size = l - f; + + range::resize(ls, ls_size); + + for (std::size_t j = 0; j < ls_size; ++j) + { + read_and_set_point_at(is, ls, j); + } } - ls_type & ls = push_back(linestrings, ls_type()); - - std::size_t ls_size = l - f; - - range::resize(ls, ls_size); - - for (std::size_t j = 0; j < ls_size; ++j) + if (!is.good()) { - read_and_set_point_at(is, ls, j); + BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); } } +}; - if (!is.good()) +struct read_polygon_policy +{ + template + static inline void apply(IStream &is, Polygons & polygons) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); + typedef typename boost::range_value::type poly_type; + typedef typename geometry::point_type::type pt_type; + typedef typename geometry::ring_type::type ring_type; + + static const bool is_ccw = geometry::point_order::value == geometry::counterclockwise; + static const bool is_open = geometry::closure::value == geometry::open; + + boost::int32_t type; + //double min_x, min_y, max_x, max_y; + boost::int32_t num_parts; + boost::int32_t num_points; + std::vector parts; + + read_little(is, type); + if (type != shape_type::polygon) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Polygon expected")); + } + + // TODO: support filtering + //read_little(is, min_x); + //read_little(is, min_y); + //read_little(is, max_x); + //read_little(is, max_y); + is.seekg(4 * sizeof(double), IStream::cur); + read_little(is, num_parts); + read_little(is, num_points); + + if (num_parts < 0 || num_points < 0) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Parts or points number lesser than 0")); + } + + read_parts(is, parts, num_parts); + + poly_type & poly = push_back(polygons, poly_type()); + + for (boost::int32_t i = 0; i < num_parts; ++i) + { + boost::int32_t f = parts[i]; + boost::int32_t l = (i + 1) < num_parts ? parts[i + 1] : num_points; + + if (f >= num_points || l > num_points || f > l) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Invalid part number")); + } + + ring_type & ring = (i == 0) + ? geometry::exterior_ring(poly) + : push_back(geometry::interior_rings(poly), ring_type()); + + std::size_t ring_size = l - f - (is_open ? 1 : 0); + + range::resize(ring, ring_size); + + for (std::size_t j = 0; j < ring_size; ++j) + { + read_and_set_point_at(is, ring, j); + } + + // if ring is open ignore the last point + if (is_open) + { + is.seekg(2 * sizeof(double), IStream::cur); + } + + // if ring is ccw reverse leaving the first point untouched + if (is_ccw) + { + typename boost::range_iterator::type + b = boost::begin(ring), + e = boost::end(ring); + std::reverse(++b, is_open ? e : (--e)); + } + } + + if (!is.good()) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); + } + } +}; + +template +inline void add_records(IStream & is, Range & rng) +{ + while (read_record_header(is)) + { + Policy::apply(is, rng); } } -// Range of Polygons -template -inline void read_polygon(IStream &is, Polygons & polygons) +template +inline void add_records_as_new_element(IStream & is, Range & rng) { - typedef typename boost::range_value::type poly_type; - typedef typename geometry::point_type::type pt_type; - typedef typename geometry::ring_type::type ring_type; + typedef typename boost::range_value::type val_type; - static const bool is_ccw = geometry::point_order::value == geometry::counterclockwise; - static const bool is_open = geometry::closure::value == geometry::open; - - boost::int32_t type; - //double min_x, min_y, max_x, max_y; - boost::int32_t num_parts; - boost::int32_t num_points; - std::vector parts; - - read_little(is, type); - if (type != shape_type::polygon) + if (! read_record_header(is)) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Polygon expected")); + return; } - // TODO: support filtering - //read_little(is, min_x); - //read_little(is, min_y); - //read_little(is, max_x); - //read_little(is, max_y); - is.seekg(4 * sizeof(double), IStream::cur); - read_little(is, num_parts); - read_little(is, num_points); + val_type & elem = shapefile::push_back(rng, val_type()); - if (num_parts < 0 || num_points < 0) + do { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Parts or points number lesser than 0")); + Policy::apply(is, elem); } + while (read_record_header(is)); +} - read_parts(is, parts, num_parts); +template +inline void add_records_as_new_elements(IStream & is, Range & rng) +{ + typedef typename boost::range_value::type val_type; - poly_type & poly = push_back(polygons, poly_type()); - - for (boost::int32_t i = 0; i < num_parts; ++i) + while (read_record_header(is)) { - boost::int32_t f = parts[i]; - boost::int32_t l = (i + 1) < num_parts ? parts[i + 1] : num_points; + val_type & elem = shapefile::push_back(rng, val_type()); - if (f >= num_points || l > num_points || f > l) - { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Invalid part number")); - } - - ring_type & ring = (i == 0) - ? geometry::exterior_ring(poly) - : push_back(geometry::interior_rings(poly), ring_type()); - - std::size_t ring_size = l - f - (is_open ? 1 : 0); - - range::resize(ring, ring_size); - - for (std::size_t j = 0; j < ring_size; ++j) - { - read_and_set_point_at(is, ring, j); - } - - // if ring is open ignore the last point - if (is_open) - { - is.seekg(2 * sizeof(double), IStream::cur); - } - - // if ring is ccw reverse leaving the first point untouched - if (is_ccw) - { - typename boost::range_iterator::type - b = boost::begin(ring), - e = boost::end(ring); - std::reverse(++b, is_open ? e : (--e)); - } - } - - if (!is.good()) - { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); + Policy::apply(is, elem); } } @@ -419,27 +468,17 @@ struct read_shapefile template static inline void apply(IStream &is, Points & points) { - boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + namespace shp = detail::shapefile; + + boost::int32_t const type = shp::reset_and_read_header(is); - if (type == detail::shapefile::shape_type::point) + if (type == shp::shape_type::point) { - for (;;) - { - if (! detail::shapefile::read_record_header(is)) - break; - - detail::shapefile::read_point(is, points); - } + shp::add_records(is, points); } else if (type == detail::shapefile::shape_type::multipoint) { - for (;;) - { - if (! detail::shapefile::read_record_header(is)) - break; - - detail::shapefile::read_multipoint(is, points); - } + shp::add_records(is, points); } } }; @@ -450,33 +489,17 @@ struct read_shapefile template static inline void apply(IStream &is, MultiPoints & multi_points) { - typedef typename boost::range_value::type mpt_type; + namespace shp = detail::shapefile; + + boost::int32_t const type = shp::reset_and_read_header(is); - boost::int32_t const type = detail::shapefile::reset_and_read_header(is); - - if (type == detail::shapefile::shape_type::point) + if (type == shp::shape_type::point) { - mpt_type & mpt = detail::shapefile::push_back(multi_points, mpt_type()); - - for (;;) - { - if (! detail::shapefile::read_record_header(is)) - break; - - detail::shapefile::read_point(is, mpt); - } + shp::add_records_as_new_element(is, multi_points); } else if (type == detail::shapefile::shape_type::multipoint) { - for (;;) - { - if (! detail::shapefile::read_record_header(is)) - break; - - mpt_type & mpt = detail::shapefile::push_back(multi_points, mpt_type()); - - detail::shapefile::read_multipoint(is, mpt); - } + shp::add_records_as_new_elements(is, multi_points); } } }; @@ -487,17 +510,13 @@ struct read_shapefile template static inline void apply(IStream &is, Linestrings & linestrings) { - boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + namespace shp = detail::shapefile; - if (type == detail::shapefile::shape_type::polyline) + boost::int32_t const type = shp::reset_and_read_header(is); + + if (type == shp::shape_type::polyline) { - for (;;) - { - if (! detail::shapefile::read_record_header(is)) - break; - - detail::shapefile::read_polyline(is, linestrings); - } + shp::add_records(is, linestrings); } } }; @@ -508,21 +527,13 @@ struct read_shapefile template static inline void apply(IStream &is, MultiLinestrings & multi_linestrings) { - typedef typename boost::range_value::type mls_type; + namespace shp = detail::shapefile; - boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + boost::int32_t const type = shp::reset_and_read_header(is); - if (type == detail::shapefile::shape_type::polyline) + if (type == shp::shape_type::polyline) { - for (;;) - { - if (! detail::shapefile::read_record_header(is)) - break; - - mls_type & mls = detail::shapefile::push_back(multi_linestrings, mls_type()); - - detail::shapefile::read_polyline(is, mls); - } + shp::add_records_as_new_elements(is, multi_linestrings); } } }; @@ -533,17 +544,13 @@ struct read_shapefile template static inline void apply(IStream &is, Polygons & polygons) { - boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + namespace shp = detail::shapefile; - if (type == detail::shapefile::shape_type::polygon) + boost::int32_t const type = shp::reset_and_read_header(is); + + if (type == shp::shape_type::polygon) { - for (;;) - { - if (! detail::shapefile::read_record_header(is)) - break; - - detail::shapefile::read_polygon(is, polygons); - } + shp::add_records(is, polygons); } } }; @@ -554,21 +561,13 @@ struct read_shapefile template static inline void apply(IStream &is, MultiPolygons & multi_polygons) { - typedef typename boost::range_value::type mpoly_type; + namespace shp = detail::shapefile; - boost::int32_t const type = detail::shapefile::reset_and_read_header(is); + boost::int32_t const type = shp::reset_and_read_header(is); - if (type == detail::shapefile::shape_type::polygon) + if (type == shp::shape_type::polygon) { - for (;;) - { - if (! detail::shapefile::read_record_header(is)) - break; - - mpoly_type & mpoly = detail::shapefile::push_back(multi_polygons, mpoly_type()); - - detail::shapefile::read_polygon(is, mpoly); - } + shp::add_records_as_new_elements(is, multi_polygons); } } }; @@ -577,6 +576,7 @@ struct read_shapefile } // namespace dispatch +// Note: if an exception is thrown the output range may contain partial data template inline void read_shapefile(IStream &is, RangeOfGeometries & range_of_geometries) { From 85fee8b95f49f134f6cb939d137d91e0cc49d108 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 1 Mar 2019 21:27:34 +0100 Subject: [PATCH 03/49] [point_order][strategies] Add calculate_point_order() algorithm with stratgies. --- .../detail/calculate_point_order.hpp | 380 ++++++++++++++++++ .../strategies/cartesian/point_order.hpp | 48 +++ .../strategies/geographic/point_order.hpp | 120 ++++++ .../boost/geometry/strategies/point_order.hpp | 45 +++ .../strategies/spherical/point_order.hpp | 54 +++ 5 files changed, 647 insertions(+) create mode 100644 include/boost/geometry/algorithms/detail/calculate_point_order.hpp create mode 100644 include/boost/geometry/strategies/cartesian/point_order.hpp create mode 100644 include/boost/geometry/strategies/geographic/point_order.hpp create mode 100644 include/boost/geometry/strategies/point_order.hpp create mode 100644 include/boost/geometry/strategies/spherical/point_order.hpp diff --git a/include/boost/geometry/algorithms/detail/calculate_point_order.hpp b/include/boost/geometry/algorithms/detail/calculate_point_order.hpp new file mode 100644 index 000000000..e0d489ce6 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/calculate_point_order.hpp @@ -0,0 +1,380 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP + + +#include + +#include + +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace order +{ + + + +}} // namespace strategy::order + +namespace detail +{ + +template +struct clean_point +{ + explicit clean_point(Iter const& iter) + : m_iter(iter), m_azi(0), m_razi(0), m_azi_diff(0) + , m_is_azi_valid(false), m_is_azi_diff_valid(false) + {} + + clean_point(Iter const& iter, CalcT const& azi, CalcT const& azi_diff) + : m_iter(iter), m_azi(azi), m_razi(0), m_azi_diff(azi_diff) + , m_is_azi_valid(true), m_is_azi_diff_valid(false) + {} + + typename boost::iterators::iterator_reference::type ref() const + { + return *m_iter; + } + + CalcT const& azimuth() const + { + return m_azi; + } + + CalcT const& reverse_azimuth() const + { + return m_razi; + } + + CalcT const& azimuth_difference() const + { + return m_azi_diff; + } + + void set_azimuths(CalcT const& azi, CalcT const& razi) + { + m_azi = azi; + m_razi = razi; + m_is_azi_valid = true; + } + + void set_azimuth_invalid() + { + m_is_azi_valid = false; + } + + bool is_azimuth_valid() const + { + return m_is_azi_valid; + } + + void set_azimuth_difference(CalcT const& diff) + { + m_azi_diff = diff; + m_is_azi_diff_valid = true; + } + + void set_azimuth_difference_invalid() + { + m_is_azi_diff_valid = false; + } + + bool is_azimuth_difference_valid() const + { + return m_is_azi_diff_valid; + } + +private: + Iter m_iter; + CalcT m_azi; + CalcT m_razi; + CalcT m_azi_diff; + bool m_is_azi_valid; + bool m_is_azi_diff_valid; +}; + +struct calculate_point_order_by_azimuth +{ + template + static geometry::order_selector apply(Ring const& ring, Strategy const& strategy) + { + typedef typename boost::range_iterator::type iter_t; + typedef typename Strategy::template result_type::type calc_t; + typedef clean_point clean_point_t; + typedef std::vector cleaned_container_t; + typedef typename cleaned_container_t::iterator cleaned_iter_t; + + calc_t const zero = 0; + calc_t const pi = math::pi(); + + std::size_t const count = boost::size(ring); + if (count < 3) + { + return geometry::order_undetermined; + } + + // non-duplicated, non-spike points + cleaned_container_t cleaned; + cleaned.reserve(count); + + for (iter_t it = boost::begin(ring); it != boost::end(ring); ++it) + { + // Add point + cleaned.push_back(clean_point_t(it)); + + while (cleaned.size() >= 3) + { + cleaned_iter_t it0 = cleaned.end() - 3; + cleaned_iter_t it1 = cleaned.end() - 2; + cleaned_iter_t it2 = cleaned.end() - 1; + + calc_t diff; + if (get_or_calculate_azimuths_difference(*it0, *it1, *it2, diff, strategy) + && ! math::equals(math::abs(diff), pi)) + { + // neither duplicate nor a spike - difference already stored + break; + } + else + { + // spike detected + // TODO: angles have to be invalidated only if spike is detected + // for duplicates it'd be ok to leave them + it0->set_azimuth_invalid(); + it0->set_azimuth_difference_invalid(); + it2->set_azimuth_difference_invalid(); + cleaned.erase(it1); + } + } + } + + // filter-out duplicates and spikes at the front and back of cleaned + cleaned_iter_t cleaned_b = cleaned.begin(); + cleaned_iter_t cleaned_e = cleaned.end(); + bool found = false; + do + { + found = false; + std::ptrdiff_t cleaned_count = std::distance(cleaned_b, cleaned_e); + + while(cleaned_count >= 3) + { + cleaned_iter_t it0 = cleaned_e - 2; + cleaned_iter_t it1 = cleaned_e - 1; + cleaned_iter_t it2 = cleaned_b; + cleaned_iter_t it3 = cleaned_b + 1; + + calc_t diff = 0; + if (! get_or_calculate_azimuths_difference(*it0, *it1, *it2, diff, strategy) + || math::equals(math::abs(diff), pi)) + { + // spike at the back + // TODO: angles have to be invalidated only if spike is detected + // for duplicates it'd be ok to leave them + it0->set_azimuth_invalid(); + it0->set_azimuth_difference_invalid(); + it2->set_azimuth_difference_invalid(); + --cleaned_e; + found = true; + } + else if (! get_or_calculate_azimuths_difference(*it1, *it2, *it3, diff, strategy) + || math::equals(math::abs(diff), pi)) + { + // spike at the front + // TODO: angles have to be invalidated only if spike is detected + // for duplicates it'd be ok to leave them + it1->set_azimuth_invalid(); + it1->set_azimuth_difference_invalid(); + it3->set_azimuth_difference_invalid(); + ++cleaned_b; + found = true; + } + else + { + break; + } + } + } + while (found); + + std::ptrdiff_t cleaned_count = std::distance(cleaned_b, cleaned_e); + if (cleaned_count < 3) + { + return geometry::order_undetermined; + } + + // calculate the sum of external angles + calc_t angles_sum = zero; + for (cleaned_iter_t it = cleaned_b; it != cleaned_e; ++it) + { + cleaned_iter_t it0 = (it == cleaned_b ? cleaned_e - 1 : it - 1); + cleaned_iter_t it2 = (it == cleaned_e - 1 ? cleaned_b : it + 1); + + calc_t diff = 0; + get_or_calculate_azimuths_difference(*it0, *it, *it2, diff, strategy); + + angles_sum += diff; + } + +#ifdef BOOST_GEOMETRY_DEBUG_POINT_ORDER + std::cout << angles_sum << " for " << geometry::wkt(ring) << std::endl; +#endif + + return angles_sum == zero ? geometry::order_undetermined + : angles_sum > zero ? geometry::clockwise + : geometry::counterclockwise; + } + +private: + template + static bool get_or_calculate_azimuths_difference(clean_point & p0, + clean_point & p1, + clean_point const& p2, + T & diff, + Strategy const& strategy) + { + if (p1.is_azimuth_difference_valid()) + { + diff = p1.azimuth_difference(); + return true; + } + + T azi1, razi1, azi2, razi2; + if (get_or_calculate_azimuths(p0, p1, azi1, razi1, strategy) + && get_or_calculate_azimuths(p1, p2, azi2, razi2, strategy)) + { + diff = strategy.apply(p0.ref(), p1.ref(), p2.ref(), razi1, azi2); + p1.set_azimuth_difference(diff); + return true; + } + return false; + } + + template + static bool get_or_calculate_azimuths(clean_point & p0, + clean_point const& p1, + T & azi, T & razi, + Strategy const& strategy) + { + if (p0.is_azimuth_valid()) + { + azi = p0.azimuth(); + razi = p0.reverse_azimuth(); + return true; + } + + if (strategy.apply(p0.ref(), p1.ref(), azi, razi)) + { + p0.set_azimuths(azi, razi); + return true; + } + + return false; + } +}; + +struct calculate_point_order_by_area +{ + template + static geometry::order_selector apply(Ring const& ring, Strategy const& strategy) + { + typedef detail::area::ring_area + < + geometry::order_as_direction::value>::value, + geometry::closure::value + > ring_area_type; + + typedef typename area_result + < + Ring, Strategy + >::type result_type; + + result_type const result = ring_area_type::apply(ring, strategy); + + result_type const zero = 0; + return result == zero ? geometry::order_undetermined + : result > zero ? geometry::clockwise + : geometry::counterclockwise; + } +}; + +} // namespace detail + +namespace dispatch +{ + +template +< + typename Strategy, + typename VersionTag = typename Strategy::version_tag +> +struct calculate_point_order +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_IMPLEMENTED_FOR_THIS_TAG, (types) + ); +}; + +template +struct calculate_point_order + : geometry::detail::calculate_point_order_by_area +{}; + +template +struct calculate_point_order + : geometry::detail::calculate_point_order_by_azimuth +{}; + + +} // namespace dispatch + +namespace detail +{ + +template +inline geometry::order_selector calculate_point_order(Ring const& ring, Strategy const& strategy) +{ + concepts::check(); + + return dispatch::calculate_point_order::apply(ring, strategy); +} + +template +inline geometry::order_selector calculate_point_order(Ring const& ring) +{ + typedef typename strategy::point_order::services::default_strategy + < + typename geometry::cs_tag::type + >::type strategy_type; + + concepts::check(); + + return dispatch::calculate_point_order::apply(ring, strategy_type()); +} + + +} // namespace detail + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP diff --git a/include/boost/geometry/strategies/cartesian/point_order.hpp b/include/boost/geometry/strategies/cartesian/point_order.hpp new file mode 100644 index 000000000..02c024be0 --- /dev/null +++ b/include/boost/geometry/strategies/cartesian/point_order.hpp @@ -0,0 +1,48 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_POINT_ORDER_HPP +#define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_POINT_ORDER_HPP + + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace point_order +{ + +template +struct cartesian + : strategy::area::cartesian +{ + typedef area_tag version_tag; +}; + +namespace services +{ + +template <> +struct default_strategy +{ + typedef cartesian<> type; +}; + +} // namespace services + +}} // namespace strategy::point_order + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_POINT_ORDER_HPP diff --git a/include/boost/geometry/strategies/geographic/point_order.hpp b/include/boost/geometry/strategies/geographic/point_order.hpp new file mode 100644 index 000000000..c06676172 --- /dev/null +++ b/include/boost/geometry/strategies/geographic/point_order.hpp @@ -0,0 +1,120 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_POINT_ORDER_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_POINT_ORDER_HPP + + +#include + +#include + +#include +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace point_order +{ + +template + < + typename FormulaPolicy = strategy::andoyer, + typename Spheroid = srs::spheroid, + typename CalculationType = void + > +struct geographic +{ + typedef azimuth_tag version_tag; + + template + struct result_type + { + typedef typename geometry::select_calculation_type_alt + < + CalculationType, Geometry + >::type type; + }; + + geographic() + {} + + explicit geographic(Spheroid const& spheroid) + : m_spheroid(spheroid) + {} + + template + inline bool apply(Point const& p1, Point const& p2, + typename result_type::type & azi, + typename result_type::type & razi) const + { + typedef typename result_type::type calc_t; + + if (equals_point_point(p1, p2)) + { + return false; + } + + formula::result_inverse res = FormulaPolicy::template inverse + < + calc_t, false, true, true, false, false + >::apply(geometry::get_as_radian<0>(p1), + geometry::get_as_radian<1>(p1), + geometry::get_as_radian<0>(p2), + geometry::get_as_radian<1>(p2), + m_spheroid); + + azi = res.azimuth; + razi = res.reverse_azimuth; + + return true; + } + + template + inline typename result_type::type + apply(Point const& /*p0*/, Point const& /*p1*/, Point const& /*p2*/, + typename result_type::type const& azi1, + typename result_type::type const& azi2) const + { + // TODO: support poles + return math::longitude_distance_signed(azi1, azi2); + } + +private: + template + static bool equals_point_point(Point const& p0, Point const& p1) + { + return strategy::within::spherical_point_point::apply(p0, p1); + } + + Spheroid m_spheroid; +}; + +namespace services +{ + +template <> +struct default_strategy +{ + typedef geographic<> type; +}; + +} // namespace services + +}} // namespace strategy::point_order + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_POINT_ORDER_HPP diff --git a/include/boost/geometry/strategies/point_order.hpp b/include/boost/geometry/strategies/point_order.hpp new file mode 100644 index 000000000..815c2185b --- /dev/null +++ b/include/boost/geometry/strategies/point_order.hpp @@ -0,0 +1,45 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_STRATEGIES_POINT_ORDER_HPP +#define BOOST_GEOMETRY_STRATEGIES_POINT_ORDER_HPP + + +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace point_order +{ + +struct area_tag {}; +struct azimuth_tag {}; + +namespace services +{ + +template +struct default_strategy +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_IMPLEMENTED_FOR_THIS_CS + , (types) + ); +}; + +} // namespace services + +}} // namespace strategy::point_order + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_STRATEGIES_POINT_ORDER_HPP diff --git a/include/boost/geometry/strategies/spherical/point_order.hpp b/include/boost/geometry/strategies/spherical/point_order.hpp new file mode 100644 index 000000000..2387c1ee7 --- /dev/null +++ b/include/boost/geometry/strategies/spherical/point_order.hpp @@ -0,0 +1,54 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_POINT_ORDER_HPP +#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_POINT_ORDER_HPP + + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace point_order +{ + +template +struct spherical + : strategy::area::spherical +{ + typedef area_tag version_tag; +}; + +namespace services +{ + +template <> +struct default_strategy +{ + typedef spherical<> type; +}; + +template <> +struct default_strategy +{ + typedef spherical<> type; +}; + +} // namespace services + +}} // namespace strategy::point_order + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_POINT_ORDER_HPP From 8f2f5932a1046f9ccd0bd295a46cdd65d2aa5472 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 1 Mar 2019 21:28:04 +0100 Subject: [PATCH 04/49] [test][point_order] Add tests for calculate_point_order() algorithm. --- test/algorithms/detail/Jamfile.v2 | 9 +- .../detail/calculate_point_order.cpp | 148 ++++++++++++++++++ 2 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 test/algorithms/detail/calculate_point_order.cpp diff --git a/test/algorithms/detail/Jamfile.v2 b/test/algorithms/detail/Jamfile.v2 index 6509c9a5c..ccddd913b 100644 --- a/test/algorithms/detail/Jamfile.v2 +++ b/test/algorithms/detail/Jamfile.v2 @@ -4,8 +4,8 @@ # Copyright (c) 2008-2015 Bruno Lalande, Paris, France. # Copyright (c) 2009-2015 Mateusz Loskot, London, UK. # -# This file was modified by Oracle on 2015. -# Modifications copyright (c) 2015 Oracle and/or its affiliates. +# This file was modified by Oracle on 2015, 2019. +# Modifications copyright (c) 2015, 2019 Oracle and/or its affiliates. # # Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle # @@ -15,8 +15,9 @@ test-suite boost-geometry-algorithms-detail : - [ run as_range.cpp : : : : algorithms_as_range ] - [ run partition.cpp : : : : algorithms_partition ] + [ run as_range.cpp : : : : algorithms_as_range ] + [ run calculate_point_order.cpp : : : : algorithms_calculate_point_order ] + [ run partition.cpp : : : : algorithms_partition ] ; build-project sections ; diff --git a/test/algorithms/detail/calculate_point_order.cpp b/test/algorithms/detail/calculate_point_order.cpp new file mode 100644 index 000000000..45ae3380b --- /dev/null +++ b/test/algorithms/detail/calculate_point_order.cpp @@ -0,0 +1,148 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + + +#include + +#include + +#include +#include + +#include + +#include +#include +#include + + +inline const char * order_str(bg::order_selector o) +{ + if (o == bg::clockwise) + return "clockwise"; + else if (o == bg::counterclockwise) + return "counterclockwise"; + else + return "order_undetermined"; +} + +inline const char * cs_str(bg::cartesian_tag) +{ + return "cartesian"; +} + +inline const char * cs_str(bg::spherical_equatorial_tag) +{ + return "spherical_equatorial"; +} + +inline const char * cs_str(bg::geographic_tag) +{ + return "geographic"; +} + +template +inline void test_one(Ring const& ring, bg::order_selector expected) +{ + typedef typename bg::cs_tag::type cs_tag; + + bg::order_selector result = bg::detail::calculate_point_order(ring); + + BOOST_CHECK_MESSAGE(result == expected, "Expected: " << order_str(expected) << " for " << bg::wkt(ring) << " in " << cs_str(cs_tag())); + + if (expected != bg::order_undetermined && result != bg::order_undetermined) + { + Ring ring_rev = ring; + + std::reverse(boost::begin(ring_rev), boost::end(ring_rev)); + + bg::order_selector result_rev = bg::detail::calculate_point_order(ring_rev); + + BOOST_CHECK_MESSAGE(result != result_rev, "Invalid order of reversed: " << bg::wkt(ring) << " in " << cs_str(cs_tag())); + } +} + +template +inline void test_one(std::string const& ring_wkt, bg::order_selector expected) +{ + //typedef typename bg::cs_tag

::type cs_tag; + + bg::model::ring

ring; + bg::read_wkt(ring_wkt, ring); + + std::size_t n = boost::size(ring); + for (size_t i = 1; i < n; ++i) + { + test_one(ring, expected); + std::rotate(boost::begin(ring), boost::begin(ring) + 1, boost::end(ring)); + + // it seems that area method doesn't work for invalid "opened" polygons + //if (! boost::is_same::value) + { + P p = bg::range::front(ring); + bg::range::push_back(ring, p); + } + } +} + +template +void test_all() +{ + // From correct() test, rings rotated and reversed in test_one() + test_one

("POLYGON((0 0,0 1,1 1,1 0,0 0))", bg::clockwise); + test_one

("POLYGON((0 0,0 1,1 1,1 0))", bg::clockwise); + test_one

("POLYGON((0 0,0 4,4 4,4 0,0 0))", bg::clockwise); + test_one

("POLYGON((1 1,2 1,2 2,1 2,1 1))", bg::counterclockwise); + + test_one

("POLYGON((0 5, 5 5, 5 0, 0 0, 0 5))", bg::clockwise); + test_one

("POLYGON((0 5, 0 5, 0 6, 0 6, 0 4, 0 5, 5 5, 5 0, 0 0, 0 6, 0 5))", bg::clockwise); + test_one

("POLYGON((2 0, 2 1, 2 -1, 2 0, 1 0, 1 -1, 0 -1, 0 1, 1 1, 1 0, 2 0))", bg::clockwise); + test_one

("POLYGON((2 0, 2 1, 2 -1, 2 0, 1 0, 1 -1, 0 -1, 0 1, 1 1, 1 0))", bg::clockwise); + test_one

("POLYGON((2 0, 2 1, 3 1, 3 -1, 2 -1, 2 0, 1 0, 1 -1, 0 -1, 0 1, 1 1, 1 0, 2 0))", bg::clockwise); + test_one

("POLYGON((0 85, 5 85, 5 84, 0 84, 0 85))", bg::clockwise); + test_one

("POLYGON((0 2, 170 2, 170 0, 0 0, 0 2))", bg::clockwise); + test_one

("POLYGON((0 2, 170 2, 170 1, 160 1, 10 1, 0 1, 0 2))", bg::clockwise); + test_one

("POLYGON((0 2, 170 2, 170 -2, 0 -2, 0 2))", bg::clockwise); + test_one

("POLYGON((5 5, 6 5, 6 6, 6 4, 6 5, 5 5, 5 4, 4 4, 4 6, 5 6, 5 5))", bg::clockwise); + test_one

("POLYGON((5 5, 6 5, 6 6, 6 4, 6 5, 5 5, 5 6, 4 6, 4 4, 5 4, 5 5))", bg::counterclockwise); + test_one

("POLYGON((5 5, 6 5, 6 5, 6 6, 6 5, 6 4, 6 5, 6 5, 5 5, 5 4, 4 4, 4 6, 5 6, 5 5))", bg::clockwise); + + // https://github.com/boostorg/geometry/pull/554 + test_one

("POLYGON((9.8591674311151110 54.602813224425063, 9.8591651519791412 54.602359676428932, 9.8584586199249316 54.602359676428932, 9.8591674311151110 54.602813224425063))", + bg::clockwise); +} + +template +void test_cartesian() +{ + test_one

("POLYGON((0 5, 1 5, 1 6, 1 4, 2 4, 0 4, 0 3, 0 5))", bg::clockwise); +} + +template +void test_spheroidal() +{ + test_one

("POLYGON((0 5, 1 5, 1 6, 1 4, 0 4, 0 3, 0 5))", bg::clockwise); + + test_one

("POLYGON((0 45, 120 45, -120 45, 0 45))", bg::counterclockwise); +} + +int test_main(int, char* []) +{ + test_all >(); + test_all > >(); + test_all > >(); + + test_cartesian >(); + + test_spheroidal > >(); + test_spheroidal > >(); + + return 0; +} From 3b5b66705c613ee96c0b91c311582557ae760cc6 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 4 Mar 2019 17:27:54 +0100 Subject: [PATCH 05/49] [point_order] Update size of range when spikes are removed from the beginning/end of the range. --- .../geometry/algorithms/detail/calculate_point_order.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/calculate_point_order.hpp b/include/boost/geometry/algorithms/detail/calculate_point_order.hpp index e0d489ce6..7ee53f215 100644 --- a/include/boost/geometry/algorithms/detail/calculate_point_order.hpp +++ b/include/boost/geometry/algorithms/detail/calculate_point_order.hpp @@ -169,12 +169,11 @@ struct calculate_point_order_by_azimuth // filter-out duplicates and spikes at the front and back of cleaned cleaned_iter_t cleaned_b = cleaned.begin(); cleaned_iter_t cleaned_e = cleaned.end(); + std::size_t cleaned_count = cleaned.size(); bool found = false; do { found = false; - std::ptrdiff_t cleaned_count = std::distance(cleaned_b, cleaned_e); - while(cleaned_count >= 3) { cleaned_iter_t it0 = cleaned_e - 2; @@ -193,6 +192,7 @@ struct calculate_point_order_by_azimuth it0->set_azimuth_difference_invalid(); it2->set_azimuth_difference_invalid(); --cleaned_e; + --cleaned_count; found = true; } else if (! get_or_calculate_azimuths_difference(*it1, *it2, *it3, diff, strategy) @@ -205,6 +205,7 @@ struct calculate_point_order_by_azimuth it1->set_azimuth_difference_invalid(); it3->set_azimuth_difference_invalid(); ++cleaned_b; + --cleaned_count; found = true; } else @@ -215,7 +216,6 @@ struct calculate_point_order_by_azimuth } while (found); - std::ptrdiff_t cleaned_count = std::distance(cleaned_b, cleaned_e); if (cleaned_count < 3) { return geometry::order_undetermined; From 53c5ffdd171573cbaff72466bd29f65e69031af2 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 4 Mar 2019 17:29:40 +0100 Subject: [PATCH 06/49] [remove_spikes] Replace deque with vector. Instead of modifying the container use iterators to track front and back. --- .../geometry/algorithms/remove_spikes.hpp | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/include/boost/geometry/algorithms/remove_spikes.hpp b/include/boost/geometry/algorithms/remove_spikes.hpp index ffc7f7cd9..307bd2df0 100644 --- a/include/boost/geometry/algorithms/remove_spikes.hpp +++ b/include/boost/geometry/algorithms/remove_spikes.hpp @@ -84,7 +84,9 @@ struct range_remove_spikes return; } - std::deque cleaned; + std::vector cleaned; + cleaned.reserve(n); + for (typename boost::range_iterator::type it = boost::begin(range); it != boost::end(range); ++it) { @@ -102,10 +104,16 @@ struct range_remove_spikes } } + typedef typename std::vector::iterator cleaned_iterator; + cleaned_iterator cleaned_b = cleaned.begin(); + cleaned_iterator cleaned_e = cleaned.end(); + std::size_t cleaned_count = cleaned.size(); + // For a closed-polygon, remove closing point, this makes checking first point(s) easier and consistent if ( BOOST_GEOMETRY_CONDITION(geometry::closure::value == geometry::closed) ) { - cleaned.pop_back(); + --cleaned_e; + --cleaned_count; } bool found = false; @@ -113,45 +121,50 @@ struct range_remove_spikes { found = false; // Check for spike in first point - int const penultimate = 2; - while(cleaned.size() >= 3 - && detail::is_spike_or_equal(range::at(cleaned, cleaned.size() - penultimate), - range::back(cleaned), - range::front(cleaned), + while(cleaned_count >= 3 + && detail::is_spike_or_equal(*(cleaned_e - 2), // prev + *(cleaned_e - 1), // back + *(cleaned_b), // front strategy)) { - cleaned.pop_back(); + --cleaned_e; + --cleaned_count; found = true; } // Check for spike in second point - while(cleaned.size() >= 3 - && detail::is_spike_or_equal(range::back(cleaned), - range::front(cleaned), - range::at(cleaned, 1), + while(cleaned_count >= 3 + && detail::is_spike_or_equal(*(cleaned_e - 1), // back + *(cleaned_b), // front + *(cleaned_b + 1), // next strategy)) { - cleaned.pop_front(); + ++cleaned_b; + --cleaned_count; found = true; } } while (found); - if (cleaned.size() == 2) + if (cleaned_count == 2) { // Ticket #9871: open polygon with only two points. // the second point forms, by definition, a spike - cleaned.pop_back(); + --cleaned_e; + //--cleaned_count; } // Close if necessary if ( BOOST_GEOMETRY_CONDITION(geometry::closure::value == geometry::closed) ) { - cleaned.push_back(cleaned.front()); + BOOST_GEOMETRY_ASSERT(cleaned_e != cleaned.end()); + *cleaned_e = *cleaned_b; + ++cleaned_e; + //++cleaned_count; } // Copy output geometry::clear(range); - std::copy(cleaned.begin(), cleaned.end(), range::back_inserter(range)); + std::copy(cleaned_b, cleaned_e, range::back_inserter(range)); } }; From 6de86f8a71c5e7e2c5d4cc1e4e74ec8c7c079e55 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 4 Mar 2019 18:11:22 +0100 Subject: [PATCH 07/49] [point_order] Cleanup, comment. Remove unneeded ctor. Remove empty namespace. --- .../algorithms/detail/calculate_point_order.hpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/calculate_point_order.hpp b/include/boost/geometry/algorithms/detail/calculate_point_order.hpp index 7ee53f215..a3024076e 100644 --- a/include/boost/geometry/algorithms/detail/calculate_point_order.hpp +++ b/include/boost/geometry/algorithms/detail/calculate_point_order.hpp @@ -26,13 +26,6 @@ namespace boost { namespace geometry { -namespace strategy { namespace order -{ - - - -}} // namespace strategy::order - namespace detail { @@ -44,11 +37,6 @@ struct clean_point , m_is_azi_valid(false), m_is_azi_diff_valid(false) {} - clean_point(Iter const& iter, CalcT const& azi, CalcT const& azi_diff) - : m_iter(iter), m_azi(azi), m_razi(0), m_azi_diff(azi_diff) - , m_is_azi_valid(true), m_is_azi_diff_valid(false) - {} - typename boost::iterators::iterator_reference::type ref() const { return *m_iter; @@ -107,6 +95,8 @@ private: CalcT m_azi; CalcT m_razi; CalcT m_azi_diff; + // NOTE: these flags could be removed and replaced with some magic number + // assigned to the above variables, e.g. CalcT(1000). bool m_is_azi_valid; bool m_is_azi_diff_valid; }; From bd2dc54e8c212f00a7c9fbede5563810ea04ef8a Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 12 Feb 2019 21:48:44 +0100 Subject: [PATCH 08/49] [extensions][io] Check rings point-order and assign inner rings. --- .../extensions/gis/io/shapefile/read.hpp | 227 ++++++++++++++---- 1 file changed, 174 insertions(+), 53 deletions(-) diff --git a/include/boost/geometry/extensions/gis/io/shapefile/read.hpp b/include/boost/geometry/extensions/gis/io/shapefile/read.hpp index cf9cfcb01..aa5c1d716 100644 --- a/include/boost/geometry/extensions/gis/io/shapefile/read.hpp +++ b/include/boost/geometry/extensions/gis/io/shapefile/read.hpp @@ -9,6 +9,7 @@ #ifndef BOOST_GEOMETRY_EXTENSIONS_GIS_IO_SHAPEFILE_READ_HPP #define BOOST_GEOMETRY_EXTENSIONS_GIS_IO_SHAPEFILE_READ_HPP + #include #include @@ -16,6 +17,8 @@ #include #include +#include +#include #include #include #include @@ -27,9 +30,16 @@ #include #include +// TEMP - only here for convenience, for now +#include +#include +#include + + namespace boost { namespace geometry { + class read_shapefile_exception : public geometry::exception { public: @@ -145,14 +155,6 @@ struct shape_type }; }; -template -inline typename boost::range_reference::type -push_back(Range & rng, typename boost::range_value::type const& v) -{ - range::push_back(rng, v); - return range::back(rng); -} - template inline void read_and_push_back_point(IStream & is, Range & rng) { @@ -197,8 +199,8 @@ inline void read_parts(IStream & is, struct read_point_policy { - template - static inline void apply(IStream & is, Points & points) + template + static inline void apply(IStream & is, Points & points, Strategy const&) { boost::int32_t type; read_little(is, type); @@ -218,8 +220,8 @@ struct read_point_policy struct read_multipoint_policy { - template - static inline void apply(IStream & is, Points & points) + template + static inline void apply(IStream & is, Points & points, Strategy const&) { typedef typename boost::range_value::type pt_type; @@ -256,8 +258,8 @@ struct read_multipoint_policy struct read_polyline_policy { - template - static inline void apply(IStream &is, Linestrings & linestrings) + template + static inline void apply(IStream &is, Linestrings & linestrings, Strategy const&) { typedef typename boost::range_value::type ls_type; typedef typename boost::range_value::type pt_type; @@ -300,7 +302,8 @@ struct read_polyline_policy BOOST_THROW_EXCEPTION(read_shapefile_exception("Invalid part number")); } - ls_type & ls = push_back(linestrings, ls_type()); + range::push_back(linestrings, ls_type()); + ls_type & ls = range::back(linestrings); std::size_t ls_size = l - f; @@ -321,8 +324,8 @@ struct read_polyline_policy struct read_polygon_policy { - template - static inline void apply(IStream &is, Polygons & polygons) + template + static inline void apply(IStream &is, Polygons & polygons, Strategy const& strategy) { typedef typename boost::range_value::type poly_type; typedef typename geometry::point_type::type pt_type; @@ -331,6 +334,11 @@ struct read_polygon_policy static const bool is_ccw = geometry::point_order::value == geometry::counterclockwise; static const bool is_open = geometry::closure::value == geometry::open; + typename Strategy::point_order_strategy_type + order_strategy = strategy.get_point_order_strategy(); + typename Strategy::template point_in_geometry_strategy::type + within_strategy = strategy.template get_point_in_geometry_strategy(); + boost::int32_t type; //double min_x, min_y, max_x, max_y; boost::int32_t num_parts; @@ -359,7 +367,8 @@ struct read_polygon_policy read_parts(is, parts, num_parts); - poly_type & poly = push_back(polygons, poly_type()); + std::vector outer_rings; + std::vector inner_rings; for (boost::int32_t i = 0; i < num_parts; ++i) { @@ -371,9 +380,7 @@ struct read_polygon_policy BOOST_THROW_EXCEPTION(read_shapefile_exception("Invalid part number")); } - ring_type & ring = (i == 0) - ? geometry::exterior_ring(poly) - : push_back(geometry::interior_rings(poly), ring_type()); + ring_type ring; std::size_t ring_size = l - f - (is_open ? 1 : 0); @@ -398,26 +405,119 @@ struct read_polygon_policy e = boost::end(ring); std::reverse(++b, is_open ? e : (--e)); } + + // TODO: support rval references in range::push_back() + // and/or implement range::emplace_back() + // implement and call move_or_copy(ring) + + // assume outer ring + if (num_parts == 1) + range::push_back(outer_rings, ring); // order could be checked here too + // check order + else if ( is_outer_ring(ring, order_strategy) ) + range::push_back(outer_rings, ring); + else + range::push_back(inner_rings, ring); } + if (inner_rings.empty()) // no inner rings + { + for (size_t i = 0; i < outer_rings.size(); ++i) + { + poly_type poly; + geometry::exterior_ring(poly) = outer_rings[i]; // TODO: move + range::push_back(polygons, poly); // TODO: move + } + } + else if (! outer_rings.empty()) // outer and inner rings + { + std::vector assigned(inner_rings.size(), false); + std::size_t assigned_count = 0; + for (size_t i = 0; i < outer_rings.size(); ++i) + { + poly_type poly; + geometry::exterior_ring(poly) = outer_rings[i]; // TODO: move + for (size_t j = 0; j < inner_rings.size(); ++j) + { + if (! assigned[j]) + { + if (is_inner_ring(inner_rings[j], + geometry::exterior_ring(poly), + within_strategy)) + { + range::push_back(geometry::interior_rings(poly), inner_rings[j]); // TODO: move + ++assigned_count; + assigned[j] = true; + } + // just in case, ignore empty + else if (boost::empty(inner_rings[j])) + { + ++assigned_count; + assigned[j] = true; + } + } + } + range::push_back(polygons, poly); // TODO: move + } + + // check if all interior rings were assigned + if (assigned_count != inner_rings.size()) + { + BOOST_THROW_EXCEPTION(read_shapefile_exception("Not all interior rings were assigned to polygons.")); + } + } + else // inner rings but no outer rings + { + // unexpected, file corrupted, bug or numerical error + BOOST_THROW_EXCEPTION(read_shapefile_exception("Exterior ring expected")); + } + if (!is.good()) { BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); } } + + template + static inline bool is_outer_ring(Ring const& ring, Strategy const& order_strategy) + { + geometry::order_selector result = detail::calculate_point_order(ring, order_strategy); + return result == geometry::point_order::value + || result == geometry::order_undetermined; + } + + template + static inline bool is_inner_ring(InnerRing const& inner_ring, + OuterRing const& outer_ring, + Strategy const& within_strategy) + { + // NOTE: The worst case complexity is O(N^2) and best O(N) + // R-tree or partition could be used (~O(NlogN)) + // but in most cases this version should be faster. + typedef typename boost::range_iterator::type iter_type; + for (iter_type it = boost::begin(inner_ring); it != boost::end(inner_ring); ++it) + { + if (detail::within::within_point_geometry(*it, outer_ring, within_strategy)) + { + // at least one point of potentially interior ring found in the interior of exterior ring + return true; + } + } + return false; + } }; -template -inline void add_records(IStream & is, Range & rng) +template +inline void add_records(IStream & is, Range & rng, Strategy const& strategy) { while (read_record_header(is)) { - Policy::apply(is, rng); + Policy::apply(is, rng, strategy); } } -template -inline void add_records_as_new_element(IStream & is, Range & rng) +template +inline void add_records_as_new_element(IStream & is, Range & rng, Strategy const& strategy) { typedef typename boost::range_value::type val_type; @@ -426,25 +526,27 @@ inline void add_records_as_new_element(IStream & is, Range & rng) return; } - val_type & elem = shapefile::push_back(rng, val_type()); + range::push_back(rng, val_type()); + val_type & elem = range::back(rng); do { - Policy::apply(is, elem); + Policy::apply(is, elem, strategy); } while (read_record_header(is)); } -template -inline void add_records_as_new_elements(IStream & is, Range & rng) +template +inline void add_records_as_new_elements(IStream & is, Range & rng, Strategy const& strategy) { typedef typename boost::range_value::type val_type; while (read_record_header(is)) { - val_type & elem = shapefile::push_back(rng, val_type()); + range::push_back(rng, val_type()); + val_type & elem = range::back(rng); - Policy::apply(is, elem); + Policy::apply(is, elem, strategy); } } @@ -465,8 +567,8 @@ struct read_shapefile template struct read_shapefile { - template - static inline void apply(IStream &is, Points & points) + template + static inline void apply(IStream &is, Points & points, Strategy const& strategy) { namespace shp = detail::shapefile; @@ -474,11 +576,11 @@ struct read_shapefile if (type == shp::shape_type::point) { - shp::add_records(is, points); + shp::add_records(is, points, strategy); } else if (type == detail::shapefile::shape_type::multipoint) { - shp::add_records(is, points); + shp::add_records(is, points, strategy); } } }; @@ -486,8 +588,8 @@ struct read_shapefile template struct read_shapefile { - template - static inline void apply(IStream &is, MultiPoints & multi_points) + template + static inline void apply(IStream &is, MultiPoints & multi_points, Strategy const& strategy) { namespace shp = detail::shapefile; @@ -495,11 +597,11 @@ struct read_shapefile if (type == shp::shape_type::point) { - shp::add_records_as_new_element(is, multi_points); + shp::add_records_as_new_element(is, multi_points, strategy); } else if (type == detail::shapefile::shape_type::multipoint) { - shp::add_records_as_new_elements(is, multi_points); + shp::add_records_as_new_elements(is, multi_points, strategy); } } }; @@ -507,8 +609,8 @@ struct read_shapefile template struct read_shapefile { - template - static inline void apply(IStream &is, Linestrings & linestrings) + template + static inline void apply(IStream &is, Linestrings & linestrings, Strategy const& strategy) { namespace shp = detail::shapefile; @@ -516,7 +618,7 @@ struct read_shapefile if (type == shp::shape_type::polyline) { - shp::add_records(is, linestrings); + shp::add_records(is, linestrings, strategy); } } }; @@ -524,8 +626,8 @@ struct read_shapefile template struct read_shapefile { - template - static inline void apply(IStream &is, MultiLinestrings & multi_linestrings) + template + static inline void apply(IStream &is, MultiLinestrings & multi_linestrings, Strategy const& strategy) { namespace shp = detail::shapefile; @@ -533,7 +635,7 @@ struct read_shapefile if (type == shp::shape_type::polyline) { - shp::add_records_as_new_elements(is, multi_linestrings); + shp::add_records_as_new_elements(is, multi_linestrings, strategy); } } }; @@ -541,8 +643,8 @@ struct read_shapefile template struct read_shapefile { - template - static inline void apply(IStream &is, Polygons & polygons) + template + static inline void apply(IStream &is, Polygons & polygons, Strategy const& strategy) { namespace shp = detail::shapefile; @@ -550,7 +652,7 @@ struct read_shapefile if (type == shp::shape_type::polygon) { - shp::add_records(is, polygons); + shp::add_records(is, polygons, strategy); } } }; @@ -558,8 +660,8 @@ struct read_shapefile template struct read_shapefile { - template - static inline void apply(IStream &is, MultiPolygons & multi_polygons) + template + static inline void apply(IStream &is, MultiPolygons & multi_polygons, Strategy const& strategy) { namespace shp = detail::shapefile; @@ -567,7 +669,7 @@ struct read_shapefile if (type == shp::shape_type::polygon) { - shp::add_records_as_new_elements(is, multi_polygons); + shp::add_records_as_new_elements(is, multi_polygons, strategy); } } }; @@ -577,12 +679,31 @@ struct read_shapefile // Note: if an exception is thrown the output range may contain partial data +template +inline void read_shapefile(IStream &is, RangeOfGeometries & range_of_geometries, + Strategy const& strategy) +{ + typedef typename boost::range_value::type geometry_type; + + geometry::concepts::check(); + + dispatch::read_shapefile::apply(is, range_of_geometries, + strategy); +} + template inline void read_shapefile(IStream &is, RangeOfGeometries & range_of_geometries) { typedef typename boost::range_value::type geometry_type; + typedef typename strategy::io::services::default_strategy + < + typename cs_tag::type + >::type strategy_type; + geometry::concepts::check(); - dispatch::read_shapefile::apply(is, range_of_geometries); + + dispatch::read_shapefile::apply(is, range_of_geometries, + strategy_type()); } From 592b774bf4d9e354cb8accf6b458a4964f9c2256 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 6 Mar 2019 02:10:26 +0100 Subject: [PATCH 09/49] [strategies][io] Add IO umbrella strategies (returning point-order and within strategies). --- .../geometry/strategies/cartesian/io.hpp | 74 ++++++++++++++ .../geometry/strategies/geographic/io.hpp | 96 +++++++++++++++++++ include/boost/geometry/strategies/io.hpp | 42 ++++++++ .../geometry/strategies/spherical/io.hpp | 80 ++++++++++++++++ 4 files changed, 292 insertions(+) create mode 100644 include/boost/geometry/strategies/cartesian/io.hpp create mode 100644 include/boost/geometry/strategies/geographic/io.hpp create mode 100644 include/boost/geometry/strategies/io.hpp create mode 100644 include/boost/geometry/strategies/spherical/io.hpp diff --git a/include/boost/geometry/strategies/cartesian/io.hpp b/include/boost/geometry/strategies/cartesian/io.hpp new file mode 100644 index 000000000..d8fda2961 --- /dev/null +++ b/include/boost/geometry/strategies/cartesian/io.hpp @@ -0,0 +1,74 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_CARTESIAN_IO_HPP +#define BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_CARTESIAN_IO_HPP + + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace io +{ + +template +struct cartesian +{ + typedef strategy::point_order::cartesian point_order_strategy_type; + + static inline point_order_strategy_type get_point_order_strategy() + { + return point_order_strategy_type(); + } + + template + struct point_in_geometry_strategy + { + typedef strategy::within::cartesian_winding + < + typename point_type::type, + typename point_type::type, + CalculationType + > type; + }; + + template + static inline typename point_in_geometry_strategy::type + get_point_in_geometry_strategy() + { + typedef typename point_in_geometry_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + return strategy_type(); + } +}; + +namespace services +{ + +template <> +struct default_strategy +{ + typedef cartesian<> type; +}; + +} // namespace services + +}} // namespace strategy::io + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_CARTESIAN_IO_HPP diff --git a/include/boost/geometry/strategies/geographic/io.hpp b/include/boost/geometry/strategies/geographic/io.hpp new file mode 100644 index 000000000..c4898ea0c --- /dev/null +++ b/include/boost/geometry/strategies/geographic/io.hpp @@ -0,0 +1,96 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_GEOGRAPHIC_IO_HPP +#define BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_GEOGRAPHIC_IO_HPP + + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace io +{ + +template + < + typename FormulaPolicy = strategy::andoyer, + typename Spheroid = srs::spheroid, + typename CalculationType = void + > +struct geographic +{ + typedef strategy::point_order::geographic + < + FormulaPolicy, + Spheroid, + CalculationType + > point_order_strategy_type; + + point_order_strategy_type get_point_order_strategy() const + { + return point_order_strategy_type(m_spheroid); + } + + template + struct point_in_geometry_strategy + { + typedef strategy::within::geographic_winding + < + typename point_type::type, + typename point_type::type, + FormulaPolicy, + Spheroid, + CalculationType + > type; + }; + + template + typename point_in_geometry_strategy::type + get_point_in_geometry_strategy() const + { + typedef typename point_in_geometry_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + return strategy_type(m_spheroid); + } + + geographic() + {} + + geographic(Spheroid const& spheroid) + : m_spheroid(spheroid) + {} + +private: + Spheroid m_spheroid; +}; + +namespace services +{ + +template <> +struct default_strategy +{ + typedef geographic<> type; +}; + +} // namespace services + +}} // namespace strategy::io + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_GEOGRAPHIC_IO_HPP diff --git a/include/boost/geometry/strategies/io.hpp b/include/boost/geometry/strategies/io.hpp new file mode 100644 index 000000000..5b01e9030 --- /dev/null +++ b/include/boost/geometry/strategies/io.hpp @@ -0,0 +1,42 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_IO_HPP +#define BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_IO_HPP + + +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace io +{ + +namespace services +{ + +template +struct default_strategy +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_IMPLEMENTED_FOR_THIS_CS + , (types) + ); +}; + +} // namespace services + +}} // namespace strategy::io + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_IO_HPP diff --git a/include/boost/geometry/strategies/spherical/io.hpp b/include/boost/geometry/strategies/spherical/io.hpp new file mode 100644 index 000000000..d5ff5c36b --- /dev/null +++ b/include/boost/geometry/strategies/spherical/io.hpp @@ -0,0 +1,80 @@ +// Boost.Geometry + +// Copyright (c) 2019, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_SPHERICAL_IO_HPP +#define BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_SPHERICAL_IO_HPP + + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace io +{ + +template +struct spherical +{ + typedef strategy::point_order::spherical point_order_strategy_type; + + static inline point_order_strategy_type get_point_order_strategy() + { + return point_order_strategy_type(); + } + + template + struct point_in_geometry_strategy + { + typedef strategy::within::spherical_winding + < + typename point_type::type, + typename point_type::type, + CalculationType + > type; + }; + + template + static inline typename point_in_geometry_strategy::type + get_point_in_geometry_strategy() + { + typedef typename point_in_geometry_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + return strategy_type(); + } +}; + +namespace services +{ + +template <> +struct default_strategy +{ + typedef spherical<> type; +}; + +template <> +struct default_strategy +{ + typedef spherical<> type; +}; + +} // namespace services + +}} // namespace strategy::io + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_EXTENSIONS_STRATEGIES_SPHERICAL_IO_HPP From e361c45a35a9491cdbdcd1dbf8022f2d564177db Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 10 Mar 2019 20:07:06 +0100 Subject: [PATCH 10/49] [extensions][io][shapefile] Support M and Z geometries. --- .../extensions/gis/io/shapefile/read.hpp | 333 ++++++++++++++---- 1 file changed, 259 insertions(+), 74 deletions(-) diff --git a/include/boost/geometry/extensions/gis/io/shapefile/read.hpp b/include/boost/geometry/extensions/gis/io/shapefile/read.hpp index aa5c1d716..684b7c1ef 100644 --- a/include/boost/geometry/extensions/gis/io/shapefile/read.hpp +++ b/include/boost/geometry/extensions/gis/io/shapefile/read.hpp @@ -134,7 +134,7 @@ inline bool read_record_header(IStream & is) //read_big(is, number); //read_big(is, length); - is.seekg(sizeof(boost::int32_t), IStream::cur); + is.seekg(sizeof(boost::int32_t), IStream::cur); // only to set flags boost::int32_t foo; read_native(is, foo); @@ -150,23 +150,131 @@ struct shape_type point = 1, polyline = 3, polygon = 5, - multipoint = 8 - // TODO: support the rest + multipoint = 8, + point_z = 11, + polyline_z = 13, + polygon_z = 15, + multipoint_z = 18, + point_m = 21, + polyline_m = 23, + polygon_m = 25, + multipoint_m = 28, + multipatch = 31 }; }; +template +inline void read_m(IStream & is) +{ + is.seekg(sizeof(double), IStream::cur); +} + +template +inline void read_ms(IStream & is, boost::int32_t num_points) +{ + is.seekg(sizeof(double) * num_points, IStream::cur); +} + +template +< + typename Point, + bool Enable = (geometry::dimension::value > 2) +> +struct read_and_set_z +{ + template + static inline void apply(IStream & is, Point & pt) + { + double z; + read_little(is, z); + + geometry::set<2>(pt, v); + } +}; + +template +struct read_and_set_z +{ + template + static inline void apply(IStream & is, Point & ) + { + read_m(is); // eat double + } +}; + +template +inline void read_and_set_back_z(IStream & is, Range & rng) +{ + typedef typename boost::range_value::type pt_type; + pt_type & pt = range::back(rng); + read_and_set_z::apply(is, pt); +} + +template +inline void read_and_set_z_at(IStream & is, Range & rng, std::size_t index) +{ + typedef typename boost::range_value::type pt_type; + pt_type & pt = range::at(rng, index); + read_and_set_z::apply(is, pt); +} + +template +< + typename Point, + bool Enable = (geometry::dimension::value > 2) +> +struct read_and_set_zs_impl +{ + template + static inline void apply(IStream & is, Range & rng, std::size_t num_points, + std::size_t offset) + { + for (std::size_t i = 0; i < num_points; ++i) + { + read_and_set_z_at(is, rng, offset + i); + } + } +}; + +template +struct read_and_set_zs_impl +{ + template + static inline void apply(IStream & is, Range & , std::size_t num_points, + std::size_t ) + { + read_ms(is, num_points); // eat array of doubles + } +}; + +template +inline void read_and_set_zs(IStream & is, Range & rng, std::size_t num_points, + std::size_t offset = 0) +{ + read_and_set_zs_impl + < + typename boost::range_value::type + >::apply(is, rng, num_points, offset); +} + +template +inline void read_and_set_point(IStream & is, Point & pt) +{ + double x, y; + read_little(is, x); + read_little(is, y); + + geometry::set<0>(pt, x); + geometry::set<1>(pt, y); +} + template inline void read_and_push_back_point(IStream & is, Range & rng) { typedef typename boost::range_value::type pt_type; - double x, y; - read_little(is, x); - read_little(is, y); - pt_type pt; - geometry::set<0>(pt, x); - geometry::set<1>(pt, y); + read_and_set_point(is, pt); range::push_back(rng, pt); } @@ -176,13 +284,18 @@ inline void read_and_set_point_at(IStream & is, Range & rng, std::size_t index) { typedef typename boost::range_value::type pt_type; - double x, y; - read_little(is, x); - read_little(is, y); - pt_type & pt = range::at(rng, index); - geometry::set<0>(pt, x); - geometry::set<1>(pt, y); + read_and_set_point(is, pt); +} + +template +inline void read_and_set_points(IStream & is, Range & rng, std::size_t num_points, + std::size_t offset = 0) +{ + for (std::size_t i = 0; i < num_points; ++i) + { + read_and_set_point_at(is, rng, offset + i); + } } template @@ -200,18 +313,29 @@ inline void read_parts(IStream & is, struct read_point_policy { template - static inline void apply(IStream & is, Points & points, Strategy const&) + static inline void apply(IStream & is, Points & points, boost::int32_t type, + Strategy const&) { - boost::int32_t type; - read_little(is, type); - if (type != shape_type::point) + boost::int32_t t; + read_little(is, t); + if (t != type) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Point expected")); + BOOST_THROW_EXCEPTION(read_shapefile_exception("Record type different than file type")); } read_and_push_back_point(is, points); - if (!is.good()) + if (type == shape_type::point_z || type == shape_type::point_m) + { + if (type == shape_type::point_z) + { + read_and_set_back_z(is, points); + } + + read_m(is); + } + + if (! is.good()) { BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); } @@ -221,18 +345,19 @@ struct read_point_policy struct read_multipoint_policy { template - static inline void apply(IStream & is, Points & points, Strategy const&) + static inline void apply(IStream & is, Points & points, boost::int32_t type, + Strategy const&) { typedef typename boost::range_value::type pt_type; - boost::int32_t type; - read_little(is, type); - if (type != shape_type::multipoint) + boost::int32_t t; + read_little(is, t); + if (t != type) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Multipoint expected")); + BOOST_THROW_EXCEPTION(read_shapefile_exception("Record type different than file type")); } - is.seekg(4 * sizeof(double), IStream::cur); + is.seekg(4 * sizeof(double), IStream::cur); // box boost::int32_t num_points; read_little(is, num_points); @@ -243,13 +368,24 @@ struct read_multipoint_policy } std::size_t old_size = boost::size(points); - range::resize(points, old_size + num_points); - for (boost::int32_t i = 0; i < num_points; ++i) + std::size_t count = std::size_t(num_points); + range::resize(points, old_size + count); + + read_and_set_points(is, points, count, old_size); + + if (type == shape_type::multipoint_z || type == shape_type::multipoint_m) { - read_and_set_point_at(is, points, old_size + i); + if (type == shape_type::multipoint_z) + { + is.seekg(2 * sizeof(double), IStream::cur); // box_z + read_and_set_zs(is, points, count, old_size); + } + + is.seekg(2 * sizeof(double), IStream::cur); // box_m + read_ms(is, num_points); } - if (!is.good()) + if (! is.good()) { BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); } @@ -259,21 +395,22 @@ struct read_multipoint_policy struct read_polyline_policy { template - static inline void apply(IStream &is, Linestrings & linestrings, Strategy const&) + static inline void apply(IStream &is, Linestrings & linestrings, boost::int32_t type, + Strategy const&) { typedef typename boost::range_value::type ls_type; typedef typename boost::range_value::type pt_type; - boost::int32_t type; + boost::int32_t t; //double min_x, min_y, max_x, max_y; boost::int32_t num_parts; boost::int32_t num_points; std::vector parts; - read_little(is, type); - if (type != shape_type::polyline) + read_little(is, t); + if (t != type) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Polyline expected")); + BOOST_THROW_EXCEPTION(read_shapefile_exception("Record type different than file type")); } // TODO: support filtering @@ -305,17 +442,25 @@ struct read_polyline_policy range::push_back(linestrings, ls_type()); ls_type & ls = range::back(linestrings); - std::size_t ls_size = l - f; - + std::size_t ls_size = l - f; range::resize(ls, ls_size); - for (std::size_t j = 0; j < ls_size; ++j) + read_and_set_points(is, ls, ls_size); + + if (type == shape_type::polyline_z || type == shape_type::polyline_m) { - read_and_set_point_at(is, ls, j); + if (type == shape_type::polyline_z) + { + is.seekg(2 * sizeof(double), IStream::cur); // box_z + read_and_set_zs(is, ls, ls_size); + } + + is.seekg(2 * sizeof(double), IStream::cur); // box_m + read_ms(is, ls_size); } } - if (!is.good()) + if (! is.good()) { BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); } @@ -325,7 +470,8 @@ struct read_polyline_policy struct read_polygon_policy { template - static inline void apply(IStream &is, Polygons & polygons, Strategy const& strategy) + static inline void apply(IStream &is, Polygons & polygons, boost::int32_t type, + Strategy const& strategy) { typedef typename boost::range_value::type poly_type; typedef typename geometry::point_type::type pt_type; @@ -339,16 +485,16 @@ struct read_polygon_policy typename Strategy::template point_in_geometry_strategy::type within_strategy = strategy.template get_point_in_geometry_strategy(); - boost::int32_t type; + boost::int32_t t; //double min_x, min_y, max_x, max_y; boost::int32_t num_parts; boost::int32_t num_points; std::vector parts; - read_little(is, type); - if (type != shape_type::polygon) + read_little(is, t); + if (t != type) { - BOOST_THROW_EXCEPTION(read_shapefile_exception("Polygon expected")); + BOOST_THROW_EXCEPTION(read_shapefile_exception("Record type different than file type")); } // TODO: support filtering @@ -383,13 +529,9 @@ struct read_polygon_policy ring_type ring; std::size_t ring_size = l - f - (is_open ? 1 : 0); - range::resize(ring, ring_size); - for (std::size_t j = 0; j < ring_size; ++j) - { - read_and_set_point_at(is, ring, j); - } + read_and_set_points(is, ring, ring_size); // if ring is open ignore the last point if (is_open) @@ -397,6 +539,30 @@ struct read_polygon_policy is.seekg(2 * sizeof(double), IStream::cur); } + if (type == shape_type::polyline_z || type == shape_type::polyline_m) + { + if (type == shape_type::polyline_z) + { + is.seekg(2 * sizeof(double), IStream::cur); // box_z + read_and_set_zs(is, ring, ring_size); + + // if ring is open ignore the last z + if (is_open) + { + is.seekg(sizeof(double), IStream::cur); + } + } + + is.seekg(2 * sizeof(double), IStream::cur); // box_m + read_ms(is, ring_size); + + // if ring is open ignore the last m + if (is_open) + { + is.seekg(sizeof(double), IStream::cur); + } + } + // if ring is ccw reverse leaving the first point untouched if (is_ccw) { @@ -472,7 +638,7 @@ struct read_polygon_policy BOOST_THROW_EXCEPTION(read_shapefile_exception("Exterior ring expected")); } - if (!is.good()) + if (! is.good()) { BOOST_THROW_EXCEPTION(read_shapefile_exception("Read error")); } @@ -508,16 +674,18 @@ struct read_polygon_policy }; template -inline void add_records(IStream & is, Range & rng, Strategy const& strategy) +inline void add_records(IStream & is, Range & rng, boost::int32_t type, + Strategy const& strategy) { while (read_record_header(is)) { - Policy::apply(is, rng, strategy); + Policy::apply(is, rng, type, strategy); } } template -inline void add_records_as_new_element(IStream & is, Range & rng, Strategy const& strategy) +inline void add_records_as_new_element(IStream & is, Range & rng, boost::int32_t type, + Strategy const& strategy) { typedef typename boost::range_value::type val_type; @@ -531,13 +699,14 @@ inline void add_records_as_new_element(IStream & is, Range & rng, Strategy const do { - Policy::apply(is, elem, strategy); + Policy::apply(is, elem, type, strategy); } while (read_record_header(is)); } template -inline void add_records_as_new_elements(IStream & is, Range & rng, Strategy const& strategy) +inline void add_records_as_new_elements(IStream & is, Range & rng, boost::int32_t type, + Strategy const& strategy) { typedef typename boost::range_value::type val_type; @@ -546,7 +715,7 @@ inline void add_records_as_new_elements(IStream & is, Range & rng, Strategy cons range::push_back(rng, val_type()); val_type & elem = range::back(rng); - Policy::apply(is, elem, strategy); + Policy::apply(is, elem, type, strategy); } } @@ -574,13 +743,17 @@ struct read_shapefile boost::int32_t const type = shp::reset_and_read_header(is); - if (type == shp::shape_type::point) + if (type == shp::shape_type::point + || type == shp::shape_type::point_m + || type == shp::shape_type::point_z) { - shp::add_records(is, points, strategy); + shp::add_records(is, points, type, strategy); } - else if (type == detail::shapefile::shape_type::multipoint) + else if (type == detail::shapefile::shape_type::multipoint + || type == detail::shapefile::shape_type::multipoint_m + || type == detail::shapefile::shape_type::multipoint_z) { - shp::add_records(is, points, strategy); + shp::add_records(is, points, type, strategy); } } }; @@ -595,13 +768,17 @@ struct read_shapefile boost::int32_t const type = shp::reset_and_read_header(is); - if (type == shp::shape_type::point) + if (type == shp::shape_type::point + || type == shp::shape_type::point_m + || type == shp::shape_type::point_z) { - shp::add_records_as_new_element(is, multi_points, strategy); + shp::add_records_as_new_element(is, multi_points, type, strategy); } - else if (type == detail::shapefile::shape_type::multipoint) + else if (type == detail::shapefile::shape_type::multipoint + || type == detail::shapefile::shape_type::multipoint_m + || type == detail::shapefile::shape_type::multipoint_z) { - shp::add_records_as_new_elements(is, multi_points, strategy); + shp::add_records_as_new_elements(is, multi_points, type, strategy); } } }; @@ -616,9 +793,11 @@ struct read_shapefile boost::int32_t const type = shp::reset_and_read_header(is); - if (type == shp::shape_type::polyline) + if (type == shp::shape_type::polyline + || type == shp::shape_type::polyline_m + || type == shp::shape_type::polyline_z) { - shp::add_records(is, linestrings, strategy); + shp::add_records(is, linestrings, type, strategy); } } }; @@ -633,9 +812,11 @@ struct read_shapefile boost::int32_t const type = shp::reset_and_read_header(is); - if (type == shp::shape_type::polyline) + if (type == shp::shape_type::polyline + || type == shp::shape_type::polyline_m + || type == shp::shape_type::polyline_z) { - shp::add_records_as_new_elements(is, multi_linestrings, strategy); + shp::add_records_as_new_elements(is, multi_linestrings, type, strategy); } } }; @@ -650,9 +831,11 @@ struct read_shapefile boost::int32_t const type = shp::reset_and_read_header(is); - if (type == shp::shape_type::polygon) + if (type == shp::shape_type::polygon + || type == shp::shape_type::polygon_m + || type == shp::shape_type::polygon_z) { - shp::add_records(is, polygons, strategy); + shp::add_records(is, polygons, type, strategy); } } }; @@ -667,9 +850,11 @@ struct read_shapefile boost::int32_t const type = shp::reset_and_read_header(is); - if (type == shp::shape_type::polygon) + if (type == shp::shape_type::polygon + || type == shp::shape_type::polygon_m + || type == shp::shape_type::polygon_z) { - shp::add_records_as_new_elements(is, multi_polygons, strategy); + shp::add_records_as_new_elements(is, multi_polygons, type, strategy); } } }; From be7b5e71ecf233a2de9f248e1cfcb79f6433900b Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 12 Apr 2019 15:42:18 +0200 Subject: [PATCH 11/49] [test] Replace tab with spaces. --- test/algorithms/detail/Jamfile.v2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/algorithms/detail/Jamfile.v2 b/test/algorithms/detail/Jamfile.v2 index ccddd913b..bd3495399 100644 --- a/test/algorithms/detail/Jamfile.v2 +++ b/test/algorithms/detail/Jamfile.v2 @@ -16,7 +16,7 @@ test-suite boost-geometry-algorithms-detail : [ run as_range.cpp : : : : algorithms_as_range ] - [ run calculate_point_order.cpp : : : : algorithms_calculate_point_order ] + [ run calculate_point_order.cpp : : : : algorithms_calculate_point_order ] [ run partition.cpp : : : : algorithms_partition ] ; From cc5e7d5ffec1f4ddb11ccd87c0a79e8297d62895 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 13 Apr 2019 04:06:34 +0200 Subject: [PATCH 12/49] [strategies] Implement spherical point_order strategy based on azimuth. Commented-out for now. --- .../strategies/spherical/point_order.hpp | 92 ++++++++++++++++++- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/strategies/spherical/point_order.hpp b/include/boost/geometry/strategies/spherical/point_order.hpp index 2387c1ee7..ad294aa38 100644 --- a/include/boost/geometry/strategies/spherical/point_order.hpp +++ b/include/boost/geometry/strategies/spherical/point_order.hpp @@ -11,10 +11,19 @@ #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_POINT_ORDER_HPP +#include + + #include -#include +#include + #include +#include +#include + +#include +#include namespace boost { namespace geometry @@ -23,6 +32,83 @@ namespace boost { namespace geometry namespace strategy { namespace point_order { +//template +//struct spherical +//{ +// typedef azimuth_tag version_tag; +// +// template +// struct result_type +// { +// typedef typename geometry::select_calculation_type_alt +// < +// CalculationType, Geometry +// >::type type; +// }; +// +// template +// inline bool apply(Point const& p1, Point const& p2, +// typename result_type::type & azi, +// typename result_type::type & razi) const +// { +// typedef typename result_type::type calc_t; +// +// if (equals_point_point(p1, p2)) +// { +// return false; +// } +// +// calc_t lon1 = geometry::get_as_radian<0>(p1); +// calc_t lat1 = geometry::get_as_radian<1>(p1); +// calc_t lon2 = geometry::get_as_radian<0>(p2); +// calc_t lat2 = geometry::get_as_radian<1>(p2); +// +// convert_latitudes(lat1, lat2); +// +// formula::result_spherical +// res = formula::spherical_azimuth(lon1, lat1, lon2, lat2); +// +// azi = res.azimuth; +// razi = res.reverse_azimuth; +// +// return true; +// } +// +// template +// inline typename result_type::type +// apply(Point const& /*p0*/, Point const& /*p1*/, Point const& /*p2*/, +// typename result_type::type const& azi1, +// typename result_type::type const& azi2) const +// { +// // TODO: support poles +// return math::longitude_distance_signed(azi1, azi2); +// } +// +//private: +// template +// static bool equals_point_point(Point const& p0, Point const& p1) +// { +// return strategy::within::spherical_point_point::apply(p0, p1); +// } +// +// template +// static void convert_latitudes(CalcT & lat1, CalcT & lat2) +// { +// static const bool is_polar = boost::is_same +// < +// typename geometry::cs_tag::type, +// spherical_polar_tag +// >::value; +// +// if (BOOST_GEOMETRY_CONDITION(is_polar)) +// { +// CalcT pi_half = math::half_pi(); +// lat1 = pi_half - lat1; +// lat2 = pi_half - lat2; +// } +// } +//}; + template struct spherical : strategy::area::spherical @@ -39,11 +125,11 @@ struct default_strategy typedef spherical<> type; }; -template <> +/*template <> struct default_strategy { typedef spherical<> type; -}; +};*/ } // namespace services From cfb36adf6ef2900ecbd947958bd8751434abecf4 Mon Sep 17 00:00:00 2001 From: Tinko Bartels Date: Fri, 12 Apr 2019 10:45:36 +0200 Subject: [PATCH 13/49] Matrix transformer support for arbitrary dimensions --- .../transform/matrix_transformers.hpp | 238 ++++++++++++------ test/strategies/Jamfile.v2 | 1 + test/strategies/matrix_transformer.cpp | 103 ++++++++ 3 files changed, 266 insertions(+), 76 deletions(-) create mode 100644 test/strategies/matrix_transformer.cpp diff --git a/include/boost/geometry/strategies/transform/matrix_transformers.hpp b/include/boost/geometry/strategies/transform/matrix_transformers.hpp index a3f3223c9..e2332bc6c 100644 --- a/include/boost/geometry/strategies/transform/matrix_transformers.hpp +++ b/include/boost/geometry/strategies/transform/matrix_transformers.hpp @@ -23,8 +23,11 @@ #include #include +#include #include +#include #include +#include #include #include @@ -41,6 +44,102 @@ namespace boost { namespace geometry namespace strategy { namespace transform { +namespace detail { namespace matrix_transformer +{ + +template +< + typename CalculationType, + typename P2, + std::size_t Dimension2, + std::size_t Dimension2Count +> +struct set_point_from_vec +{ + typedef CalculationType ct; + static inline void apply(P2& p2, qvm::vec& p2temp) + { + typedef typename geometry::coordinate_type::type ct2; + set(p2, boost::numeric_cast(qvm::A(p2temp))); + set_point_from_vec::apply(p2, p2temp); + } +}; + +template +< + typename CalculationType, + typename P2, + std::size_t Dimension2 +> +struct set_point_from_vec +{ + typedef CalculationType ct; + static inline void apply(P2& p2, qvm::vec& p2temp) {} +}; + +template +< + typename CalculationType, + typename P1, + std::size_t Dimension1, + std::size_t Dimension1Count +> +struct set_vec_from_point +{ + typedef CalculationType ct; + static inline void apply(const P1& p1, qvm::vec& p1temp) + { + qvm::A(p1temp) = get(p1); + set_vec_from_point::apply(p1, p1temp); + } +}; + +template +< + typename CalculationType, + typename P1, + std::size_t Dimension1 +> +struct set_vec_from_point +{ + typedef CalculationType ct; + static inline void apply(const P1& p1, qvm::vec& p1temp) {} +}; + + +template +< + typename CalculationType, + std::size_t Dimension1, + std::size_t Dimension2 +> +class matrix_transformer +{ +protected : + typedef CalculationType ct; + typedef boost::qvm::mat matrix_type; + matrix_type m_matrix; +public : + + matrix_type const& matrix() const { return m_matrix; } + template + inline bool apply(P1 const& p1, P2& p2) const + { + assert_dimension(); + assert_dimension(); + qvm::vec p1temp; + qvm::vec p2temp; + set_vec_from_point::apply(p1, p1temp); + qvm::A(p1temp) = 1; + p2temp = m_matrix * p1temp; + set_point_from_vec::apply(p2, p2temp); + return true; + } + +}; + +}} // namespace detail::matrix_transform + /*! \brief Affine transformation strategy in Cartesian system. \details The strategy serves as a generic definition of affine transformation matrix @@ -57,93 +156,88 @@ template std::size_t Dimension1, std::size_t Dimension2 > -class matrix_transformer +class matrix_transformer : public detail::matrix_transformer::matrix_transformer { +protected: + typedef CalculationType ct; + typedef boost::qvm::mat matrix_type; +public: + + template + inline matrix_transformer(Matrix const& matrix) + { + qvm::assign(this->m_matrix, matrix); + } + inline matrix_transformer() {} }; template -class matrix_transformer +class matrix_transformer : public detail::matrix_transformer::matrix_transformer { protected : typedef CalculationType ct; typedef boost::qvm::mat matrix_type; - matrix_type m_matrix; - public : + template + inline matrix_transformer(Matrix const& matrix) + { + qvm::assign(this->m_matrix, matrix); + } + inline matrix_transformer() {} inline matrix_transformer( ct const& m_0_0, ct const& m_0_1, ct const& m_0_2, ct const& m_1_0, ct const& m_1_1, ct const& m_1_2, ct const& m_2_0, ct const& m_2_1, ct const& m_2_2) { - qvm::A<0,0>(m_matrix) = m_0_0; qvm::A<0,1>(m_matrix) = m_0_1; qvm::A<0,2>(m_matrix) = m_0_2; - qvm::A<1,0>(m_matrix) = m_1_0; qvm::A<1,1>(m_matrix) = m_1_1; qvm::A<1,2>(m_matrix) = m_1_2; - qvm::A<2,0>(m_matrix) = m_2_0; qvm::A<2,1>(m_matrix) = m_2_1; qvm::A<2,2>(m_matrix) = m_2_2; + qvm::A<0,0>(this->m_matrix) = m_0_0; qvm::A<0,1>(this->m_matrix) = m_0_1; qvm::A<0,2>(this->m_matrix) = m_0_2; + qvm::A<1,0>(this->m_matrix) = m_1_0; qvm::A<1,1>(this->m_matrix) = m_1_1; qvm::A<1,2>(this->m_matrix) = m_1_2; + qvm::A<2,0>(this->m_matrix) = m_2_0; qvm::A<2,1>(this->m_matrix) = m_2_1; qvm::A<2,2>(this->m_matrix) = m_2_2; } - - inline matrix_transformer(matrix_type const& matrix) - : m_matrix(matrix) - {} - - - inline matrix_transformer() {} - - template - inline bool apply(P1 const& p1, P2& p2) const - { - assert_dimension_greater_equal(); - assert_dimension_greater_equal(); - - ct const& c1 = get<0>(p1); - ct const& c2 = get<1>(p1); - - ct p2x = c1 * qvm::A<0,0>(m_matrix) + c2 * qvm::A<0,1>(m_matrix) + qvm::A<0,2>(m_matrix); - ct p2y = c1 * qvm::A<1,0>(m_matrix) + c2 * qvm::A<1,1>(m_matrix) + qvm::A<1,2>(m_matrix); - - typedef typename geometry::coordinate_type::type ct2; - set<0>(p2, boost::numeric_cast(p2x)); - set<1>(p2, boost::numeric_cast(p2y)); - - return true; - } - - matrix_type const& matrix() const { return m_matrix; } }; // It IS possible to go from 3 to 2 coordinates template -class matrix_transformer : public matrix_transformer +class matrix_transformer : public detail::matrix_transformer::matrix_transformer { typedef CalculationType ct; - + typedef boost::qvm::mat matrix_type; public : + + template + inline matrix_transformer(Matrix const& matrix) + { + qvm::assign(this->m_matrix, matrix); + } + inline matrix_transformer() {} inline matrix_transformer( ct const& m_0_0, ct const& m_0_1, ct const& m_0_2, ct const& m_1_0, ct const& m_1_1, ct const& m_1_2, ct const& m_2_0, ct const& m_2_1, ct const& m_2_2) - : matrix_transformer( - m_0_0, m_0_1, m_0_2, - m_1_0, m_1_1, m_1_2, - m_2_0, m_2_1, m_2_2) - {} - - inline matrix_transformer() - : matrix_transformer() - {} + { + qvm::A<0,0>(this->m_matrix) = m_0_0; qvm::A<0,1>(this->m_matrix) = m_0_1; qvm::A<0,2>(this->m_matrix) = 0; qvm::A<0,3>(this->m_matrix) = m_0_2; + qvm::A<1,0>(this->m_matrix) = m_1_0; qvm::A<1,1>(this->m_matrix) = m_1_1; qvm::A<1,2>(this->m_matrix) = 0; qvm::A<1,3>(this->m_matrix) = m_1_2; + qvm::A<2,0>(this->m_matrix) = m_2_0; qvm::A<2,1>(this->m_matrix) = m_2_1; qvm::A<2,2>(this->m_matrix) = 0; qvm::A<2,3>(this->m_matrix) = m_2_2; + } }; template -class matrix_transformer +class matrix_transformer : public detail::matrix_transformer::matrix_transformer { protected : typedef CalculationType ct; typedef boost::qvm::mat matrix_type; - matrix_type m_matrix; - public : + + template + inline matrix_transformer(Matrix const& matrix) + { + qvm::assign(this->m_matrix, matrix); + } + inline matrix_transformer() {} inline matrix_transformer( ct const& m_0_0, ct const& m_0_1, ct const& m_0_2, ct const& m_0_3, ct const& m_1_0, ct const& m_1_1, ct const& m_1_2, ct const& m_1_3, @@ -151,34 +245,11 @@ public : ct const& m_3_0, ct const& m_3_1, ct const& m_3_2, ct const& m_3_3 ) { - qvm::A<0,0>(m_matrix) = m_0_0; qvm::A<0,1>(m_matrix) = m_0_1; qvm::A<0,2>(m_matrix) = m_0_2; qvm::A<0,3>(m_matrix) = m_0_3; - qvm::A<1,0>(m_matrix) = m_1_0; qvm::A<1,1>(m_matrix) = m_1_1; qvm::A<1,2>(m_matrix) = m_1_2; qvm::A<1,3>(m_matrix) = m_1_3; - qvm::A<2,0>(m_matrix) = m_2_0; qvm::A<2,1>(m_matrix) = m_2_1; qvm::A<2,2>(m_matrix) = m_2_2; qvm::A<2,3>(m_matrix) = m_2_3; - qvm::A<3,0>(m_matrix) = m_3_0; qvm::A<3,1>(m_matrix) = m_3_1; qvm::A<3,2>(m_matrix) = m_3_2; qvm::A<3,3>(m_matrix) = m_3_3; + qvm::A<0,0>(this->m_matrix) = m_0_0; qvm::A<0,1>(this->m_matrix) = m_0_1; qvm::A<0,2>(this->m_matrix) = m_0_2; qvm::A<0,3>(this->m_matrix) = m_0_3; + qvm::A<1,0>(this->m_matrix) = m_1_0; qvm::A<1,1>(this->m_matrix) = m_1_1; qvm::A<1,2>(this->m_matrix) = m_1_2; qvm::A<1,3>(this->m_matrix) = m_1_3; + qvm::A<2,0>(this->m_matrix) = m_2_0; qvm::A<2,1>(this->m_matrix) = m_2_1; qvm::A<2,2>(this->m_matrix) = m_2_2; qvm::A<2,3>(this->m_matrix) = m_2_3; + qvm::A<3,0>(this->m_matrix) = m_3_0; qvm::A<3,1>(this->m_matrix) = m_3_1; qvm::A<3,2>(this->m_matrix) = m_3_2; qvm::A<3,3>(this->m_matrix) = m_3_3; } - - inline matrix_transformer() {} - - template - inline bool apply(P1 const& p1, P2& p2) const - { - ct const& c1 = get<0>(p1); - ct const& c2 = get<1>(p1); - ct const& c3 = get<2>(p1); - - typedef typename geometry::coordinate_type::type ct2; - - set<0>(p2, boost::numeric_cast( - c1 * qvm::A<0,0>(m_matrix) + c2 * qvm::A<0,1>(m_matrix) + c3 * qvm::A<0,2>(m_matrix) + qvm::A<0,3>(m_matrix))); - set<1>(p2, boost::numeric_cast( - c1 * qvm::A<1,0>(m_matrix) + c2 * qvm::A<1,1>(m_matrix) + c3 * qvm::A<1,2>(m_matrix) + qvm::A<1,3>(m_matrix))); - set<2>(p2, boost::numeric_cast( - c1 * qvm::A<2,0>(m_matrix) + c2 * qvm::A<2,1>(m_matrix) + c3 * qvm::A<2,2>(m_matrix) + qvm::A<2,3>(m_matrix))); - - return true; - } - - matrix_type const& matrix() const { return m_matrix; } }; @@ -253,6 +324,21 @@ class scale_transformer { }; +template +< + typename CalculationType, + std::size_t Dimension1 +> +class scale_transformer : public matrix_transformer +{ +public: + inline scale_transformer(CalculationType const& scale) + { + boost::qvm::set_identity(this->m_matrix); + this->m_matrix*=scale; + qvm::A(this->m_matrix) = 1; + } +}; template class scale_transformer : public matrix_transformer diff --git a/test/strategies/Jamfile.v2 b/test/strategies/Jamfile.v2 index 10d281b38..6dedcfa2c 100644 --- a/test/strategies/Jamfile.v2 +++ b/test/strategies/Jamfile.v2 @@ -38,6 +38,7 @@ test-suite boost-geometry-strategies [ run thomas.cpp : : : : strategies_thomas ] [ run transform_cs.cpp : : : : strategies_transform_cs ] [ run transformer.cpp : : : : strategies_transformer ] + [ run matrix_transformer.cpp : : : : matrix_strategies_transformer ] [ run vincenty.cpp : : : : strategies_vincenty ] [ run winding.cpp : : : : strategies_winding ] ; diff --git a/test/strategies/matrix_transformer.cpp b/test/strategies/matrix_transformer.cpp new file mode 100644 index 000000000..b617b5c20 --- /dev/null +++ b/test/strategies/matrix_transformer.cpp @@ -0,0 +1,103 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2019 Tinko Bartels + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + + +#include + +#include +#include +#include + +template +void test_all() +{ + typedef bg::model::point point_2d; + typedef bg::model::point point_3d; + typedef bg::model::point point_4d; + + point_2d p2d; + point_3d p3d; + point_4d p4d; + + bg::assign_values(p2d, 3, 5); + + boost::qvm::mat mat24; + boost::qvm::A<0, 0>(mat24) = 1; boost::qvm::A<0, 1>(mat24) = 0; boost::qvm::A<0, 2>(mat24) = 0; + boost::qvm::A<1, 0>(mat24) = 0; boost::qvm::A<1, 1>(mat24) = 1; boost::qvm::A<1, 2>(mat24) = 0; + boost::qvm::A<2, 0>(mat24) = 1; boost::qvm::A<2, 1>(mat24) = -1; boost::qvm::A<2, 2>(mat24) = 0; + boost::qvm::A<3, 0>(mat24) = -1; boost::qvm::A<3, 1>(mat24) = 1; boost::qvm::A<3, 2>(mat24) = 0; + boost::qvm::A<4, 0>(mat24) = 0; boost::qvm::A<4, 1>(mat24) = 0; boost::qvm::A<4, 2>(mat24) = 1; + bg::strategy::transform::matrix_transformer trans24(mat24); + bg::transform(p2d, p4d, trans24); + + BOOST_CHECK_CLOSE(double(bg::get<0>(p4d)), 3.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<1>(p4d)), 5.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<2>(p4d)), -2.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<3>(p4d)), 2.0, 0.001); + + bg::strategy::transform::scale_transformer scale44(2); + bg::transform(p4d, p4d, scale44); + + BOOST_CHECK_CLOSE(double(bg::get<0>(p4d)), 6.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<1>(p4d)), 10.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<2>(p4d)), -4.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<3>(p4d)), 4.0, 0.001); + + boost::qvm::mat mat43; + boost::qvm::A<0, 0>(mat43) = 0 ; boost::qvm::A<0, 1>(mat43) = 0; boost::qvm::A<0, 2>(mat43) = 0.5; boost::qvm::A<0, 3>(mat43) = 0 ; boost::qvm::A<0, 4>(mat43) = 0; + boost::qvm::A<1, 0>(mat43) = 0.5; boost::qvm::A<1, 1>(mat43) = 0; boost::qvm::A<1, 2>(mat43) = 0 ; boost::qvm::A<1, 3>(mat43) = 0 ; boost::qvm::A<1, 4>(mat43) = 0; + boost::qvm::A<2, 0>(mat43) = 0 ; boost::qvm::A<2, 1>(mat43) = 0; boost::qvm::A<2, 2>(mat43) = 0 ; boost::qvm::A<2, 3>(mat43) = 0.5; boost::qvm::A<2, 4>(mat43) = 0; + boost::qvm::A<3, 0>(mat43) = 0 ; boost::qvm::A<3, 1>(mat43) = 0; boost::qvm::A<3, 2>(mat43) = 0 ; boost::qvm::A<3, 3>(mat43) = 0 ; boost::qvm::A<3, 4>(mat43) = 1; + bg::strategy::transform::matrix_transformer trans43(mat43); + bg::transform(p4d, p3d, trans43); + + BOOST_CHECK_CLOSE(double(bg::get<0>(p3d)), -2.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<1>(p3d)), 3.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<2>(p3d)), 2.0, 0.001); + + bg::strategy::transform::matrix_transformer trans33(1, 0, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 0, + 0, 0, 0, 1); + bg::transform(p3d, p3d, trans33); + + BOOST_CHECK_CLOSE(double(bg::get<0>(p3d)), -2.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<1>(p3d)), 2.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<2>(p3d)), 3.0, 0.001); + + boost::qvm::mat mat32; + boost::qvm::A<0, 0>(mat32) = 1; boost::qvm::A<0, 1>(mat32) = 1; boost::qvm::A<0, 2>(mat32) = 1; boost::qvm::A<0, 3>(mat32) = 0; + boost::qvm::A<1, 0>(mat32) = -1; boost::qvm::A<1, 1>(mat32) = 0; boost::qvm::A<1, 2>(mat32) = 1; boost::qvm::A<1, 3>(mat32) = 0; + boost::qvm::A<2, 0>(mat32) = 0; boost::qvm::A<2, 1>(mat32) = 0; boost::qvm::A<2, 2>(mat32) = 0; boost::qvm::A<2, 3>(mat32) = 1; + + bg::strategy::transform::matrix_transformer trans32(mat32); + bg::transform(p3d, p2d, trans32); + + BOOST_CHECK_CLOSE(double(bg::get<0>(p2d)), 3.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<1>(p2d)), 5.0, 0.001); + + bg::strategy::transform::matrix_transformer + trans_composed( + trans32.matrix() * trans33.matrix() * trans43.matrix() * scale44.matrix() * trans24.matrix()); + bg::transform(p2d, p2d, trans_composed); + + BOOST_CHECK_CLOSE(double(bg::get<0>(p2d)), 3.0, 0.001); + BOOST_CHECK_CLOSE(double(bg::get<1>(p2d)), 5.0, 0.001); +} + +int test_main(int, char* []) +{ + test_all(); + test_all(); + + return 0; +} From c92ccab382b3cb3f3207cbdceee0899fc8b34207 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Tue, 2 Jul 2019 19:30:51 +0300 Subject: [PATCH 14/49] Remove unused parameters (#608) [algorithms] Remove unused parameters * [algorithms] Use ignore unused instead of macros * [algorithms] Use ignore unused instead of macros 2 --- .../geometry/algorithms/detail/overlay/get_turn_info.hpp | 6 ++++-- .../index/detail/algorithms/intersection_content.hpp | 2 +- include/boost/geometry/index/parameters.hpp | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index f726fc1a0..c6429bc55 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -153,12 +153,14 @@ struct base_turn_handler typename UmbrellaStrategy, typename TurnInfo > - static inline void both_collinear(UniqueSubRange1 const& range_p, + static inline void both_collinear( + UniqueSubRange1 const& range_p, UniqueSubRange2 const& range_q, - UmbrellaStrategy const& umbrella_strategy, + UmbrellaStrategy const&, std::size_t index_p, std::size_t index_q, TurnInfo& ti) { + boost::ignore_unused(range_p, range_q); BOOST_GEOMETRY_ASSERT(IndexP + IndexQ == 1); BOOST_GEOMETRY_ASSERT(index_p > 0 && index_p <= 2); BOOST_GEOMETRY_ASSERT(index_q > 0 && index_q <= 2); diff --git a/include/boost/geometry/index/detail/algorithms/intersection_content.hpp b/include/boost/geometry/index/detail/algorithms/intersection_content.hpp index 9519162b8..2fee7b13d 100644 --- a/include/boost/geometry/index/detail/algorithms/intersection_content.hpp +++ b/include/boost/geometry/index/detail/algorithms/intersection_content.hpp @@ -24,7 +24,7 @@ namespace boost { namespace geometry { namespace index { namespace detail { // Util to distinguish between default and non-default index strategy template -inline bool disjoint_box_box(Box const& box1, Box const& box2, Strategy const& strategy) +inline bool disjoint_box_box(Box const& box1, Box const& box2, Strategy const&) { return geometry::detail::disjoint::disjoint_box_box(box1, box2, typename Strategy::disjoint_box_box_strategy_type()); diff --git a/include/boost/geometry/index/parameters.hpp b/include/boost/geometry/index/parameters.hpp index bdd5f1cd6..7c5b3fb4f 100644 --- a/include/boost/geometry/index/parameters.hpp +++ b/include/boost/geometry/index/parameters.hpp @@ -304,7 +304,7 @@ struct strategy_type< parameters > template struct get_strategy_impl { - static inline default_strategy apply(Parameters const& parameters) + static inline default_strategy apply(Parameters const&) { return default_strategy(); } From 656924a20008d3498c51c2cda548a606e4a38333 Mon Sep 17 00:00:00 2001 From: Tinko Bartels Date: Tue, 2 Jul 2019 01:08:31 +0200 Subject: [PATCH 15/49] [strategy] in matrix_transformer reverse iteration order, remove unneeded CalculationType, unneeded matrix_type typedef, change name of test --- .../transform/matrix_transformers.hpp | 148 ++++++++++++------ test/strategies/Jamfile.v2 | 2 +- 2 files changed, 101 insertions(+), 49 deletions(-) diff --git a/include/boost/geometry/strategies/transform/matrix_transformers.hpp b/include/boost/geometry/strategies/transform/matrix_transformers.hpp index 151781c3d..78de443b2 100644 --- a/include/boost/geometry/strategies/transform/matrix_transformers.hpp +++ b/include/boost/geometry/strategies/transform/matrix_transformers.hpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -49,64 +51,59 @@ namespace detail { namespace matrix_transformer template < - typename CalculationType, - typename P2, - std::size_t Dimension2, - std::size_t Dimension2Count + typename Point, + std::size_t Dimension = 0, + std::size_t DimensionCount = geometry::dimension::value > struct set_point_from_vec { - typedef CalculationType ct; - static inline void apply(P2& p2, qvm::vec& p2temp) + template + static inline void apply(Point & p, Vector const& v) { - typedef typename geometry::coordinate_type::type ct2; - set(p2, boost::numeric_cast(qvm::A(p2temp))); - set_point_from_vec::apply(p2, p2temp); + typedef typename geometry::coordinate_type::type coord_t; + set(p, boost::numeric_cast(qvm::A(v))); + set_point_from_vec::apply(p, v); } }; template < - typename CalculationType, - typename P2, - std::size_t Dimension2 + typename Point, + std::size_t DimensionCount > -struct set_point_from_vec +struct set_point_from_vec { - typedef CalculationType ct; - static inline void apply(P2& p2, qvm::vec& p2temp) {} + template + static inline void apply(Point &, Vector const& v) {} }; template < - typename CalculationType, - typename P1, - std::size_t Dimension1, - std::size_t Dimension1Count + typename Point, + std::size_t Dimension = 0, + std::size_t DimensionCount = geometry::dimension::value > struct set_vec_from_point { - typedef CalculationType ct; - static inline void apply(const P1& p1, qvm::vec& p1temp) + template + static inline void apply(Point const& p, Vector & v) { - qvm::A(p1temp) = get(p1); - set_vec_from_point::apply(p1, p1temp); + qvm::A(v) = get(p); + set_vec_from_point::apply(p, v); } }; template < - typename CalculationType, - typename P1, - std::size_t Dimension1 + typename Point, + std::size_t DimensionCount > -struct set_vec_from_point +struct set_vec_from_point { - typedef CalculationType ct; - static inline void apply(const P1& p1, qvm::vec& p1temp) {} + template + static inline void apply(Point const& p, Vector & v) {} }; - template < typename CalculationType, @@ -120,19 +117,18 @@ protected : typedef boost::qvm::mat matrix_type; matrix_type m_matrix; public : - matrix_type const& matrix() const { return m_matrix; } template inline bool apply(P1 const& p1, P2& p2) const { - assert_dimension(); - assert_dimension(); + assert_dimension_greater_equal(); + assert_dimension_greater_equal(); qvm::vec p1temp; - qvm::vec p2temp; - set_vec_from_point::apply(p1, p1temp); qvm::A(p1temp) = 1; + qvm::vec p2temp; + set_vec_from_point::apply(p1, p1temp); p2temp = m_matrix * p1temp; - set_point_from_vec::apply(p2, p2temp); + set_point_from_vec::apply(p2, p2temp); return true; } @@ -158,11 +154,7 @@ template > class matrix_transformer : public detail::matrix_transformer::matrix_transformer { -protected: - typedef CalculationType ct; - typedef boost::qvm::mat matrix_type; public: - template inline matrix_transformer(Matrix const& matrix) { @@ -175,17 +167,16 @@ public: template class matrix_transformer : public detail::matrix_transformer::matrix_transformer { -protected : typedef CalculationType ct; - typedef boost::qvm::mat matrix_type; public : - template inline matrix_transformer(Matrix const& matrix) { qvm::assign(this->m_matrix, matrix); } + inline matrix_transformer() {} + inline matrix_transformer( ct const& m_0_0, ct const& m_0_1, ct const& m_0_2, ct const& m_1_0, ct const& m_1_1, ct const& m_1_2, @@ -195,6 +186,22 @@ public : qvm::A<1,0>(this->m_matrix) = m_1_0; qvm::A<1,1>(this->m_matrix) = m_1_1; qvm::A<1,2>(this->m_matrix) = m_1_2; qvm::A<2,0>(this->m_matrix) = m_2_0; qvm::A<2,1>(this->m_matrix) = m_2_1; qvm::A<2,2>(this->m_matrix) = m_2_2; } + + template + inline bool apply(P1 const& p1, P2& p2) const + { + assert_dimension_greater_equal(); + assert_dimension_greater_equal(); + + ct const& c1 = get<0>(p1); + ct const& c2 = get<1>(p1); + + typedef typename geometry::coordinate_type::type ct2; + set<0>(p2, boost::numeric_cast(c1 * qvm::A<0,0>(this->m_matrix) + c2 * qvm::A<0,1>(this->m_matrix) + qvm::A<0,2>(this->m_matrix))); + set<1>(p2, boost::numeric_cast(c1 * qvm::A<1,0>(this->m_matrix) + c2 * qvm::A<1,1>(this->m_matrix) + qvm::A<1,2>(this->m_matrix))); + + return true; + } }; @@ -203,16 +210,15 @@ template class matrix_transformer : public detail::matrix_transformer::matrix_transformer { typedef CalculationType ct; - typedef boost::qvm::mat matrix_type; - public : - template inline matrix_transformer(Matrix const& matrix) { qvm::assign(this->m_matrix, matrix); } + inline matrix_transformer() {} + inline matrix_transformer( ct const& m_0_0, ct const& m_0_1, ct const& m_0_2, ct const& m_1_0, ct const& m_1_1, ct const& m_1_2, @@ -222,19 +228,43 @@ public : qvm::A<1,0>(this->m_matrix) = m_1_0; qvm::A<1,1>(this->m_matrix) = m_1_1; qvm::A<1,2>(this->m_matrix) = 0; qvm::A<1,3>(this->m_matrix) = m_1_2; qvm::A<2,0>(this->m_matrix) = m_2_0; qvm::A<2,1>(this->m_matrix) = m_2_1; qvm::A<2,2>(this->m_matrix) = 0; qvm::A<2,3>(this->m_matrix) = m_2_2; } + + template + inline bool apply(P1 const& p1, P2& p2) const + { + assert_dimension_greater_equal(); + assert_dimension_greater_equal(); + + ct const& c1 = get<0>(p1); + ct const& c2 = get<1>(p1); + ct const& c3 = get<2>(p1); + + typedef typename geometry::coordinate_type::type ct2; + + set<0>(p2, boost::numeric_cast( + c1 * qvm::A<0,0>(this->m_matrix) + c2 * qvm::A<0,1>(this->m_matrix) + c3 * qvm::A<0,2>(this->m_matrix) + qvm::A<0,3>(this->m_matrix))); + set<1>(p2, boost::numeric_cast( + c1 * qvm::A<1,0>(this->m_matrix) + c2 * qvm::A<1,1>(this->m_matrix) + c3 * qvm::A<1,2>(this->m_matrix) + qvm::A<1,3>(this->m_matrix))); + + return true; + } + }; template class matrix_transformer : public detail::matrix_transformer::matrix_transformer { + typedef CalculationType ct; public : template inline matrix_transformer(Matrix const& matrix) { qvm::assign(this->m_matrix, matrix); } + inline matrix_transformer() {} + inline matrix_transformer( ct const& m_0_0, ct const& m_0_1, ct const& m_0_2, ct const& m_0_3, ct const& m_1_0, ct const& m_1_1, ct const& m_1_2, ct const& m_1_3, @@ -247,6 +277,28 @@ public : qvm::A<2,0>(this->m_matrix) = m_2_0; qvm::A<2,1>(this->m_matrix) = m_2_1; qvm::A<2,2>(this->m_matrix) = m_2_2; qvm::A<2,3>(this->m_matrix) = m_2_3; qvm::A<3,0>(this->m_matrix) = m_3_0; qvm::A<3,1>(this->m_matrix) = m_3_1; qvm::A<3,2>(this->m_matrix) = m_3_2; qvm::A<3,3>(this->m_matrix) = m_3_3; } + + template + inline bool apply(P1 const& p1, P2& p2) const + { + assert_dimension_greater_equal(); + assert_dimension_greater_equal(); + + ct const& c1 = get<0>(p1); + ct const& c2 = get<1>(p1); + ct const& c3 = get<2>(p1); + + typedef typename geometry::coordinate_type::type ct2; + + set<0>(p2, boost::numeric_cast( + c1 * qvm::A<0,0>(this->m_matrix) + c2 * qvm::A<0,1>(this->m_matrix) + c3 * qvm::A<0,2>(this->m_matrix) + qvm::A<0,3>(this->m_matrix))); + set<1>(p2, boost::numeric_cast( + c1 * qvm::A<1,0>(this->m_matrix) + c2 * qvm::A<1,1>(this->m_matrix) + c3 * qvm::A<1,2>(this->m_matrix) + qvm::A<1,3>(this->m_matrix))); + set<2>(p2, boost::numeric_cast( + c1 * qvm::A<2,0>(this->m_matrix) + c2 * qvm::A<2,1>(this->m_matrix) + c3 * qvm::A<2,2>(this->m_matrix) + qvm::A<2,3>(this->m_matrix))); + + return true; + } }; @@ -426,11 +478,11 @@ template std::size_t Dimension2 > class rad_rotate_transformer - : public matrix_transformer + : public transform::matrix_transformer { public : inline rad_rotate_transformer(CalculationType const& angle) - : matrix_transformer( + : transform::matrix_transformer( cos(angle), sin(angle), 0, -sin(angle), cos(angle), 0, 0, 0, 1) diff --git a/test/strategies/Jamfile.v2 b/test/strategies/Jamfile.v2 index 706ee0140..e58abee7e 100644 --- a/test/strategies/Jamfile.v2 +++ b/test/strategies/Jamfile.v2 @@ -40,7 +40,7 @@ test-suite boost-geometry-strategies [ run thomas.cpp : : : : strategies_thomas ] [ run transform_cs.cpp : : : : strategies_transform_cs ] [ run transformer.cpp : : : : strategies_transformer ] - [ run matrix_transformer.cpp : : : : matrix_strategies_transformer ] + [ run matrix_transformer.cpp : : : : strategies_matrix_transformer ] [ run vincenty.cpp : : : : strategies_vincenty ] [ run winding.cpp : : : : strategies_winding ] ; From 6688360d9056018ffb7d91be315514bdea19f5ec Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 3 Jul 2019 14:28:35 +0200 Subject: [PATCH 16/49] [intersection] Add general_form functionality for handling of infinite lines in the form ax + by + c = 0 This is used already at several places in the library, in those places the general_form replaces that functionality. The general form will (most probably) also used for segment-segment intersection. Including unit test. This makes parallel_continue obsolete. --- .../detail/buffer/buffer_inserter.hpp | 22 +- .../detail/buffer/line_line_intersection.hpp | 59 ++--- .../detail/buffer/parallel_continue.hpp | 33 --- .../detail/overlay/get_distance_measure.hpp | 25 +- .../geometry/arithmetic/general_form.hpp | 154 +++++++++++++ test/arithmetic/Jamfile.v2 | 1 + test/arithmetic/general_form.cpp | 217 ++++++++++++++++++ 7 files changed, 411 insertions(+), 100 deletions(-) delete mode 100644 include/boost/geometry/algorithms/detail/buffer/parallel_continue.hpp create mode 100644 include/boost/geometry/arithmetic/general_form.hpp create mode 100755 test/arithmetic/general_form.cpp diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index a969b21cf..3825eb4b5 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -33,12 +33,13 @@ #include #include #include -#include #include #include #include +#include + #include @@ -175,6 +176,16 @@ struct buffer_range } } + static inline bool similar_direction(output_point_type const& p0, + output_point_type const& p1, + output_point_type const& p2) + { + typedef arithmetic::general_form gf; + gf const p = arithmetic::construct_line(p0, p1); + gf const q = arithmetic::construct_line(p1, p2); + return arithmetic::similar_direction(p, q); + } + template static inline geometry::strategy::buffer::join_selector get_join_type( output_point_type const& p0, @@ -185,13 +196,8 @@ struct buffer_range int const side = strategy.apply(p0, p1, p2); return side == -1 ? geometry::strategy::buffer::join_convex : side == 1 ? geometry::strategy::buffer::join_concave - : parallel_continue - ( - get<0>(p2) - get<0>(p1), - get<1>(p2) - get<1>(p1), - get<0>(p1) - get<0>(p0), - get<1>(p1) - get<1>(p0) - ) ? geometry::strategy::buffer::join_continue + : similar_direction(p0, p1, p2) + ? geometry::strategy::buffer::join_continue : geometry::strategy::buffer::join_spike; } diff --git a/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp b/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp index 11f1c689a..02d0d180a 100644 --- a/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp @@ -10,9 +10,8 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP -#include +#include #include -#include namespace boost { namespace geometry { @@ -22,55 +21,33 @@ namespace boost { namespace geometry namespace detail { namespace buffer { - -// TODO: once change this to proper strategy -// It is different from current segment intersection because these are not segments but lines -// If we have the Line concept, we can create a strategy -// Assumes a convex corner +// TODO: it might once be changed this to proper strategy struct line_line_intersection { - template - static inline strategy::buffer::join_selector apply(Point const& pi, Point const& pj, - Point const& qi, Point const& qj, Point& ip) + static inline strategy::buffer::join_selector + apply(Point const& pi, Point const& pj, + Point const& qi, Point const& qj, + Point& ip) { typedef typename coordinate_type::type ct; + typedef arithmetic::general_form gf; - // Construct lines in general form (ax + by + c = 0), - // (will be replaced by a general_form structure in a next PR) - ct const pa = get<1>(pi) - get<1>(pj); - ct const pb = get<0>(pj) - get<0>(pi); - ct const pc = -pa * get<0>(pi) - pb * get<1>(pi); + gf const p = arithmetic::construct_line(pi, pj); + gf const q = arithmetic::construct_line(qi, qj); - ct const qa = get<1>(qi) - get<1>(qj); - ct const qb = get<0>(qj) - get<0>(qi); - ct const qc = -qa * get<0>(qi) - qb * get<1>(qi); - - ct const denominator = pb * qa - pa * qb; - - // Even if the corner was checked before (so it is convex now), that - // was done on the original geometry. This function runs on the buffered - // geometries, where sides are generated and might be slightly off. In - // Floating Point, that slightly might just exceed the limit and we have - // to check it again. - - // For round joins, it will not be used at all. - // For miter joins, there is a miter limit - // If segments are parallel/collinear we must be distinguish two cases: - // they continue each other, or they form a spike - ct const zero = ct(); - if (math::equals(denominator, zero)) + if (arithmetic::get_intersection(ip, p, q)) { - return parallel_continue(qb, -qa, pb, -pa) - ? strategy::buffer::join_continue - : strategy::buffer::join_spike - ; + return strategy::buffer::join_convex; } - set<0>(ip, (pc * qb - pb * qc) / denominator); - set<1>(ip, (pa * qc - pc * qa) / denominator); - - return strategy::buffer::join_convex; + // The lines do not intersect. + // Distinguish between continuing lines (having a similar direction) + // and spikes (having the opposite direction). + return arithmetic::similar_direction(p, q) + ? strategy::buffer::join_continue + : strategy::buffer::join_spike + ; } }; diff --git a/include/boost/geometry/algorithms/detail/buffer/parallel_continue.hpp b/include/boost/geometry/algorithms/detail/buffer/parallel_continue.hpp deleted file mode 100644 index 119d64de7..000000000 --- a/include/boost/geometry/algorithms/detail/buffer/parallel_continue.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) - -// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. - -// Use, modification and distribution is subject to the Boost Software License, -// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP - - -namespace boost { namespace geometry -{ - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace buffer -{ - -template -inline bool parallel_continue(T dx1, T dy1, T dx2, T dy2) -{ - T const dot = dx1 * dx2 + dy1 * dy2; - return dot > 0; -} - -}} // namespace detail::buffer -#endif // DOXYGEN_NO_DETAIL - - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp b/include/boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp index a306cb442..ccb2f5841 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -100,26 +101,14 @@ struct get_distance_measure static result_type apply(SegmentPoint const& p1, SegmentPoint const& p2, Point const& p) { - typedef CalculationType ct; + // Get the distance measure / side value + // It is not a real distance and purpose is + // to detect small differences in collinearity - // Construct a line in general form (ax + by + c = 0), - // (will be replaced by a general_form structure in next PR) - ct const x1 = geometry::get<0>(p1); - ct const y1 = geometry::get<1>(p1); - ct const x2 = geometry::get<0>(p2); - ct const y2 = geometry::get<1>(p2); - ct const a = y1 - y2; - ct const b = x2 - x1; - ct const c = -a * x1 - b * y1; - - // Returns a distance measure - // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_an_equation - // dividing by sqrt(a*a+b*b) is not necessary for this distance measure, - // it is not a real distance and purpose is to detect small differences - // in collinearity + typedef arithmetic::general_form gf; + gf const f = arithmetic::construct_line(p1, p2); result_type result; - result.measure = a * geometry::get<0>(p) + b * geometry::get<1>(p) + c; - + result.measure = arithmetic::side_value(f, p); return result; } }; diff --git a/include/boost/geometry/arithmetic/general_form.hpp b/include/boost/geometry/arithmetic/general_form.hpp new file mode 100644 index 000000000..d344b7ead --- /dev/null +++ b/include/boost/geometry/arithmetic/general_form.hpp @@ -0,0 +1,154 @@ +// Boost.Geometry + +// Copyright (c) 2018-2019 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ARITHMETIC_GENERAL_FORM_HPP +#define BOOST_GEOMETRY_ARITHMETIC_GENERAL_FORM_HPP + +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + +namespace arithmetic +{ + +//-------------------------------------------------------------------------- +// Structure containing the General Form of a line, a*x + b*y + c == 0 +// Might be conceptized later. Therefore operations are implemented outside +// the structure itself. +template +struct general_form +{ + general_form() + : a(GeneralType()) + , b(GeneralType()) + , c(GeneralType()) + , normalized(false) + {} + + // Horizontal: a == 0, for example y-3=0, y==3 + // Vertical: b == 0, for example x-2=0, x==2 + // Through origin: c == 0 + GeneralType a; + GeneralType b; + GeneralType c; + bool normalized; +}; + +template +inline +general_form construct_line(Coordinate const& x1, + Coordinate const& y1, Coordinate const& x2, Coordinate const& y2) +{ + general_form result; + result.a = y1 - y2; + result.b = x2 - x1; + result.c = -result.a * x1 - result.b * y1; + return result; +} + +template +inline general_form construct_line(Point const& a, Point const& b) +{ + return construct_line(geometry::get<0>(a), + geometry::get<1>(a), + geometry::get<0>(b), + geometry::get<1>(b)); +} + +template +inline general_form construct_line(Segment const& segment) +{ + return construct_line(geometry::get<0, 0>(segment), + geometry::get<0, 1>(segment), + geometry::get<1, 0>(segment), + geometry::get<1, 1>(segment)); +} + + +// Calculates intersection point of two infinite lines. +// Returns true if the lines intersect. +// Returns false if lines are parallel (or collinear, possibly opposite) +template +inline +bool get_intersection(Point& ip, + general_form const& p, + general_form const& q) +{ + GeneralType const denominator = p.b * q.a - p.a * q.b; + + static GeneralType const zero = 0; + if (math::equals(denominator, zero)) + { + // Lines are parallel + return false; + } + + // Calculate the intersection coordinates + geometry::set<0>(ip, (p.c * q.b - p.b * q.c) / denominator); + geometry::set<1>(ip, (p.a * q.c - p.c * q.a) / denominator); + + return true; +} + +//! Return a distance-side-measure for a point to a line +//! Point is located left of the line if value is positive, +//! right of the line is value is negative, and on the line if the value +//! is exactly zero +template +inline +typename select_most_precise::type +side_value(general_form const& f, + CoordinateType const& x, CoordinateType const& y) +{ + // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_an_equation + // Distance from point to line in general form is given as: + // (a * x + b * y + c) / sqrt(a * a + b * b); + // In most use cases comparisons are enough, saving the sqrt + // and often even the division. + // Also, this gives positive values for points left to the line, + // and negative values for points right to the line. + return f.a * x + f.b * y + f.c; +} + +template +inline +typename select_most_precise +< + GeneralType, + typename geometry::coordinate_type::type +>::type +side_value(general_form const& f, Point const& p) +{ + return side_value(f, geometry::get<0>(p), geometry::get<1>(p)); +} + +// Returns true for two lines which are supposed to be (close to) collinear +// (which is not checked) and have a similar direction +// (in practice up to 45 degrees, TO BE VERIFIED) +// true: -----------------> p -----------------> q +// false: -----------------> p <----------------- q +template +inline +bool similar_direction(const general_form& p, + const general_form& q) +{ + return p.a * q.a >= 0 && p.b * q.b >= 0; +} + + +} // namespace arithmetic + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ARITHMETIC_GENERAL_FORM_HPP diff --git a/test/arithmetic/Jamfile.v2 b/test/arithmetic/Jamfile.v2 index 9e108104a..f7c54a523 100644 --- a/test/arithmetic/Jamfile.v2 +++ b/test/arithmetic/Jamfile.v2 @@ -14,5 +14,6 @@ test-suite boost-geometry-arithmetic [ run general.cpp : : : : arithmetic_general ] [ run dot_product.cpp : : : : arithmetic_dot_product ] [ run cross_product.cpp : : : : arithmetic_cross_product ] + [ run general_form.cpp : : : : arithmetic_general_form ] [ compile-fail cross_product.cpp : TEST_FAIL_CROSS_PRODUCT : arithmetic_cross_product_cf ] ; diff --git a/test/arithmetic/general_form.cpp b/test/arithmetic/general_form.cpp new file mode 100755 index 000000000..7cb779112 --- /dev/null +++ b/test/arithmetic/general_form.cpp @@ -0,0 +1,217 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2018-2019 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include + +namespace +{ + // Boost.Test does not support BOOST_CHECK_CLOSE for integral types + template + bool is_small(T const& value) + { + static long double const epsilon = 1.0e-5; + return std::fabs(value) < epsilon; + } +} + +template +void verify_point_on_line(bg::arithmetic::general_form const& f, + C const& x, C const& y) +{ + BOOST_CHECK_MESSAGE(is_small(f.a * x + f.b * y + f.c), + "Point is not located on the line"); +} + +template +void test_construct_line() +{ + typedef bg::arithmetic::general_form gf; + + // Horizontal through origin + gf f = bg::arithmetic::construct_line(0, 0, 10, 0); + verify_point_on_line(f, 0, 0); + verify_point_on_line(f, 10, 0); + + // Horizontal line above origin + f = bg::arithmetic::construct_line(0, 5, 10, 5); + verify_point_on_line(f, 0, 5); + verify_point_on_line(f, 10, 5); + + // Vertical through origin + f = bg::arithmetic::construct_line(0, 0, 0, 10); + verify_point_on_line(f, 0, 0); + verify_point_on_line(f, 0, 10); + + // Vertical line left from origin + f = bg::arithmetic::construct_line(5, 0, 5, 10); + verify_point_on_line(f, 5, 0); + verify_point_on_line(f, 5, 10); + + // Diagonal through origin + f = bg::arithmetic::construct_line(0, 0, 8, 10); + verify_point_on_line(f, 0, 0); + verify_point_on_line(f, 8, 10); + + // Diagonal not through origin + f = bg::arithmetic::construct_line(5, 2, -8, 10); + verify_point_on_line(f, 5, 2); + verify_point_on_line(f, -8, 10); +} + +template +void test_side_value() +{ + typedef bg::arithmetic::general_form gf; + + // Horizontal line going right + gf f = bg::arithmetic::construct_line(0, 0, 10, 0); + + // Point above (= on left side) + T d = bg::arithmetic::side_value(f, 5, 5); + BOOST_CHECK_MESSAGE(d > 0, "point not on left side"); + + // Point below (= on right side) + d = bg::arithmetic::side_value(f, 5, -5); + BOOST_CHECK_MESSAGE(d < 0, "point not on right side"); + + // Diagonal not through origin, from right (down) to left (up) + f = bg::arithmetic::construct_line(5, 2, -7, 10); + d = bg::arithmetic::side_value(f, 5, 2); + BOOST_CHECK_MESSAGE(is_small(d), "point not on line"); + d = bg::arithmetic::side_value(f, -7, 10); + BOOST_CHECK_MESSAGE(is_small(d), "point not on line"); + + // vector is (-12, 8), move (-3,2) on the line from (5,2) + d = bg::arithmetic::side_value(f, 2, 4); + BOOST_CHECK_MESSAGE(is_small(d), "point not on line"); + + // Go perpendicular (2,3) from (2,4) up, so right of the line (negative) + d = bg::arithmetic::side_value(f, 4, 7); + BOOST_CHECK_MESSAGE(d < 0, "point not on right side"); + + // Go perpendicular (2,3) from (2,4) down, so left of the line (positive) + d = bg::arithmetic::side_value(f, 0, 1); + BOOST_CHECK_MESSAGE(d > 0, "point not on left side"); +} + + +template +void test_get_intersection() +{ + typedef bg::arithmetic::general_form gf; + + // Diagonal lines (first is same as in distance measure, + // second is perpendicular and used there for distance measures) + gf p = bg::arithmetic::construct_line(5, 2, -7, 10); + gf q = bg::arithmetic::construct_line(4, 7, 0, 1); + + typedef bg::model::point point_type; + point_type ip; + BOOST_CHECK(bg::arithmetic::get_intersection(ip, p, q)); + + BOOST_CHECK_MESSAGE(is_small(bg::get<0>(ip) - 2), "x-coordinate wrong"); + BOOST_CHECK_MESSAGE(is_small(bg::get<1>(ip) - 4), "y-coordinate wrong"); + + verify_point_on_line(p, bg::get<0>(ip), bg::get<1>(ip)); + verify_point_on_line(q, bg::get<0>(ip), bg::get<1>(ip)); +} + +template +void test_same_direction() +{ + bg::arithmetic::general_form p, q; + + // Exactly opposite, diagonal + p = bg::arithmetic::construct_line(2, 1, 12, 11); + q = bg::arithmetic::construct_line(12, 11, 2, 1); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Exactly opposite, horizontal + p = bg::arithmetic::construct_line(0, 0, 10, 0); + q = bg::arithmetic::construct_line(10, 0, 0, 0); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Exactly opposite, vertical + p = bg::arithmetic::construct_line(0, 0, 0, 10); + q = bg::arithmetic::construct_line(0, 10, 0, 0); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Exactly equal, diagonal + p = bg::arithmetic::construct_line(0, 0, 10, 10); + q = bg::arithmetic::construct_line(0, 0, 10, 10); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // Exactly equal, horizontal + p = bg::arithmetic::construct_line(0, 0, 10, 0); + q = bg::arithmetic::construct_line(0, 0, 10, 0); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // Exactly equal, vertical + p = bg::arithmetic::construct_line(0, 0, 0, 10); + q = bg::arithmetic::construct_line(0, 0, 0, 10); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // Coming together, diagonal + p = bg::arithmetic::construct_line(0, 0, 10, 10); + q = bg::arithmetic::construct_line(20, 20, 10, 10); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Leaving from common point, diagonal + p = bg::arithmetic::construct_line(10, 10, 0, 0); + q = bg::arithmetic::construct_line(0, 0, 10, 10); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Continuing each other, diagonal + p = bg::arithmetic::construct_line(0, 0, 10, 10); + q = bg::arithmetic::construct_line(10, 10, 20, 20); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // (Nearly) perpendicular + p = bg::arithmetic::construct_line(0, 0, 10, 10); + q = bg::arithmetic::construct_line(0, 0, -10, 10); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // 45 deg + p = bg::arithmetic::construct_line(0, 0, 10, 10); + q = bg::arithmetic::construct_line(0, 0, 0, 10); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // a bit more than 45 deg + p = bg::arithmetic::construct_line(0, 0, 10, 10); + q = bg::arithmetic::construct_line(0, 0, -1, 10); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // 135 deg + p = bg::arithmetic::construct_line(0, 0, 10, 10); + q = bg::arithmetic::construct_line(0, 0, -10, 0); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); +} + + +template +void test_all() +{ + test_construct_line(); + test_side_value(); + test_get_intersection(); + test_same_direction(); +} + +int test_main(int, char* []) +{ + test_all(); + test_all(); + test_all(); + test_all(); + return 0; +} From 3db9de9b766fe0e8920c520d6def603a266cc96a Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 3 Jul 2019 14:43:03 +0200 Subject: [PATCH 17/49] [spike] Remove debug code --- .../algorithms/detail/direction_code.hpp | 14 ---------- .../detail/point_is_spike_or_equal.hpp | 28 ------------------- 2 files changed, 42 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index ab3b1e0a2..13620230b 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -32,20 +32,6 @@ namespace boost { namespace geometry namespace detail { - -// TODO: remove -template -inline int sign_of_difference(Point1 const& point1, Point2 const& point2) -{ - return - math::equals(geometry::get(point1), geometry::get(point2)) - ? - 0 - : - (geometry::get(point1) > geometry::get(point2) ? 1 : -1); -} - - template struct direction_code_impl { diff --git a/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp b/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp index 502a5b0bb..e5ba6e30d 100644 --- a/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp +++ b/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp @@ -35,26 +35,6 @@ namespace boost { namespace geometry namespace detail { -template -inline bool collinear_point_is_spike_or_equal(Point1 const& last_point, - Point2 const& segment_a, - Point3 const& segment_b) -{ - // Check if segment is equal - int const sgn_x1 = sign_of_difference<0>(last_point, segment_b); - int const sgn_y1 = sign_of_difference<1>(last_point, segment_b); - if (sgn_x1 == 0 && sgn_y1 == 0) - { - return true; - } - - // Check if segment moves forward - int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); - int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); - - return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; -} - // Checks if a point ("last_point") causes a spike w.r.t. // the specified two other points (segment_a, segment_b) // @@ -81,15 +61,7 @@ inline bool point_is_spike_or_equal(Point1 const& last_point, // prev | back if (side == 0) { // Last point is collinear w.r.t previous segment. -#ifdef BOOST_GEOMETRY_ENABLE_POINT_IS_SPIKE_OR_EQUAL_TEST - bool r1 = collinear_point_is_spike_or_equal(last_point, segment_a, segment_b); - bool r2 = direction_code(segment_a, segment_b, last_point) < 1; - if (r1 != r2) - std::cout << "spike detection failure with: " << r1 << " " << r2 << std::endl; - return r2; -#else return direction_code(segment_a, segment_b, last_point) < 1; -#endif } return false; } From b2412f986b608499245cf03f46423c32de10916b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 3 Jul 2019 14:55:46 +0200 Subject: [PATCH 18/49] [direction] use general_form for direction code --- .../algorithms/detail/direction_code.hpp | 44 +++++++------------ 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index 13620230b..5021e7977 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -43,42 +44,27 @@ struct direction_code_impl { template static inline int apply(Point1 const& segment_a, Point1 const& segment_b, - Point2 const& p) + Point2 const& point) { + if ( (math::equals(geometry::get<0>(segment_b), geometry::get<0>(segment_a)) + && math::equals(geometry::get<1>(segment_b), geometry::get<1>(segment_a))) + || (math::equals(geometry::get<0>(segment_b), geometry::get<0>(point)) + && math::equals(geometry::get<1>(segment_b), geometry::get<1>(point))) ) + { + return 0; + } + typedef typename geometry::select_coordinate_type < Point1, Point2 >::type calc_t; - if ( (math::equals(geometry::get<0>(segment_b), geometry::get<0>(segment_a)) - && math::equals(geometry::get<1>(segment_b), geometry::get<1>(segment_a))) - || (math::equals(geometry::get<0>(segment_b), geometry::get<0>(p)) - && math::equals(geometry::get<1>(segment_b), geometry::get<1>(p))) ) - { - return 0; - } + typedef arithmetic::general_form gf; + gf const p = arithmetic::construct_line(segment_a, segment_b); + gf const q = arithmetic::construct_line(segment_b, point); - calc_t x1 = geometry::get<0>(segment_b) - geometry::get<0>(segment_a); - calc_t y1 = geometry::get<1>(segment_b) - geometry::get<1>(segment_a); - calc_t x2 = geometry::get<0>(segment_b) - geometry::get<0>(p); - calc_t y2 = geometry::get<1>(segment_b) - geometry::get<1>(p); - - calc_t ax = (std::min)(math::abs(x1), math::abs(x2)); - calc_t ay = (std::min)(math::abs(y1), math::abs(y2)); - - int s1 = 0, s2 = 0; - if (ax >= ay) - { - s1 = x1 > 0 ? 1 : -1; - s2 = x2 > 0 ? 1 : -1; - } - else - { - s1 = y1 > 0 ? 1 : -1; - s2 = y2 > 0 ? 1 : -1; - } - - return s1 == s2 ? -1 : 1; + // p extends a-b if direction is similar + return arithmetic::similar_direction(p, q) ? 1 : -1; } }; From a1e1caf8e538e3891ec8f297a390a4f7d7a8a29e Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 3 Jul 2019 19:38:03 +0200 Subject: [PATCH 19/49] [general_form] add is_degenerate predicate and use it in direction code, instead of 4 equality checks --- .../algorithms/detail/direction_code.hpp | 21 +++++++++++-------- .../geometry/arithmetic/general_form.hpp | 7 +++++++ test/arithmetic/general_form.cpp | 19 +++++++++++++++++ 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index 5021e7977..313229dfd 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -46,22 +46,25 @@ struct direction_code_impl static inline int apply(Point1 const& segment_a, Point1 const& segment_b, Point2 const& point) { - if ( (math::equals(geometry::get<0>(segment_b), geometry::get<0>(segment_a)) - && math::equals(geometry::get<1>(segment_b), geometry::get<1>(segment_a))) - || (math::equals(geometry::get<0>(segment_b), geometry::get<0>(point)) - && math::equals(geometry::get<1>(segment_b), geometry::get<1>(point))) ) - { - return 0; - } - typedef typename geometry::select_coordinate_type < Point1, Point2 >::type calc_t; typedef arithmetic::general_form gf; - gf const p = arithmetic::construct_line(segment_a, segment_b); + + // point b is often equal to the specified point, check that first gf const q = arithmetic::construct_line(segment_b, point); + if (arithmetic::is_degenerate(q)) + { + return 0; + } + + gf const p = arithmetic::construct_line(segment_a, segment_b); + if (arithmetic::is_degenerate(p)) + { + return 0; + } // p extends a-b if direction is similar return arithmetic::similar_direction(p, q) ? 1 : -1; diff --git a/include/boost/geometry/arithmetic/general_form.hpp b/include/boost/geometry/arithmetic/general_form.hpp index d344b7ead..115ac61cb 100644 --- a/include/boost/geometry/arithmetic/general_form.hpp +++ b/include/boost/geometry/arithmetic/general_form.hpp @@ -144,6 +144,13 @@ bool similar_direction(const general_form& p, return p.a * q.a >= 0 && p.b * q.b >= 0; } +template +inline bool is_degenerate(const general_form& f) +{ + static GeneralType const zero = 0; + return math::equals(f.a, zero) && math::equals(f.b, zero); +} + } // namespace arithmetic diff --git a/test/arithmetic/general_form.cpp b/test/arithmetic/general_form.cpp index 7cb779112..744b7d231 100755 --- a/test/arithmetic/general_form.cpp +++ b/test/arithmetic/general_form.cpp @@ -197,6 +197,24 @@ void test_same_direction() BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); } +template +void test_degenerate() +{ + typedef bg::arithmetic::general_form gf; + + gf f = bg::arithmetic::construct_line(0, 0, 10, 0); + BOOST_CHECK(! bg::arithmetic::is_degenerate(f)); + + f = bg::arithmetic::construct_line(0, 0, 0, 10); + BOOST_CHECK(! bg::arithmetic::is_degenerate(f)); + + f = bg::arithmetic::construct_line(0, 0, 10, 10); + BOOST_CHECK(! bg::arithmetic::is_degenerate(f)); + + f = bg::arithmetic::construct_line(0, 0, 0, 0); + BOOST_CHECK(bg::arithmetic::is_degenerate(f)); +} + template void test_all() @@ -205,6 +223,7 @@ void test_all() test_side_value(); test_get_intersection(); test_same_direction(); + test_degenerate(); } int test_main(int, char* []) From 422a0b768b6e294d79c5e5812418bd22c49ea091 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 10 Jul 2019 11:19:30 +0200 Subject: [PATCH 20/49] [line] process review remarks, rename general_form to infinite_line --- .../detail/buffer/buffer_inserter.hpp | 9 +- .../detail/buffer/line_line_intersection.hpp | 11 +- .../algorithms/detail/direction_code.hpp | 15 +- .../geometry/algorithms/detail/make/make.hpp | 60 +++++ .../detail/overlay/get_distance_measure.hpp | 9 +- .../geometry/arithmetic/general_form.hpp | 161 ------------ .../arithmetic/infinite_line_functions.hpp | 106 ++++++++ .../geometry/geometries/infinite_line.hpp | 50 ++++ test/arithmetic/Jamfile.v2 | 20 +- test/arithmetic/general_form.cpp | 236 ------------------ test/arithmetic/infinite_line_functions.cpp | 201 +++++++++++++++ test/geometries/Jamfile.v2 | 11 +- test/geometries/infinite_line.cpp | 85 +++++++ 13 files changed, 542 insertions(+), 432 deletions(-) create mode 100644 include/boost/geometry/algorithms/detail/make/make.hpp delete mode 100644 include/boost/geometry/arithmetic/general_form.hpp create mode 100644 include/boost/geometry/arithmetic/infinite_line_functions.hpp create mode 100644 include/boost/geometry/geometries/infinite_line.hpp delete mode 100755 test/arithmetic/general_form.cpp create mode 100755 test/arithmetic/infinite_line_functions.cpp create mode 100755 test/geometries/infinite_line.cpp diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index 3825eb4b5..c79abcc7a 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -38,7 +39,7 @@ #include #include -#include +#include #include @@ -180,9 +181,9 @@ struct buffer_range output_point_type const& p1, output_point_type const& p2) { - typedef arithmetic::general_form gf; - gf const p = arithmetic::construct_line(p0, p1); - gf const q = arithmetic::construct_line(p1, p2); + typedef model::infinite_line line_type; + line_type const p = detail::make::make_infinite_line(p0, p1); + line_type const q = detail::make::make_infinite_line(p1, p2); return arithmetic::similar_direction(p, q); } diff --git a/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp b/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp index 02d0d180a..ec50d91a8 100644 --- a/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp @@ -10,7 +10,8 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP -#include +#include +#include #include namespace boost { namespace geometry @@ -31,12 +32,12 @@ struct line_line_intersection Point& ip) { typedef typename coordinate_type::type ct; - typedef arithmetic::general_form gf; + typedef model::infinite_line line_type; - gf const p = arithmetic::construct_line(pi, pj); - gf const q = arithmetic::construct_line(qi, qj); + line_type const p = detail::make::make_infinite_line(pi, pj); + line_type const q = detail::make::make_infinite_line(qi, qj); - if (arithmetic::get_intersection(ip, p, q)) + if (arithmetic::intersection_point(p, q, ip)) { return strategy::buffer::join_convex; } diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index 313229dfd..38c65afa2 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -12,12 +12,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_ALGORITHMS_DETAIL_DIRECITON_CODE_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECITON_CODE_HPP +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECTION_CODE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECTION_CODE_HPP #include -#include +#include +#include #include #include #include @@ -51,16 +52,16 @@ struct direction_code_impl Point1, Point2 >::type calc_t; - typedef arithmetic::general_form gf; + typedef model::infinite_line line_type; // point b is often equal to the specified point, check that first - gf const q = arithmetic::construct_line(segment_b, point); + line_type const q = detail::make::make_infinite_line(segment_b, point); if (arithmetic::is_degenerate(q)) { return 0; } - gf const p = arithmetic::construct_line(segment_a, segment_b); + line_type const p = detail::make::make_infinite_line(segment_a, segment_b); if (arithmetic::is_degenerate(p)) { return 0; @@ -252,4 +253,4 @@ inline int direction_code(Point1 const& segment_a, Point1 const& segment_b, }} // namespace boost::geometry -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECITON_CODE_HPP +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECTION_CODE_HPP diff --git a/include/boost/geometry/algorithms/detail/make/make.hpp b/include/boost/geometry/algorithms/detail/make/make.hpp new file mode 100644 index 000000000..87631992a --- /dev/null +++ b/include/boost/geometry/algorithms/detail/make/make.hpp @@ -0,0 +1,60 @@ +// Boost.Geometry + +// Copyright (c) 2019 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_MAKE_MAKE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_MAKE_MAKE_HPP + +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace make +{ + +template +inline +model::infinite_line make_infinite_line(Coordinate const& x1, + Coordinate const& y1, Coordinate const& x2, Coordinate const& y2) +{ + model::infinite_line result; + result.a = y1 - y2; + result.b = x2 - x1; + result.c = -result.a * x1 - result.b * y1; + return result; +} + +template +inline +model::infinite_line make_infinite_line(Point const& a, Point const& b) +{ + return make_infinite_line(geometry::get<0>(a), geometry::get<1>(a), + geometry::get<0>(b), geometry::get<1>(b)); +} + +template +inline +model::infinite_line make_infinite_line(Segment const& segment) +{ + return make_infinite_line(geometry::get<0, 0>(segment), + geometry::get<0, 1>(segment), + geometry::get<1, 0>(segment), + geometry::get<1, 1>(segment)); +} + + + +}} // namespace detail::make +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_MAKE_MAKE_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp b/include/boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp index ccb2f5841..a4de22b27 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_distance_measure.hpp @@ -12,7 +12,8 @@ #include #include #include -#include +#include +#include #include #include @@ -105,10 +106,10 @@ struct get_distance_measure // It is not a real distance and purpose is // to detect small differences in collinearity - typedef arithmetic::general_form gf; - gf const f = arithmetic::construct_line(p1, p2); + typedef model::infinite_line line_type; + line_type const line = detail::make::make_infinite_line(p1, p2); result_type result; - result.measure = arithmetic::side_value(f, p); + result.measure = arithmetic::side_value(line, p); return result; } }; diff --git a/include/boost/geometry/arithmetic/general_form.hpp b/include/boost/geometry/arithmetic/general_form.hpp deleted file mode 100644 index 115ac61cb..000000000 --- a/include/boost/geometry/arithmetic/general_form.hpp +++ /dev/null @@ -1,161 +0,0 @@ -// Boost.Geometry - -// Copyright (c) 2018-2019 Barend Gehrels, Amsterdam, the Netherlands. - -// Use, modification and distribution is subject to the Boost Software License, -// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_GEOMETRY_ARITHMETIC_GENERAL_FORM_HPP -#define BOOST_GEOMETRY_ARITHMETIC_GENERAL_FORM_HPP - -#include -#include -#include -#include - -namespace boost { namespace geometry -{ - -namespace arithmetic -{ - -//-------------------------------------------------------------------------- -// Structure containing the General Form of a line, a*x + b*y + c == 0 -// Might be conceptized later. Therefore operations are implemented outside -// the structure itself. -template -struct general_form -{ - general_form() - : a(GeneralType()) - , b(GeneralType()) - , c(GeneralType()) - , normalized(false) - {} - - // Horizontal: a == 0, for example y-3=0, y==3 - // Vertical: b == 0, for example x-2=0, x==2 - // Through origin: c == 0 - GeneralType a; - GeneralType b; - GeneralType c; - bool normalized; -}; - -template -inline -general_form construct_line(Coordinate const& x1, - Coordinate const& y1, Coordinate const& x2, Coordinate const& y2) -{ - general_form result; - result.a = y1 - y2; - result.b = x2 - x1; - result.c = -result.a * x1 - result.b * y1; - return result; -} - -template -inline general_form construct_line(Point const& a, Point const& b) -{ - return construct_line(geometry::get<0>(a), - geometry::get<1>(a), - geometry::get<0>(b), - geometry::get<1>(b)); -} - -template -inline general_form construct_line(Segment const& segment) -{ - return construct_line(geometry::get<0, 0>(segment), - geometry::get<0, 1>(segment), - geometry::get<1, 0>(segment), - geometry::get<1, 1>(segment)); -} - - -// Calculates intersection point of two infinite lines. -// Returns true if the lines intersect. -// Returns false if lines are parallel (or collinear, possibly opposite) -template -inline -bool get_intersection(Point& ip, - general_form const& p, - general_form const& q) -{ - GeneralType const denominator = p.b * q.a - p.a * q.b; - - static GeneralType const zero = 0; - if (math::equals(denominator, zero)) - { - // Lines are parallel - return false; - } - - // Calculate the intersection coordinates - geometry::set<0>(ip, (p.c * q.b - p.b * q.c) / denominator); - geometry::set<1>(ip, (p.a * q.c - p.c * q.a) / denominator); - - return true; -} - -//! Return a distance-side-measure for a point to a line -//! Point is located left of the line if value is positive, -//! right of the line is value is negative, and on the line if the value -//! is exactly zero -template -inline -typename select_most_precise::type -side_value(general_form const& f, - CoordinateType const& x, CoordinateType const& y) -{ - // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_an_equation - // Distance from point to line in general form is given as: - // (a * x + b * y + c) / sqrt(a * a + b * b); - // In most use cases comparisons are enough, saving the sqrt - // and often even the division. - // Also, this gives positive values for points left to the line, - // and negative values for points right to the line. - return f.a * x + f.b * y + f.c; -} - -template -inline -typename select_most_precise -< - GeneralType, - typename geometry::coordinate_type::type ->::type -side_value(general_form const& f, Point const& p) -{ - return side_value(f, geometry::get<0>(p), geometry::get<1>(p)); -} - -// Returns true for two lines which are supposed to be (close to) collinear -// (which is not checked) and have a similar direction -// (in practice up to 45 degrees, TO BE VERIFIED) -// true: -----------------> p -----------------> q -// false: -----------------> p <----------------- q -template -inline -bool similar_direction(const general_form& p, - const general_form& q) -{ - return p.a * q.a >= 0 && p.b * q.b >= 0; -} - -template -inline bool is_degenerate(const general_form& f) -{ - static GeneralType const zero = 0; - return math::equals(f.a, zero) && math::equals(f.b, zero); -} - - -} // namespace arithmetic - - -}} // namespace boost::geometry - - -#endif // BOOST_GEOMETRY_ARITHMETIC_GENERAL_FORM_HPP diff --git a/include/boost/geometry/arithmetic/infinite_line_functions.hpp b/include/boost/geometry/arithmetic/infinite_line_functions.hpp new file mode 100644 index 000000000..0e8281483 --- /dev/null +++ b/include/boost/geometry/arithmetic/infinite_line_functions.hpp @@ -0,0 +1,106 @@ +// Boost.Geometry + +// Copyright (c) 2018-2019 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ARITHMETIC_LINE_FUNCTIONS_HPP +#define BOOST_GEOMETRY_ARITHMETIC_LINE_FUNCTIONS_HPP + +#include +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + +namespace arithmetic +{ + +// Calculates intersection point of two infinite lines. +// Returns true if the lines intersect. +// Returns false if lines are parallel (or collinear, possibly opposite) +template +inline bool intersection_point(model::infinite_line const& p, + model::infinite_line const& q, Point& ip) +{ + Type const denominator = p.b * q.a - p.a * q.b; + + static Type const zero = 0; + if (math::equals(denominator, zero)) + { + // Lines are parallel + return false; + } + + // Calculate the intersection coordinates + geometry::set<0>(ip, (p.c * q.b - p.b * q.c) / denominator); + geometry::set<1>(ip, (p.a * q.c - p.c * q.a) / denominator); + + return true; +} + +//! Return a distance-side-measure for a point to a line +//! Point is located left of the line if value is positive, +//! right of the line is value is negative, and on the line if the value +//! is exactly zero +template +inline +typename select_most_precise::type +side_value(model::infinite_line const& line, + CoordinateType const& x, CoordinateType const& y) +{ + // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_an_equation + // Distance from point to line in general form is given as: + // (a * x + b * y + c) / sqrt(a * a + b * b); + // In most use cases comparisons are enough, saving the sqrt + // and often even the division. + // Also, this gives positive values for points left to the line, + // and negative values for points right to the line. + return line.a * x + line.b * y + line.c; +} + +template +inline +typename select_most_precise +< + Type, + typename geometry::coordinate_type::type +>::type +side_value(model::infinite_line const& line, Point const& p) +{ + return side_value(line, geometry::get<0>(p), geometry::get<1>(p)); +} + +// Returns true for two lines which are supposed to be (close to) collinear +// (which is not checked) and have a similar direction +// (in practice up to 45 degrees, TO BE VERIFIED) +// true: -----------------> p -----------------> q +// false: -----------------> p <----------------- q +template +inline +bool similar_direction(const model::infinite_line& p, + const model::infinite_line& q) +{ + return p.a * q.a >= 0 && p.b * q.b >= 0; +} + +template +inline bool is_degenerate(const model::infinite_line& line) +{ + static Type const zero = 0; + return math::equals(line.a, zero) && math::equals(line.b, zero); +} + + +} // namespace arithmetic + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ARITHMETIC_LINE_FUNCTIONS_HPP diff --git a/include/boost/geometry/geometries/infinite_line.hpp b/include/boost/geometry/geometries/infinite_line.hpp new file mode 100644 index 000000000..1c04a3bec --- /dev/null +++ b/include/boost/geometry/geometries/infinite_line.hpp @@ -0,0 +1,50 @@ +// Boost.Geometry + +// Copyright (c) 2018-2019 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_GEOMETRIES_INFINITE_LINE_HPP +#define BOOST_GEOMETRY_GEOMETRIES_INFINITE_LINE_HPP + + +namespace boost { namespace geometry +{ + +namespace model +{ + +//-------------------------------------------------------------------------- +// Structure containing an infinite line. +// It is written using "General Form", a*x + b*y + c == 0 +// Might be conceptized later. Therefore operations are implemented outside +// the structure itself. +template +struct infinite_line +{ + infinite_line() + : a(0) + , b(0) + , c(0) + , normalized(false) + {} + + // Horizontal: a == 0, for example y-3=0, y==3 + // Vertical: b == 0, for example x-2=0, x==2 + // Through origin: c == 0 + Type a; + Type b; + Type c; + bool normalized; +}; + + +} // namespace model + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_GEOMETRIES_INFINITE_LINE_HPP diff --git a/test/arithmetic/Jamfile.v2 b/test/arithmetic/Jamfile.v2 index f7c54a523..232fe79fb 100644 --- a/test/arithmetic/Jamfile.v2 +++ b/test/arithmetic/Jamfile.v2 @@ -1,9 +1,9 @@ -# Boost.Geometry (aka GGL, Generic Geometry Library) +# Boost.Geometry # -# Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -# Copyright (c) 2008-2015 Bruno Lalande, Paris, France. -# Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -# Copyright (c) 2015-2017 Adam Wulkiewicz, Lodz, Poland. +# Copyright (c) 2007-2019 Barend Gehrels, Amsterdam, the Netherlands. +# Copyright (c) 2008-2019 Bruno Lalande, Paris, France. +# Copyright (c) 2009-2019 Mateusz Loskot, London, UK. +# Copyright (c) 2015-2019 Adam Wulkiewicz, Lodz, Poland. # # Use, modification and distribution is subject to the Boost Software License, # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -11,9 +11,9 @@ test-suite boost-geometry-arithmetic : - [ run general.cpp : : : : arithmetic_general ] - [ run dot_product.cpp : : : : arithmetic_dot_product ] - [ run cross_product.cpp : : : : arithmetic_cross_product ] - [ run general_form.cpp : : : : arithmetic_general_form ] - [ compile-fail cross_product.cpp : TEST_FAIL_CROSS_PRODUCT : arithmetic_cross_product_cf ] + [ run general.cpp : : : : arithmetic_general ] + [ run dot_product.cpp : : : : arithmetic_dot_product ] + [ run cross_product.cpp : : : : arithmetic_cross_product ] + [ run infinite_line_functions.cpp : : : : arithmetic_infinite_line_functions ] + [ compile-fail cross_product.cpp : TEST_FAIL_CROSS_PRODUCT : arithmetic_cross_product_cf ] ; diff --git a/test/arithmetic/general_form.cpp b/test/arithmetic/general_form.cpp deleted file mode 100755 index 744b7d231..000000000 --- a/test/arithmetic/general_form.cpp +++ /dev/null @@ -1,236 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) -// Unit Test - -// Copyright (c) 2018-2019 Barend Gehrels, Amsterdam, the Netherlands. - -// Use, modification and distribution is subject to the Boost Software License, -// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include - -#include -#include -#include - -namespace -{ - // Boost.Test does not support BOOST_CHECK_CLOSE for integral types - template - bool is_small(T const& value) - { - static long double const epsilon = 1.0e-5; - return std::fabs(value) < epsilon; - } -} - -template -void verify_point_on_line(bg::arithmetic::general_form const& f, - C const& x, C const& y) -{ - BOOST_CHECK_MESSAGE(is_small(f.a * x + f.b * y + f.c), - "Point is not located on the line"); -} - -template -void test_construct_line() -{ - typedef bg::arithmetic::general_form gf; - - // Horizontal through origin - gf f = bg::arithmetic::construct_line(0, 0, 10, 0); - verify_point_on_line(f, 0, 0); - verify_point_on_line(f, 10, 0); - - // Horizontal line above origin - f = bg::arithmetic::construct_line(0, 5, 10, 5); - verify_point_on_line(f, 0, 5); - verify_point_on_line(f, 10, 5); - - // Vertical through origin - f = bg::arithmetic::construct_line(0, 0, 0, 10); - verify_point_on_line(f, 0, 0); - verify_point_on_line(f, 0, 10); - - // Vertical line left from origin - f = bg::arithmetic::construct_line(5, 0, 5, 10); - verify_point_on_line(f, 5, 0); - verify_point_on_line(f, 5, 10); - - // Diagonal through origin - f = bg::arithmetic::construct_line(0, 0, 8, 10); - verify_point_on_line(f, 0, 0); - verify_point_on_line(f, 8, 10); - - // Diagonal not through origin - f = bg::arithmetic::construct_line(5, 2, -8, 10); - verify_point_on_line(f, 5, 2); - verify_point_on_line(f, -8, 10); -} - -template -void test_side_value() -{ - typedef bg::arithmetic::general_form gf; - - // Horizontal line going right - gf f = bg::arithmetic::construct_line(0, 0, 10, 0); - - // Point above (= on left side) - T d = bg::arithmetic::side_value(f, 5, 5); - BOOST_CHECK_MESSAGE(d > 0, "point not on left side"); - - // Point below (= on right side) - d = bg::arithmetic::side_value(f, 5, -5); - BOOST_CHECK_MESSAGE(d < 0, "point not on right side"); - - // Diagonal not through origin, from right (down) to left (up) - f = bg::arithmetic::construct_line(5, 2, -7, 10); - d = bg::arithmetic::side_value(f, 5, 2); - BOOST_CHECK_MESSAGE(is_small(d), "point not on line"); - d = bg::arithmetic::side_value(f, -7, 10); - BOOST_CHECK_MESSAGE(is_small(d), "point not on line"); - - // vector is (-12, 8), move (-3,2) on the line from (5,2) - d = bg::arithmetic::side_value(f, 2, 4); - BOOST_CHECK_MESSAGE(is_small(d), "point not on line"); - - // Go perpendicular (2,3) from (2,4) up, so right of the line (negative) - d = bg::arithmetic::side_value(f, 4, 7); - BOOST_CHECK_MESSAGE(d < 0, "point not on right side"); - - // Go perpendicular (2,3) from (2,4) down, so left of the line (positive) - d = bg::arithmetic::side_value(f, 0, 1); - BOOST_CHECK_MESSAGE(d > 0, "point not on left side"); -} - - -template -void test_get_intersection() -{ - typedef bg::arithmetic::general_form gf; - - // Diagonal lines (first is same as in distance measure, - // second is perpendicular and used there for distance measures) - gf p = bg::arithmetic::construct_line(5, 2, -7, 10); - gf q = bg::arithmetic::construct_line(4, 7, 0, 1); - - typedef bg::model::point point_type; - point_type ip; - BOOST_CHECK(bg::arithmetic::get_intersection(ip, p, q)); - - BOOST_CHECK_MESSAGE(is_small(bg::get<0>(ip) - 2), "x-coordinate wrong"); - BOOST_CHECK_MESSAGE(is_small(bg::get<1>(ip) - 4), "y-coordinate wrong"); - - verify_point_on_line(p, bg::get<0>(ip), bg::get<1>(ip)); - verify_point_on_line(q, bg::get<0>(ip), bg::get<1>(ip)); -} - -template -void test_same_direction() -{ - bg::arithmetic::general_form p, q; - - // Exactly opposite, diagonal - p = bg::arithmetic::construct_line(2, 1, 12, 11); - q = bg::arithmetic::construct_line(12, 11, 2, 1); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Exactly opposite, horizontal - p = bg::arithmetic::construct_line(0, 0, 10, 0); - q = bg::arithmetic::construct_line(10, 0, 0, 0); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Exactly opposite, vertical - p = bg::arithmetic::construct_line(0, 0, 0, 10); - q = bg::arithmetic::construct_line(0, 10, 0, 0); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Exactly equal, diagonal - p = bg::arithmetic::construct_line(0, 0, 10, 10); - q = bg::arithmetic::construct_line(0, 0, 10, 10); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // Exactly equal, horizontal - p = bg::arithmetic::construct_line(0, 0, 10, 0); - q = bg::arithmetic::construct_line(0, 0, 10, 0); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // Exactly equal, vertical - p = bg::arithmetic::construct_line(0, 0, 0, 10); - q = bg::arithmetic::construct_line(0, 0, 0, 10); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // Coming together, diagonal - p = bg::arithmetic::construct_line(0, 0, 10, 10); - q = bg::arithmetic::construct_line(20, 20, 10, 10); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Leaving from common point, diagonal - p = bg::arithmetic::construct_line(10, 10, 0, 0); - q = bg::arithmetic::construct_line(0, 0, 10, 10); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // Continuing each other, diagonal - p = bg::arithmetic::construct_line(0, 0, 10, 10); - q = bg::arithmetic::construct_line(10, 10, 20, 20); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // (Nearly) perpendicular - p = bg::arithmetic::construct_line(0, 0, 10, 10); - q = bg::arithmetic::construct_line(0, 0, -10, 10); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // 45 deg - p = bg::arithmetic::construct_line(0, 0, 10, 10); - q = bg::arithmetic::construct_line(0, 0, 0, 10); - BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); - - // a bit more than 45 deg - p = bg::arithmetic::construct_line(0, 0, 10, 10); - q = bg::arithmetic::construct_line(0, 0, -1, 10); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); - - // 135 deg - p = bg::arithmetic::construct_line(0, 0, 10, 10); - q = bg::arithmetic::construct_line(0, 0, -10, 0); - BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); -} - -template -void test_degenerate() -{ - typedef bg::arithmetic::general_form gf; - - gf f = bg::arithmetic::construct_line(0, 0, 10, 0); - BOOST_CHECK(! bg::arithmetic::is_degenerate(f)); - - f = bg::arithmetic::construct_line(0, 0, 0, 10); - BOOST_CHECK(! bg::arithmetic::is_degenerate(f)); - - f = bg::arithmetic::construct_line(0, 0, 10, 10); - BOOST_CHECK(! bg::arithmetic::is_degenerate(f)); - - f = bg::arithmetic::construct_line(0, 0, 0, 0); - BOOST_CHECK(bg::arithmetic::is_degenerate(f)); -} - - -template -void test_all() -{ - test_construct_line(); - test_side_value(); - test_get_intersection(); - test_same_direction(); - test_degenerate(); -} - -int test_main(int, char* []) -{ - test_all(); - test_all(); - test_all(); - test_all(); - return 0; -} diff --git a/test/arithmetic/infinite_line_functions.cpp b/test/arithmetic/infinite_line_functions.cpp new file mode 100755 index 000000000..eb2284908 --- /dev/null +++ b/test/arithmetic/infinite_line_functions.cpp @@ -0,0 +1,201 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2018-2019 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include +#include + +namespace +{ + // Boost.Test does not support BOOST_CHECK_CLOSE for integral types + template + bool is_small(T const& value) + { + static long double const epsilon = 1.0e-5; + return std::fabs(value) < epsilon; + } +} + +template +void verify_point_on_line(bg::model::infinite_line const& line, + C const& x, C const& y) +{ + BOOST_CHECK_MESSAGE(is_small(line.a * x + line.b * y + line.c), + "Point is not located on the line"); +} + +template +void test_side_value() +{ + typedef bg::model::infinite_line line_type; + + // Horizontal line going right + line_type line = bg::detail::make::make_infinite_line(0, 0, 10, 0); + + // Point above (= on left side) + T d = bg::arithmetic::side_value(line, 5, 5); + BOOST_CHECK_MESSAGE(d > 0, "point not on left side"); + + // Point below (= on right side) + d = bg::arithmetic::side_value(line, 5, -5); + BOOST_CHECK_MESSAGE(d < 0, "point not on right side"); + + // Diagonal not through origin, from right (down) to left (up) + line = bg::detail::make::make_infinite_line(5, 2, -7, 10); + d = bg::arithmetic::side_value(line, 5, 2); + BOOST_CHECK_MESSAGE(is_small(d), "point not on line"); + d = bg::arithmetic::side_value(line, -7, 10); + BOOST_CHECK_MESSAGE(is_small(d), "point not on line"); + + // vector is (-12, 8), move (-3,2) on the line from (5,2) + d = bg::arithmetic::side_value(line, 2, 4); + BOOST_CHECK_MESSAGE(is_small(d), "point not on line"); + + // Go perpendicular (2,3) from (2,4) up, so right of the line (negative) + d = bg::arithmetic::side_value(line, 4, 7); + BOOST_CHECK_MESSAGE(d < 0, "point not on right side"); + + // Go perpendicular (2,3) from (2,4) down, so left of the line (positive) + d = bg::arithmetic::side_value(line, 0, 1); + BOOST_CHECK_MESSAGE(d > 0, "point not on left side"); +} + + +template +void test_get_intersection() +{ + typedef bg::model::infinite_line line_type; + + // Diagonal lines (first is same as in distance measure, + // second is perpendicular and used there for distance measures) + line_type p = bg::detail::make::make_infinite_line(5, 2, -7, 10); + line_type q = bg::detail::make::make_infinite_line(4, 7, 0, 1); + + typedef bg::model::point point_type; + point_type ip; + BOOST_CHECK(bg::arithmetic::intersection_point(p, q, ip)); + + BOOST_CHECK_MESSAGE(is_small(bg::get<0>(ip) - 2), "x-coordinate wrong"); + BOOST_CHECK_MESSAGE(is_small(bg::get<1>(ip) - 4), "y-coordinate wrong"); + + verify_point_on_line(p, bg::get<0>(ip), bg::get<1>(ip)); + verify_point_on_line(q, bg::get<0>(ip), bg::get<1>(ip)); +} + +template +void test_same_direction() +{ + bg::model::infinite_line p, q; + + // Exactly opposite, diagonal + p = bg::detail::make::make_infinite_line(2, 1, 12, 11); + q = bg::detail::make::make_infinite_line(12, 11, 2, 1); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Exactly opposite, horizontal + p = bg::detail::make::make_infinite_line(0, 0, 10, 0); + q = bg::detail::make::make_infinite_line(10, 0, 0, 0); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Exactly opposite, vertical + p = bg::detail::make::make_infinite_line(0, 0, 0, 10); + q = bg::detail::make::make_infinite_line(0, 10, 0, 0); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Exactly equal, diagonal + p = bg::detail::make::make_infinite_line(0, 0, 10, 10); + q = bg::detail::make::make_infinite_line(0, 0, 10, 10); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // Exactly equal, horizontal + p = bg::detail::make::make_infinite_line(0, 0, 10, 0); + q = bg::detail::make::make_infinite_line(0, 0, 10, 0); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // Exactly equal, vertical + p = bg::detail::make::make_infinite_line(0, 0, 0, 10); + q = bg::detail::make::make_infinite_line(0, 0, 0, 10); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // Coming together, diagonal + p = bg::detail::make::make_infinite_line(0, 0, 10, 10); + q = bg::detail::make::make_infinite_line(20, 20, 10, 10); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Leaving from common point, diagonal + p = bg::detail::make::make_infinite_line(10, 10, 0, 0); + q = bg::detail::make::make_infinite_line(0, 0, 10, 10); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // Continuing each other, diagonal + p = bg::detail::make::make_infinite_line(0, 0, 10, 10); + q = bg::detail::make::make_infinite_line(10, 10, 20, 20); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // (Nearly) perpendicular + p = bg::detail::make::make_infinite_line(0, 0, 10, 10); + q = bg::detail::make::make_infinite_line(0, 0, -10, 10); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // 45 deg + p = bg::detail::make::make_infinite_line(0, 0, 10, 10); + q = bg::detail::make::make_infinite_line(0, 0, 0, 10); + BOOST_CHECK(bg::arithmetic::similar_direction(p, q)); + + // a bit more than 45 deg + p = bg::detail::make::make_infinite_line(0, 0, 10, 10); + q = bg::detail::make::make_infinite_line(0, 0, -1, 10); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); + + // 135 deg + p = bg::detail::make::make_infinite_line(0, 0, 10, 10); + q = bg::detail::make::make_infinite_line(0, 0, -10, 0); + BOOST_CHECK(! bg::arithmetic::similar_direction(p, q)); +} + +template +void test_degenerate() +{ + typedef bg::model::infinite_line line_type; + + line_type line = bg::detail::make::make_infinite_line(0, 0, 10, 0); + BOOST_CHECK(! bg::arithmetic::is_degenerate(line)); + + line = bg::detail::make::make_infinite_line(0, 0, 0, 10); + BOOST_CHECK(! bg::arithmetic::is_degenerate(line)); + + line = bg::detail::make::make_infinite_line(0, 0, 10, 10); + BOOST_CHECK(! bg::arithmetic::is_degenerate(line)); + + line = bg::detail::make::make_infinite_line(0, 0, 0, 0); + BOOST_CHECK(bg::arithmetic::is_degenerate(line)); +} + + +template +void test_all() +{ + test_side_value(); + test_get_intersection(); + test_same_direction(); + test_degenerate(); +} + +int test_main(int, char* []) +{ + test_all(); + test_all(); + test_all(); + test_all(); + return 0; +} diff --git a/test/geometries/Jamfile.v2 b/test/geometries/Jamfile.v2 index c6b360f7a..15f8c60c5 100644 --- a/test/geometries/Jamfile.v2 +++ b/test/geometries/Jamfile.v2 @@ -1,9 +1,9 @@ -# Boost.Geometry (aka GGL, Generic Geometry Library) +# Boost.Geometry # -# Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -# Copyright (c) 2008-2015 Bruno Lalande, Paris, France. -# Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -# Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. +# Copyright (c) 2007-2019 Barend Gehrels, Amsterdam, the Netherlands. +# Copyright (c) 2008-2019 Bruno Lalande, Paris, France. +# Copyright (c) 2009-2019 Mateusz Loskot, London, UK. +# Copyright (c) 2014-2019 Adam Wulkiewicz, Lodz, Poland. # Use, modification and distribution is subject to the Boost Software License, # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -27,4 +27,5 @@ test-suite boost-geometry-geometries [ run custom_linestring.cpp : : : : geometries_custom_linestring ] [ run initialization.cpp : : : : geometries_initialization ] [ run segment.cpp : : : : geometries_segment ] + [ run infinite_line.cpp : : : : geometries_infinite_line ] ; diff --git a/test/geometries/infinite_line.cpp b/test/geometries/infinite_line.cpp new file mode 100755 index 000000000..1968d6f8a --- /dev/null +++ b/test/geometries/infinite_line.cpp @@ -0,0 +1,85 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2019 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include + +namespace +{ + // Boost.Test does not support BOOST_CHECK_CLOSE for integral types + template + bool is_small(T const& value) + { + static long double const epsilon = 1.0e-5; + return std::fabs(value) < epsilon; + } +} + +template +void verify_point_on_line(bg::model::infinite_line const& line, + C const& x, C const& y) +{ + BOOST_CHECK_MESSAGE(is_small(line.a * x + line.b * y + line.c), + "Point is not located on the line"); +} + +template +void test_make() +{ + typedef bg::model::infinite_line line_type; + + // Horizontal through origin + line_type line = bg::detail::make::make_infinite_line(0, 0, 10, 0); + verify_point_on_line(line, 0, 0); + verify_point_on_line(line, 10, 0); + + // Horizontal line above origin + line = bg::detail::make::make_infinite_line(0, 5, 10, 5); + verify_point_on_line(line, 0, 5); + verify_point_on_line(line, 10, 5); + + // Vertical through origin + line = bg::detail::make::make_infinite_line(0, 0, 0, 10); + verify_point_on_line(line, 0, 0); + verify_point_on_line(line, 0, 10); + + // Vertical line left from origin + line = bg::detail::make::make_infinite_line(5, 0, 5, 10); + verify_point_on_line(line, 5, 0); + verify_point_on_line(line, 5, 10); + + // Diagonal through origin + line = bg::detail::make::make_infinite_line(0, 0, 8, 10); + verify_point_on_line(line, 0, 0); + verify_point_on_line(line, 8, 10); + + // Diagonal not through origin + line = bg::detail::make::make_infinite_line(5, 2, -8, 10); + verify_point_on_line(line, 5, 2); + verify_point_on_line(line, -8, 10); +} + + +template +void test_all() +{ + test_make(); +} + +int test_main(int, char* []) +{ + test_all(); + test_all(); + test_all(); + test_all(); + return 0; +} From 8c13a57f097be082c91fbfdec5c903335587db08 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Jun 2019 15:40:35 +0200 Subject: [PATCH 21/49] [test] Changes in difference unit test --- .../set_operations/difference/difference.cpp | 105 ++++++++++-------- test/geometry_test_common.hpp | 6 + 2 files changed, 63 insertions(+), 48 deletions(-) diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index 88fb141b4..124bfd1e7 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -308,19 +308,19 @@ void test_all() settings); } -#if defined(BOOST_GEOMETRY_TEST_FAILURES) - test_one("geos_1", - geos_1[0], geos_1[1], - 21, -1, 0.31640625, - 9, -1, 0.01953125); +#if ! defined(BOOST_GEOMETRY_USE_RESCALING) + { + ut_settings settings; + settings.percentage = 0.01; + settings.test_validity = false; - // Excluded this test in the normal suite, it is OK like this for many clang/gcc/msvc - // versions, but NOT OK for many other clang/gcc/msvc versions on other platforms - // It might depend on partition (order) - // 10, -1, 0.02148439); // change in partition might give these results - - // SQL Server gives: 0.28937764436705 and 0.000786406897532288 with 44/35 rings - // PostGIS gives: 0.30859375 and 0.033203125 with 35/35 rings + // SQL Server gives: 0.28937764436705 and 0.000786406897532288 with 44/35 rings + // PostGIS gives: 0.30859375 and 0.033203125 with 35/35 rings + TEST_DIFFERENCE_WITH(geos_1, + -1, BG_IF_KRAMER(0.29171, 0.1898158), + -1, BG_IF_KRAMER(0.00076855, 0.000052889), + -1); + } #endif { @@ -367,25 +367,25 @@ void test_all() if ( BOOST_GEOMETRY_CONDITION((! boost::is_same::value)) ) { - test_one("ggl_list_20110716_enrico", - ggl_list_20110716_enrico[0], ggl_list_20110716_enrico[1], - 3, -1, 35723.8506317139, - 1, -1, 58456.4964294434, - 1, -1, 35723.8506317139 + 58456.4964294434); + TEST_DIFFERENCE(ggl_list_20110716_enrico, + 3, 35723.8506317139, // TODO FOR GENERIC, misses one of three outputs + 1, 58456.4964294434, + 1); } -#if defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES) +#if defined(BOOST_GEOMETRY_USE_RESCALING) \ + || ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) \ + || defined(BOOST_GEOMETRY_TEST_FAILURES) { - // symmetric difference is not valid due to robustness issue, it has - // two turns (touch_only) and a midpoint is located in other polygon - ut_settings ignore_validity; - ignore_validity.test_validity = false; + // Symmetric difference should output one polygon + // Using rescaling, it currently outputs two. + ut_settings settings; + settings.test_validity = false; - test_one("ggl_list_20110820_christophe", - ggl_list_20110820_christophe[0], ggl_list_20110820_christophe[1], - 1, -1, 2.8570121719168924, - 1, -1, 64.498061986388564, - ignore_validity); + TEST_DIFFERENCE_WITH(ggl_list_20110820_christophe, + 1, 2.8570121719168924, + 1, 64.498061986388564, + BG_IF_RESCALED(2, 1)); } #endif @@ -415,7 +415,10 @@ void test_all() // With rescaling, difference of output a-b and a sym b is invalid ut_settings settings; settings.test_validity = BG_IF_RESCALED(false, true); - TEST_DIFFERENCE_WITH(ggl_list_20190307_matthieu_1, 2, 0.18461532, 2, 0.617978, 4); + TEST_DIFFERENCE_WITH(ggl_list_20190307_matthieu_1, + BG_IF_KRAMER(2, 1), 0.18461532, + BG_IF_KRAMER(2, 1), 0.617978, + BG_IF_KRAMER(4, 2)); TEST_DIFFERENCE_WITH(ggl_list_20190307_matthieu_2, 2, 12.357152, 0, 0.0, 2); } @@ -443,12 +446,19 @@ void test_all() 2, 12, 0.0451236449624935, 0, 0, 0); -#if defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES) - test_one("ticket_9563", - ticket_9563[0], ticket_9563[1], - 0, -1, 0, - 6, -1, 20.096189); + { + ut_settings settings; + settings.test_validity = BG_IF_RESCALED(true, false); +#if !defined(BOOST_GEOMETRY_USE_RESCALING) && defined(BOOST_GEOMETRY_USE_KRAMER_RULE) + const int expected_count = 1; // Wrong, considers all consecutive polygons as one +#else + const int expected_count = 6; #endif + TEST_DIFFERENCE_WITH(ticket_9563, + 0, 0, + expected_count, 20.096189, + expected_count); + } test_one("ticket_10108_a", ticket_10108_a[0], ticket_10108_a[1], @@ -475,7 +485,7 @@ void test_all() 2, 23, 62.25, 0, 0, 0.0); - // Other combi's + // Other combinations { test_one( "star_ring_ring", example_star, example_ring, @@ -536,30 +546,29 @@ void test_all() } // Rescaling generates a very small false polygon - TEST_DIFFERENCE(issue_566_a, 1, 143.662, BG_IF_RESCALED(1, 0), - BG_IF_RESCALED(1.605078e-6, 0.0), - BG_IF_RESCALED(2, 1)); + { + ut_settings settings; +#if defined(BOOST_GEOMETRY_USE_KRAMER_RULE) + settings.test_validity = BG_IF_RESCALED(true, false); +#endif + TEST_DIFFERENCE_WITH(issue_566_a, 1, 143.662, BG_IF_RESCALED(1, 0), + BG_IF_RESCALED(1.605078e-6, 0.0), + BG_IF_RESCALED(2, 1)); + } TEST_DIFFERENCE(issue_566_b, 1, 143.662, BG_IF_RESCALED(1, 0), BG_IF_RESCALED(1.605078e-6, 0.0), BG_IF_RESCALED(2, 1)); - /*** - Experimental (cut), does not work: - test_one( - "polygon_pseudo_line", - "POLYGON((0 0,0 4,4 4,4 0,0 0))", - "POLYGON((2 -2,2 -1,2 6,2 -2))", - 5, 22, 1.1901714, - 5, 27, 1.6701714); - ***/ - TEST_DIFFERENCE(mysql_21977775, 2, 160.856568913, 2, 92.3565689126, 4); TEST_DIFFERENCE(mysql_21965285, 1, 92.0, 1, 14.0, 1); TEST_DIFFERENCE(mysql_23023665_1, 1, 92.0, 1, 142.5, 2); TEST_DIFFERENCE(mysql_23023665_2, 1, 96.0, 1, 16.0, 2); TEST_DIFFERENCE(mysql_23023665_3, 1, 225.0, 1, 66.0, 2); TEST_DIFFERENCE(mysql_23023665_5, 2, 165.23735, 2, 105.73735, 4); -#if defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES) +#if defined(BOOST_GEOMETRY_USE_RESCALING) \ + || ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) \ + || defined(BOOST_GEOMETRY_TEST_FAILURES) + // Testcases going wrong with Kramer's rule and no rescaling TEST_DIFFERENCE(mysql_23023665_6, 2, 105.68756, 3, 10.18756, 5); TEST_DIFFERENCE(mysql_23023665_13, 3, 99.74526, 3, 37.74526, 6); #endif diff --git a/test/geometry_test_common.hpp b/test/geometry_test_common.hpp index 41c5f665d..1696d0f15 100644 --- a/test/geometry_test_common.hpp +++ b/test/geometry_test_common.hpp @@ -167,4 +167,10 @@ struct mathematical_policy #define BG_IF_RESCALED(a, b) b #endif +#if defined(BOOST_GEOMETRY_USE_KRAMER_RULE) +#define BG_IF_KRAMER(a, b) a +#else +#define BG_IF_KRAMER(a, b) b +#endif + #endif // GEOMETRY_TEST_GEOMETRY_TEST_COMMON_HPP From df7137e76ddf75a53e7da6172d9690decdc9427f Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 12 Jun 2019 14:43:32 +0200 Subject: [PATCH 22/49] [test] Changes in difference multi unit test --- .../difference/difference_multi.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 4a25ac433..54e14b14f 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -157,23 +157,25 @@ void test_areal() // output is discarded because of zero (rescaled) area // POSTGIS areas: 3.75893745345145, 2.5810000723917e-15 ut_settings settings; - settings.sym_difference = false; // Validity problem in sym difference -#if defined(BOOST_GEOMETRY_USE_RESCALING) - settings.test_validity = false; // Invalid output in A - TEST_DIFFERENCE_WITH(0, 1, bug_21155501, 1, 3.758937, 0, 0.0, 2); + settings.sym_difference = BG_IF_RESCALED(false, true); + settings.test_validity = BG_IF_RESCALED(false, true); +#if defined(BOOST_GEOMETRY_USE_RESCALING) || ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) + // No output for B + TEST_DIFFERENCE_WITH(0, 1, bug_21155501, 1, 3.758937, 0, 0.0, 1); #else + // Very small sliver for B TEST_DIFFERENCE_WITH(0, 1, bug_21155501, 1, 3.758937, 1, 1.7763568394002505e-15, 2); #endif } #if defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES) { - // Without rescaling, this one misses one of the output polygons // With rescaling, it is complete but invalid ut_settings settings; settings.percentage = 0.001; - settings.test_validity = false; - TEST_DIFFERENCE_WITH(0, 1, ticket_9081, 2, 0.0907392476356186, 4, 0.126018011439877, 4); + settings.test_validity = BG_IF_RESCALED(false, true); + TEST_DIFFERENCE_WITH(0, 1, ticket_9081, 2, 0.0907392476356186, + 4, 0.126018011439877, BG_IF_RESCALED(4, 3)); } #endif @@ -317,7 +319,7 @@ void test_areal() TEST_DIFFERENCE(case_recursive_boxes_60, 6, 5.25, 7, 5.25, 11); TEST_DIFFERENCE(case_recursive_boxes_61, 2, 1.5, 6, 2.0, 7); #if defined(BOOST_GEOMETRY_TEST_FAILURES) - // Misses one triangle + // Misses one triangle. It is NOT related to rescaling. TEST_DIFFERENCE(case_recursive_boxes_62, 5, 5.0, 11, 5.75, 12); #endif From 33ea069ac066c702d5b3cc951ec20c1c5555e989 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 17 Jul 2019 12:10:36 +0200 Subject: [PATCH 23/49] [TEST] Test updates, most importantly the check on validity which now only reports if input is considered as valid. --- test/algorithms/check_validity.hpp | 79 +++++++++++++++++++ test/algorithms/is_valid.cpp | 9 ++- test/algorithms/overlay/overlay.cpp | 8 +- test/algorithms/overlay/overlay_cases.hpp | 24 ++++++ .../set_operations/check_validity.hpp | 50 ------------ .../set_operations/difference/difference.cpp | 51 ++++++++++-- .../difference/test_difference.hpp | 4 +- .../intersection/intersection.cpp | 43 +++++----- .../intersection/test_intersection.hpp | 28 +++---- .../set_operations/union/test_union.hpp | 4 +- .../algorithms/set_operations/union/union.cpp | 11 ++- 11 files changed, 214 insertions(+), 97 deletions(-) create mode 100644 test/algorithms/check_validity.hpp delete mode 100644 test/algorithms/set_operations/check_validity.hpp diff --git a/test/algorithms/check_validity.hpp b/test/algorithms/check_validity.hpp new file mode 100644 index 000000000..388ca6fad --- /dev/null +++ b/test/algorithms/check_validity.hpp @@ -0,0 +1,79 @@ +// Boost.Geometry + +// Copyright (c) 2017 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_TEST_CHECK_VALIDITY_HPP +#define BOOST_GEOMETRY_TEST_CHECK_VALIDITY_HPP + +#include + +#include + +template +inline bool is_output_valid(Geometry const& geometry, + std::string const& case_id, + G1 const& g1, G2 const& g2, + std::string& message) +{ + bool const result = bg::is_valid(geometry, message); + if (! result) + { + // Check if input was valid. If not, do not report output validity + if (! bg::is_valid(g1) || ! bg::is_valid(g2)) + { + std::cout << "WARNING: Input is not considered as valid; " + << "this can cause that output is invalid: " << case_id + << std::endl; + return true; + } + } + return result; +} + +template +< + typename Geometry, + typename Tag = typename bg::tag::type +> +struct check_validity +{ + template + static inline + bool apply(Geometry const& geometry, + std::string const& case_id, + G1 const& g1, G2 const& g2, + std::string& message) + { + return is_output_valid(geometry, case_id, g1, g2, message); + } +}; + +// Specialization for vector of (e.g. rings) +template +struct check_validity +{ + template + static inline + bool apply(Geometry const& geometry, + std::string const& case_id, + G1 const& g1, G2 const& g2, + std::string& message) + { + typedef typename boost::range_value::type single_type; + BOOST_FOREACH(single_type const& element, geometry) + { + if (! is_output_valid(element, case_id, g1, g2, message)) + { + return false; + } + } + return true; + } +}; + + +#endif // BOOST_GEOMETRY_TEST_CHECK_VALIDITY_HPP diff --git a/test/algorithms/is_valid.cpp b/test/algorithms/is_valid.cpp index 6b4b164a4..9c9ca4dde 100644 --- a/test/algorithms/is_valid.cpp +++ b/test/algorithms/is_valid.cpp @@ -19,6 +19,7 @@ #include #include "test_is_valid.hpp" +#include "overlay/overlay_cases.hpp" #include @@ -784,6 +785,7 @@ inline void test_open_polygons() false); } + template inline void test_doc_example_polygon() { @@ -794,13 +796,16 @@ inline void test_doc_example_polygon() std::cout << "************************************" << std::endl; #endif - typedef bg::model::polygon CCW_CG; + typedef bg::model::polygon ClockwiseClosedPolygon; typedef validity_tester_areal tester; - typedef test_valid test; + typedef test_valid test; test::apply("pg-doc", "POLYGON((0 0,0 10,10 10,10 0,0 0),(0 0,9 1,9 2,0 0),(0 0,2 9,1 9,0 0),(2 9,9 2,9 9,2 9))", false); + + // Containing a self touching point, which should be valid + test::apply("ggl_list_20190307_matthieu_2", ggl_list_20190307_matthieu_2[1], true); } BOOST_AUTO_TEST_CASE( test_is_valid_polygon ) diff --git a/test/algorithms/overlay/overlay.cpp b/test/algorithms/overlay/overlay.cpp index ea9786b12..f89bfabff 100644 --- a/test/algorithms/overlay/overlay.cpp +++ b/test/algorithms/overlay/overlay.cpp @@ -25,7 +25,7 @@ #endif #include - +#include #include #include @@ -433,6 +433,12 @@ void test_overlay(std::string const& caseid, overlay::apply(g1, g2, robust_policy, std::back_inserter(result), strategy, visitor); + std::string message; + bool const valid = check_validity::apply(result, caseid, g1, g2, message); + BOOST_CHECK_MESSAGE(valid, + "overlay: " << caseid << " not valid: " << message + << " type: " << (type_for_assert_message())); + BOOST_CHECK_CLOSE(bg::area(result), expected_area, 0.001); BOOST_CHECK_MESSAGE((bg::num_interior_rings(result) == expected_hole_count), caseid diff --git a/test/algorithms/overlay/overlay_cases.hpp b/test/algorithms/overlay/overlay_cases.hpp index d5373f065..65bea7fbb 100644 --- a/test/algorithms/overlay/overlay_cases.hpp +++ b/test/algorithms/overlay/overlay_cases.hpp @@ -838,6 +838,30 @@ static std::string case_precision_22[2] = "POLYGON((-1 -1,-1 8,8 8,8 -1,-1 -1),(2 7,2 3,4.00000000200000017 2.99999999000000006,4 7,2 7))" }; +static std::string case_precision_23[2] = +{ + "POLYGON((0 0,0 4,2 4,2 3,4 3,4 0,0 0))", + "POLYGON((-1 -1,-1 8,8 8,8 -1,-1 -1),(2 7,2 3,3.99998999999999993 2.99998999999999993,4 7,2 7))" +}; + +static std::string case_precision_24[2] = +{ + "POLYGON((0 0,0 4,2 4,2 3,4 3,4 0,0 0))", + "POLYGON((2 7,4 7,4 3.000001,2 3,2 7))" +}; + +static std::string case_precision_25[2] = +{ + "POLYGON((0 0,0 4,2 4,2 3,4 3,4 0,0 0))", + "POLYGON((2 7,4 7,4 3.00001,2 3,2 7))" +}; + +static std::string case_precision_26[2] = +{ + "POLYGON((0 0,0 4,2 4,2 3,4 3,4 0,0 0))", + "POLYGON((-1 -1,-1 8,8 8,8 -1,-1 -1),(2 7,2 3,3.999991 2.999991,4 7,2 7))" +}; + // ticket_17 is keyholed, so has a hole formed by an deliberate intersection // This will fail the intersection/traversal process diff --git a/test/algorithms/set_operations/check_validity.hpp b/test/algorithms/set_operations/check_validity.hpp deleted file mode 100644 index 3e81538dc..000000000 --- a/test/algorithms/set_operations/check_validity.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// Boost.Geometry - -// Copyright (c) 2017 Barend Gehrels, Amsterdam, the Netherlands. - -// Use, modification and distribution is subject to the Boost Software License, -// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_GEOMETRY_TEST_SETOP_CHECK_VALIDITY_HPP -#define BOOST_GEOMETRY_TEST_SETOP_CHECK_VALIDITY_HPP - -#include - -#include - -template -< - typename Geometry, - typename Tag = typename bg::tag::type -> -struct check_validity -{ - static inline - bool apply(Geometry const& geometry, std::string& message) - { - return bg::is_valid(geometry, message); - } -}; - -// Specialization for vector of (e.g. rings) -template -struct check_validity -{ - static inline - bool apply(Geometry const& geometry, std::string& message) - { - typedef typename boost::range_value::type single_type; - BOOST_FOREACH(single_type const& element, geometry) - { - if (! bg::is_valid(element, message)) - { - return false; - } - } - return true; - } -}; - - -#endif // BOOST_GEOMETRY_TEST_SETOP_CHECK_VALIDITY_HPP diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index 124bfd1e7..289667ba7 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -49,9 +49,7 @@ template void test_all() { - typedef bg::model::box

box; typedef bg::model::polygon

polygon; - typedef bg::model::ring

ring; typedef typename bg::coordinate_type

::type ct; @@ -246,6 +244,45 @@ void test_all() TEST_DIFFERENCE(case_106, 1, 17.5, 2, 32.5, 3); TEST_DIFFERENCE(case_107, 2, 18.0, 2, 29.0, 4); + TEST_DIFFERENCE(case_precision_1, 1, 14.0, 1, BG_IF_RESCALED(8.00001, 8.0), 1); + TEST_DIFFERENCE(case_precision_2, 1, 14.0, 1, 8.0, 1); + TEST_DIFFERENCE(case_precision_3, 1, 14.0, 1, 8.0, 1); + TEST_DIFFERENCE(case_precision_4, 1, 14.0, 1, 8.0, 1); +#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) + TEST_DIFFERENCE(case_precision_5, 1, 14.0, 1, 8.0, 1); + TEST_DIFFERENCE(case_precision_6, 0, 0.0, 1, 57.0, 1); +#endif + TEST_DIFFERENCE(case_precision_7, 1, 14.0, 1, 8.0, 1); + TEST_DIFFERENCE(case_precision_8, 0, 0.0, 1, 59.0, 1); +#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) + TEST_DIFFERENCE(case_precision_9, 0, 0.0, 1, 59.0, 1); + TEST_DIFFERENCE(case_precision_10, 0, 0.0, 1, 59.0, 1); +#endif + TEST_DIFFERENCE(case_precision_11, 0, 0.0, 1, 59.0, 1); + TEST_DIFFERENCE(case_precision_12, 1, 12.0, 0, 0.0, 1); + TEST_DIFFERENCE(case_precision_13, 1, BG_IF_RESCALED(12.00002, 12.0), 0, 0.0, 1); + TEST_DIFFERENCE(case_precision_14, 1, 14.0, 1, 8.0, 1); + TEST_DIFFERENCE(case_precision_15, 0, 0.0, 1, 59.0, 1); +#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) + TEST_DIFFERENCE(case_precision_16, 0, 0.0, 1, 59.0, 1); +#endif + TEST_DIFFERENCE(case_precision_17, 0, 0.0, 1, 59.0, 1); + TEST_DIFFERENCE(case_precision_18, 0, 0.0, 1, 59.0, 1); +#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) + TEST_DIFFERENCE(case_precision_19, 0, 0.0, 1, 59.0, 1); +#endif + TEST_DIFFERENCE(case_precision_20, 1, 14.0, 1, 8.0, 1); + TEST_DIFFERENCE(case_precision_21, 1, 14.0, 1, 7.99999, 1); + TEST_DIFFERENCE(case_precision_22, 0, 0.0, 1, 59.0, 1); +#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) + TEST_DIFFERENCE(case_precision_23, 0, 0.0, 1, 59.0, 1); +#endif + TEST_DIFFERENCE(case_precision_24, 1, 14.0, 1, 8.0, 1); + TEST_DIFFERENCE(case_precision_25, 1, 14.0, 1, 7.99999, 1); +#if ! defined(BOOST_GEOMETRY_USE_KRAMER_RULE) + TEST_DIFFERENCE(case_precision_26, 0, 0.0, 1, 59.0, 1); +#endif + test_one("winded", winded[0], winded[1], 3, 37, 61, @@ -317,8 +354,8 @@ void test_all() // SQL Server gives: 0.28937764436705 and 0.000786406897532288 with 44/35 rings // PostGIS gives: 0.30859375 and 0.033203125 with 35/35 rings TEST_DIFFERENCE_WITH(geos_1, - -1, BG_IF_KRAMER(0.29171, 0.1898158), - -1, BG_IF_KRAMER(0.00076855, 0.000052889), + -1, BG_IF_KRAMER(0.29171, 0.189697476), + -1, BG_IF_KRAMER(0.00076855, 0.000018266), -1); } #endif @@ -485,6 +522,10 @@ void test_all() 2, 23, 62.25, 0, 0, 0.0); +#if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE) + typedef bg::model::box

box; + typedef bg::model::ring

ring; + // Other combinations { test_one( @@ -544,6 +585,7 @@ void test_all() 3, -1, 8.53333333333, 2, -1, 0.53333333333); } +#endif // Rescaling generates a very small false polygon { @@ -601,7 +643,6 @@ void test_specific() TEST_DIFFERENCE(ticket_11676, 2, 2537992.5, 2, 294963.5, 3); } - int test_main(int, char* []) { test_all >(); diff --git a/test/algorithms/set_operations/difference/test_difference.hpp b/test/algorithms/set_operations/difference/test_difference.hpp index b1e479fcf..9baeb293b 100644 --- a/test/algorithms/set_operations/difference/test_difference.hpp +++ b/test/algorithms/set_operations/difference/test_difference.hpp @@ -19,6 +19,7 @@ #include #include +#include #include "../setop_output_type.hpp" #include @@ -194,8 +195,9 @@ std::string test_difference(std::string const& caseid, G1 const& g1, G2 const& g #endif { // std::cout << bg::dsv(result) << std::endl; + typedef bg::model::multi_polygon result_type; std::string message; - bool const valid = bg::is_valid(result, message); + bool const valid = check_validity::apply(result, caseid, g1, g2, message); BOOST_CHECK_MESSAGE(valid, "difference: " << caseid << " not valid " << message << " type: " << (type_for_assert_message())); diff --git a/test/algorithms/set_operations/intersection/intersection.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index e92575880..9ae6fff1d 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -188,10 +188,13 @@ void test_areal() settings); } - test_one("geos_1", - geos_1[0], geos_1[1], - 1, -1, 3461.12321694, // MSVC 14 reports 3461.025390625 - ut_settings(0.01, false)); + if (! BOOST_GEOMETRY_CONDITION((boost::is_same::value)) ) + { + test_one("geos_1", + geos_1[0], geos_1[1], + 1, -1, 3461.12321694, // MSVC 14 reports 3461.025390625 + ut_settings(0.01, false)); + } // Expectations: // In most cases: 0 (no intersection) @@ -205,7 +208,7 @@ void test_areal() 0, 0, 0.0); test_one("geos_4", geos_4[0], geos_4[1], - 1, -1, 0.08368849); + 1, -1, 0.08368849, ut_settings(0.01)); if ( BOOST_GEOMETRY_CONDITION(! ccw && open) ) @@ -253,15 +256,17 @@ void test_areal() TEST_INTERSECTION(ggl_list_20190307_matthieu_1, 2, -1, 0.035136); TEST_INTERSECTION(ggl_list_20190307_matthieu_2, 1, -1, 3.64285); +#if defined(BOOST_GEOMETRY_USE_RESCALING) || !defined(BOOST_GEOMETRY_USE_KRAMER_RULE) test_one("buffer_rt_f", buffer_rt_f[0], buffer_rt_f[1], 1, 4, 0.00029437899183903937, ut_settings(0.01)); +#endif test_one("buffer_rt_g", buffer_rt_g[0], buffer_rt_g[1], 1, 0, 2.914213562373); test_one("ticket_8254", ticket_8254[0], ticket_8254[1], - 1, 4, 3.635930e-08, ut_settings(0.01)); + if_typed(0, 1), -1, if_typed(0.0, 3.635930e-08), ut_settings(0.01)); test_one("ticket_6958", ticket_6958[0], ticket_6958[1], - 1, 4, 4.34355e-05, ut_settings(0.01)); + if_typed(0, 1), -1, if_typed(0.0, 4.34355e-05), ut_settings(0.01)); test_one("ticket_8652", ticket_8652[0], ticket_8652[1], 1, 4, 0.0003); @@ -306,7 +311,7 @@ void test_areal() test_one("ticket_11576", ticket_11576[0], ticket_11576[1], - 1, 0, 5.585617332907136e-07); + if_typed(0, 1), -1, if_typed(0.0, 5.585617332907136e-07)); { // Not yet valid when rescaling is turned off @@ -370,15 +375,8 @@ void test_areal() TEST_INTERSECTION(case_precision_9, 1, -1, 14.0); TEST_INTERSECTION(case_precision_10, 1, -1, 14.0); TEST_INTERSECTION(case_precision_11, 1, -1, 14.0); - - { - ut_settings settings(0.01); - TEST_INTERSECTION_WITH(case_precision_12, 0, 1, 1, -1, 2.0, settings); - TEST_INTERSECTION_WITH(case_precision_13, 0, 1, 1, -1, 2.0, settings); - TEST_INTERSECTION_WITH(case_precision_12, 1, 0, 1, -1, 2.0, settings); - TEST_INTERSECTION_WITH(case_precision_13, 1, 0, 1, -1, 2.0, settings); - } - + TEST_INTERSECTION(case_precision_12, 1, -1, 2.0); + TEST_INTERSECTION(case_precision_13, 1, -1, 1.99998); TEST_INTERSECTION(case_precision_14, 0, -1, 0.0); TEST_INTERSECTION(case_precision_15, 1, -1, 14.0); TEST_INTERSECTION(case_precision_16, 1, -1, 14.0); @@ -388,6 +386,10 @@ void test_areal() TEST_INTERSECTION(case_precision_20, 0, 0, 0.0); TEST_INTERSECTION(case_precision_21, 0, 0, 0.0); TEST_INTERSECTION(case_precision_22, 1, -1, 14.0); + TEST_INTERSECTION(case_precision_23, 1, -1, 14.0); + TEST_INTERSECTION(case_precision_24, 0, 0, 0.0); + TEST_INTERSECTION(case_precision_25, 0, 0, 0.0); + TEST_INTERSECTION(case_precision_26, 1, -1, 14.0); TEST_INTERSECTION_REV(case_precision_1, 0, 0, 0.0); TEST_INTERSECTION_REV(case_precision_2, 0, 0, 0.0); @@ -400,7 +402,8 @@ void test_areal() TEST_INTERSECTION_REV(case_precision_9, 1, -1, 14.0); TEST_INTERSECTION_REV(case_precision_10, 1, -1, 14.0); TEST_INTERSECTION_REV(case_precision_11, 1, -1, 14.0); - + TEST_INTERSECTION_REV(case_precision_12, 1, -1, 2.0); + TEST_INTERSECTION_REV(case_precision_13, 1, -1, 1.99998); TEST_INTERSECTION_REV(case_precision_14, 0, -1, 0.0); TEST_INTERSECTION_REV(case_precision_15, 1, -1, 14.0); TEST_INTERSECTION_REV(case_precision_16, 1, -1, 14.0); @@ -410,6 +413,10 @@ void test_areal() TEST_INTERSECTION_REV(case_precision_20, 0, 0, 0.0); TEST_INTERSECTION_REV(case_precision_21, 0, 0, 0.0); TEST_INTERSECTION_REV(case_precision_22, 1, -1, 14.0); + TEST_INTERSECTION_REV(case_precision_23, 1, -1, 14.0); + TEST_INTERSECTION_REV(case_precision_24, 0, 0, 0.0); + TEST_INTERSECTION_REV(case_precision_25, 0, 0, 0.0); + TEST_INTERSECTION_REV(case_precision_26, 1, -1, 14.0); test_one("mysql_21964049", mysql_21964049[0], mysql_21964049[1], diff --git a/test/algorithms/set_operations/intersection/test_intersection.hpp b/test/algorithms/set_operations/intersection/test_intersection.hpp index 099b1c047..836851009 100644 --- a/test/algorithms/set_operations/intersection/test_intersection.hpp +++ b/test/algorithms/set_operations/intersection/test_intersection.hpp @@ -40,8 +40,8 @@ #endif #include +#include #include "../setop_output_type.hpp" -#include "../check_validity.hpp" struct ut_settings { @@ -57,17 +57,10 @@ struct ut_settings }; -template -< - typename G1, - typename G2, - typename ResultType, - typename IntersectionOutput -> -typename bg::default_area_result::type -check_result( - IntersectionOutput const& intersection_output, +template +void check_result(IntersectionOutput const& intersection_output, std::string const& caseid, + G1 const& g1, G2 const& g2, std::size_t expected_count, std::size_t expected_holes_count, int expected_point_count, double expected_length_or_area, ut_settings const& settings) @@ -110,7 +103,9 @@ check_result( #endif { std::string message; - bool const valid = check_validity::apply(intersection_output, message); + bool const valid = check_validity + ::apply(intersection_output, caseid, g1, g2, message); + BOOST_CHECK_MESSAGE(valid, "intersection: " << caseid << " not valid: " << message << " type: " << (type_for_assert_message())); @@ -172,7 +167,6 @@ check_result( } #endif - return length_or_area; } @@ -212,7 +206,7 @@ typename bg::default_area_result::type test_intersection(std::string const& result_type intersection_output; bg::intersection(g1, g2, intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, g1, g2, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); @@ -221,21 +215,21 @@ typename bg::default_area_result::type test_intersection(std::string const& intersection_output.clear(); bg::intersection(boost::variant(g1), g2, intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, g1, g2, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); intersection_output.clear(); bg::intersection(g1, boost::variant(g2), intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, g1, g2, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); intersection_output.clear(); bg::intersection(boost::variant(g1), boost::variant(g2), intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, g1, g2, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); #endif diff --git a/test/algorithms/set_operations/union/test_union.hpp b/test/algorithms/set_operations/union/test_union.hpp index 2ea8f00d4..d92fff221 100644 --- a/test/algorithms/set_operations/union/test_union.hpp +++ b/test/algorithms/set_operations/union/test_union.hpp @@ -18,8 +18,8 @@ #include #include +#include #include "../setop_output_type.hpp" -#include "../check_validity.hpp" #include #include @@ -130,7 +130,7 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, #endif { std::string message; - bool const valid = check_validity::apply(clip, message); + bool const valid = check_validity::apply(clip, caseid, g1, g2, message); BOOST_CHECK_MESSAGE(valid, "union: " << caseid << " not valid: " << message << " type: " << (type_for_assert_message())); diff --git a/test/algorithms/set_operations/union/union.cpp b/test/algorithms/set_operations/union/union.cpp index c2799eabe..f1dbd0e8f 100644 --- a/test/algorithms/set_operations/union/union.cpp +++ b/test/algorithms/set_operations/union/union.cpp @@ -279,6 +279,10 @@ void test_areal() #endif TEST_UNION(case_precision_21, 1, 0, -1, 22.0); TEST_UNION(case_precision_22, 1, 1, -1, 73.0); + TEST_UNION(case_precision_23, 1, 1, -1, 73.0); + TEST_UNION(case_precision_24, 1, 0, -1, 22.0); + TEST_UNION(case_precision_25, 1, 0, -1, 22.0); + TEST_UNION(case_precision_26, 1, 1, -1, 73.0); TEST_UNION_REV(case_precision_1, 1, 0, -1, 22.0); TEST_UNION_REV(case_precision_2, 1, 0, -1, 22.0); @@ -304,6 +308,10 @@ void test_areal() #endif TEST_UNION_REV(case_precision_21, 1, 0, -1, 22.0); TEST_UNION_REV(case_precision_22, 1, 1, -1, 73.0); + TEST_UNION_REV(case_precision_23, 1, 1, -1, 73.0); + TEST_UNION_REV(case_precision_24, 1, 0, -1, 22.0); + TEST_UNION_REV(case_precision_25, 1, 0, -1, 22.0); + TEST_UNION_REV(case_precision_26, 1, 1, -1, 73.0); /* test_one(102, @@ -446,6 +454,7 @@ void test_areal() TEST_UNION_REV(issue_566_a, 1, 0, -1, 214.3728); TEST_UNION_REV(issue_566_b, 1, 0, -1, 214.3728); + if (! BOOST_GEOMETRY_CONDITION((boost::is_same::value)) ) { ut_settings ignore_validity; ignore_validity.test_validity = false; @@ -527,7 +536,7 @@ void test_areal() 1, 0, if_typed_tt(93, 91), 22.815); test_one("buffer_mp2", buffer_mp2[0], buffer_mp2[1], - 1, BG_IF_RESCALED(1, 0), 217, 36.752837); + 1, BG_IF_RESCALED(1, (if_typed(1, 0))), 217, 36.752837); test_one("mysql_21964079_1", mysql_21964079_1[0], mysql_21964079_1[1], From eefd70fcb768f4fd9faa40f13a7ced1ebaf6c3a2 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 17 Jul 2019 16:45:03 +0200 Subject: [PATCH 24/49] [self_turns] Changes for self-turns and fix dissolve --- .../detail/is_valid/has_valid_self_turns.hpp | 4 +++- .../algorithms/detail/overlay/overlay.hpp | 21 ++++++++++++------- .../extensions/algorithms/dissolve.hpp | 7 ++----- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp b/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp index 1978e8309..09a0d3745 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp @@ -87,11 +87,13 @@ public: is_acceptable_turn > interrupt_policy; + // Calculate self-turns, skipping adjacent segments detail::self_get_turn_points::self_turns(geometry, strategy, robust_policy, turns, - interrupt_policy); + interrupt_policy, + 0, true); if (interrupt_policy.has_intersections) { diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index e52d3741a..6bda53e93 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -318,15 +318,20 @@ std::cout << "get turns" << std::endl; visitor.visit_turns(1, turns); #if ! defined(BOOST_GEOMETRY_NO_SELF_TURNS) - if (needs_self_turns::apply(geometry1)) + if (! turns.empty() || OverlayType == overlay_dissolve) { - self_get_turn_points::self_turns(geometry1, - strategy, robust_policy, turns, policy, 0); - } - if (needs_self_turns::apply(geometry2)) - { - self_get_turn_points::self_turns(geometry2, - strategy, robust_policy, turns, policy, 1); + // Calculate self turns if the output contains turns already, + // and if necessary (e.g.: multi-geometry, polygon with interior rings) + if (needs_self_turns::apply(geometry1)) + { + self_get_turn_points::self_turns(geometry1, + strategy, robust_policy, turns, policy, 0); + } + if (needs_self_turns::apply(geometry2)) + { + self_get_turn_points::self_turns(geometry2, + strategy, robust_policy, turns, policy, 1); + } } #endif diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index 080371bdc..902531cbc 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -200,12 +200,9 @@ struct dissolve_ring cluster_type clusters; // Enrich/traverse the polygons - typename Strategy::side_strategy_type const - side_strategy = strategy.get_side_strategy(); - enrich_intersection_points(turns, clusters, input_ring, input_ring, rescale_policy, - side_strategy); + strategy); visitor.visit_turns(2, turns); @@ -374,7 +371,7 @@ struct dissolve_polygon // expect - alternatively, difference could be used to have them pure // as interior rings only return detail::sym_difference::sym_difference_insert( - exterior_out, interior_out, rescale_policy, out); + exterior_out, interior_out, out); } }; From b3686c98e4fd7620e8c0804ac385f217bed4ff39 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 17 Jul 2019 16:45:51 +0200 Subject: [PATCH 25/49] [test] Exclude one currently failing testcase --- extensions/test/algorithms/dissolve.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index cbf1773ee..6657d7584 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -576,7 +576,6 @@ void test_one(std::string caseid, std::string const& wkt, template void test_all(ut_settings const& settings_for_sensitive_cases) { - typedef bg::model::ring ring; typedef bg::model::polygon polygon; typedef bg::model::multi_polygon multi_polygon; @@ -652,7 +651,9 @@ void test_all(ut_settings const& settings_for_sensitive_cases) TEST_DISSOLVE_WITH(dissolve_reallife, 91756.916526794434, 1, 0, 25, settings_for_sensitive_cases); +#if defined(BOOST_GEOMETRY_TEST_FAILURES) TEST_DISSOLVE(gitter_2013_04_a, 3043.9181, 3, 0, 21); +#endif TEST_DISSOLVE(gitter_2013_04_b, 31210.429356259738, 1, 0, 11); From c421e5a434f6e567b460dabcbb9b798029769cf2 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 20 Jul 2019 14:07:52 +0200 Subject: [PATCH 26/49] Turn off warnings reported by some compiler settings --- .../buffer/buffered_piece_collection.hpp | 20 +++++++++---------- .../algorithms/detail/get_left_turns.hpp | 16 +++++---------- .../detail/overlay/handle_colocations.hpp | 2 +- .../algorithms/detail/within/multi_point.hpp | 18 +++++++++++++---- .../strategies/cartesian/intersection.hpp | 14 ++++++------- .../strategies/geographic/intersection.hpp | 1 - .../spherical/point_in_poly_winding.hpp | 2 +- 7 files changed, 38 insertions(+), 35 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index 8a4618da4..b21dd4af0 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -484,25 +484,25 @@ struct buffered_piece_collection // intersection-point -> outgoing) // for all (co-located) points still present in the map - for (iterator_type it = boost::begin(m_turns); - it != boost::end(m_turns); - ++it) + for (iterator_type tit = boost::begin(m_turns); + tit != boost::end(m_turns); + ++tit) { typename occupation_map_type::iterator mit = - occupation_map.find(it->get_robust_point()); + occupation_map.find(tit->get_robust_point()); if (mit != occupation_map.end()) { buffer_occupation_info& info = mit->second; for (int i = 0; i < 2; i++) { - add_incoming_and_outgoing_angles(it->get_robust_point(), *it, + add_incoming_and_outgoing_angles(tit->get_robust_point(), *tit, m_pieces, - i, it->operations[i].seg_id, + i, tit->operations[i].seg_id, info); } - it->count_on_multi++; + tit->count_on_multi++; } } @@ -526,10 +526,10 @@ struct buffered_piece_collection #endif // Get left turns from all clusters - for (typename occupation_map_type::iterator it = occupation_map.begin(); - it != occupation_map.end(); ++it) + for (typename occupation_map_type::iterator mit = occupation_map.begin(); + mit != occupation_map.end(); ++mit) { - it->second.get_left_turns(it->first, m_turns, m_side_strategy); + mit->second.get_left_turns(mit->first, m_turns, m_side_strategy); } } diff --git a/include/boost/geometry/algorithms/detail/get_left_turns.hpp b/include/boost/geometry/algorithms/detail/get_left_turns.hpp index 1fec47a01..2e0275a9b 100644 --- a/include/boost/geometry/algorithms/detail/get_left_turns.hpp +++ b/include/boost/geometry/algorithms/detail/get_left_turns.hpp @@ -247,17 +247,11 @@ inline void block_turns(AngleCollection& sorted, std::size_t cluster_size) for (typename boost::range_iterator::type it = sorted.begin(); it != sorted.end(); ++it) { - signed_size_type cluster_index = static_cast(it->cluster_index); - signed_size_type previous_index = cluster_index - 1; - if (previous_index < 0) - { - previous_index = cluster_size - 1; - } - signed_size_type next_index = cluster_index + 1; - if (next_index >= static_cast(cluster_size)) - { - next_index = 0; - } + std::size_t const cluster_index = it->cluster_index; + std::size_t const previous_index + = cluster_index == 0 ? cluster_size - 1 : cluster_index - 1; + std::size_t const next_index + = cluster_index + 1 >= cluster_size ? 0 : cluster_index + 1; if (directions[cluster_index].first && directions[cluster_index].second) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index c417e7d1a..5ae246ec9 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -833,7 +833,7 @@ struct is_turn_index return indexed.turn_index == m_index; } - std::size_t m_index; + signed_size_type m_index; }; diff --git a/include/boost/geometry/algorithms/detail/within/multi_point.hpp b/include/boost/geometry/algorithms/detail/within/multi_point.hpp index 66ae88240..273784a7b 100644 --- a/include/boost/geometry/algorithms/detail/within/multi_point.hpp +++ b/include/boost/geometry/algorithms/detail/within/multi_point.hpp @@ -223,15 +223,21 @@ struct multi_point_multi_geometry bool found_boundary = false; int boundaries = 0; - typedef typename box_pair_vector::const_iterator iterator; - for ( iterator box_it = inters_boxes.begin() ; box_it != inters_boxes.end() ; ++box_it ) + typedef typename box_pair_vector::const_iterator box_iterator; + for (box_iterator box_it = inters_boxes.begin() ; + box_it != inters_boxes.end() ; ++box_it ) { - int in_val = point_in_geometry(*it, range::at(linear_or_areal, box_it->second), strategy); + int const in_val = point_in_geometry(*it, + range::at(linear_or_areal, box_it->second), strategy); if (in_val > 0) + { found_interior = true; + } else if (in_val == 0) + { ++boundaries; + } // If the result was set previously (interior or // interior/boundary found) the only thing that needs to be @@ -243,12 +249,16 @@ struct multi_point_multi_geometry } } - if ( boundaries > 0) + if (boundaries > 0) { if (is_linear && boundaries % 2 == 0) + { found_interior = true; + } else + { found_boundary = true; + } } // exterior diff --git a/include/boost/geometry/strategies/cartesian/intersection.hpp b/include/boost/geometry/strategies/cartesian/intersection.hpp index 22272426c..050cee4f0 100644 --- a/include/boost/geometry/strategies/cartesian/intersection.hpp +++ b/include/boost/geometry/strategies/cartesian/intersection.hpp @@ -226,14 +226,14 @@ struct cartesian_segments // division. BOOST_GEOMETRY_ASSERT(ratio.denominator() != 0); - typedef typename promote_integral::type promoted_type; + typedef typename promote_integral::type integral_type; - promoted_type const numerator - = boost::numeric_cast(ratio.numerator()); - promoted_type const denominator - = boost::numeric_cast(ratio.denominator()); - promoted_type const dx_promoted = boost::numeric_cast(dx); - promoted_type const dy_promoted = boost::numeric_cast(dy); + integral_type const numerator + = boost::numeric_cast(ratio.numerator()); + integral_type const denominator + = boost::numeric_cast(ratio.denominator()); + integral_type const dx_promoted = boost::numeric_cast(dx); + integral_type const dy_promoted = boost::numeric_cast(dy); set<0>(point, get<0, 0>(segment) + boost::numeric_cast < diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index a4d13c9bf..1d6993350 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -499,7 +499,6 @@ private: } // NOTE: this is probably not needed - calc_t const c0 = 0; int a1_on_b = position_value(c0, dist_a1_b1, dist_a1_b2); int a2_on_b = position_value(dist_a1_a2, dist_a1_b1, dist_a1_b2); int b1_on_a = position_value(c0, dist_b1_a1, dist_b1_a2); diff --git a/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp b/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp index aa64241e5..1917869ed 100644 --- a/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp +++ b/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp @@ -502,7 +502,7 @@ protected: typedef math::detail::constants_on_spheroid constants; return constants::half_period() / CalcT(180); - }; + } template static inline bool longitudes_equal(CalcT const& lon1, CalcT const& lon2) From ebfbda842edb9305ea456599f57fb5cce2604570 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 20 Jul 2019 16:55:27 +0200 Subject: [PATCH 27/49] Review, rename integral_type to calc_type --- .../strategies/cartesian/intersection.hpp | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/include/boost/geometry/strategies/cartesian/intersection.hpp b/include/boost/geometry/strategies/cartesian/intersection.hpp index 050cee4f0..33c3da2b6 100644 --- a/include/boost/geometry/strategies/cartesian/intersection.hpp +++ b/include/boost/geometry/strategies/cartesian/intersection.hpp @@ -226,23 +226,21 @@ struct cartesian_segments // division. BOOST_GEOMETRY_ASSERT(ratio.denominator() != 0); - typedef typename promote_integral::type integral_type; + typedef typename promote_integral::type calc_type; - integral_type const numerator - = boost::numeric_cast(ratio.numerator()); - integral_type const denominator - = boost::numeric_cast(ratio.denominator()); - integral_type const dx_promoted = boost::numeric_cast(dx); - integral_type const dy_promoted = boost::numeric_cast(dy); + calc_type const numerator + = boost::numeric_cast(ratio.numerator()); + calc_type const denominator + = boost::numeric_cast(ratio.denominator()); + calc_type const dx_calc = boost::numeric_cast(dx); + calc_type const dy_calc = boost::numeric_cast(dy); - set<0>(point, get<0, 0>(segment) + boost::numeric_cast - < - CoordinateType - >(numerator * dx_promoted / denominator)); - set<1>(point, get<0, 1>(segment) + boost::numeric_cast - < - CoordinateType - >(numerator * dy_promoted / denominator)); + set<0>(point, get<0, 0>(segment) + + boost::numeric_cast(numerator * dx_calc + / denominator)); + set<1>(point, get<0, 1>(segment) + + boost::numeric_cast(numerator * dy_calc + / denominator)); } public : From 32a3a4e8f8c888ab96287adfee3bd2b378336cad Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 30 Jul 2019 20:39:17 +0200 Subject: [PATCH 28/49] [doc] Fix reference matrix (strategies). --- doc/quickref.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/quickref.xml b/doc/quickref.xml index 6bca7566c..0d6de52ca 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -614,6 +614,8 @@ strategy::simplify::douglas_peucker + + Transform @@ -625,9 +627,7 @@ strategy::transform::rotate_transformer - - - + Within strategy::winding From 27b66405104c5db8f4c9ac94cdb75de92b961537 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 30 Jul 2019 23:53:22 +0200 Subject: [PATCH 29/49] [strategies] Add workaround for msvc-10..12 in within strategy concept check. --- include/boost/geometry/strategies/concepts/within_concept.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/strategies/concepts/within_concept.hpp b/include/boost/geometry/strategies/concepts/within_concept.hpp index 65077647e..42f2c9303 100644 --- a/include/boost/geometry/strategies/concepts/within_concept.hpp +++ b/include/boost/geometry/strategies/concepts/within_concept.hpp @@ -55,7 +55,7 @@ class WithinStrategyPolygonal struct checker { template - static void apply(ApplyMethod const&, ResultMethod const& ) + static void apply(ApplyMethod, ResultMethod) { typedef typename parameter_type_of < @@ -129,7 +129,7 @@ class WithinStrategyPointBox struct checker { template - static void apply(ApplyMethod const&) + static void apply(ApplyMethod) { typedef typename parameter_type_of < From f6d33812c79c2e2cc736bdd96972dc388d4d20c6 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 30 Jul 2019 23:55:18 +0200 Subject: [PATCH 30/49] [test] Call bg::math::abs() instead of std::fabs() in infinite_line test (workaround for msvc-10..12). --- test/geometries/infinite_line.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/geometries/infinite_line.cpp b/test/geometries/infinite_line.cpp index 1968d6f8a..a69b43137 100755 --- a/test/geometries/infinite_line.cpp +++ b/test/geometries/infinite_line.cpp @@ -3,15 +3,20 @@ // Copyright (c) 2019 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2019. +// Modifications copyright (c) 2019, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include -#include #include +#include #include +#include namespace { @@ -20,7 +25,7 @@ namespace bool is_small(T const& value) { static long double const epsilon = 1.0e-5; - return std::fabs(value) < epsilon; + return bg::math::abs(value) < epsilon; } } From 5d520285ab59c68ab41ccb4684b13bff60464672 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 1 Aug 2019 21:34:33 +0200 Subject: [PATCH 31/49] [test][strategies] Add missing = (fixes compilation with clang -std=c++98). --- test/strategies/distance_cross_track.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/strategies/distance_cross_track.cpp b/test/strategies/distance_cross_track.cpp index 42207b285..5989d9067 100644 --- a/test/strategies/distance_cross_track.cpp +++ b/test/strategies/distance_cross_track.cpp @@ -4,6 +4,7 @@ // Copyright (c) 2019 Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -57,7 +58,7 @@ void test_all(expected_results const& results) // WGS84 Spheroid spheroid(6378137.0, 6356752.3142451793); - error errors [] + error errors [] = { {0.00000001, 0.00000001, 0.00000001, 0.000001}, //vincenty {0.0002, 0.002, 0.01, 0.2}, //thomas From 88029cd2382937ca7bf793d0a38411cc624f071e Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 1 Aug 2019 22:05:31 +0200 Subject: [PATCH 32/49] [util] Add namespace in range::erase call to prevent the compiler instantiating c++20 std::erase. --- include/boost/geometry/util/range.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/geometry/util/range.hpp b/include/boost/geometry/util/range.hpp index 6d49e45fd..e6acb1816 100644 --- a/include/boost/geometry/util/range.hpp +++ b/include/boost/geometry/util/range.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014, 2015, 2016. -// Modifications copyright (c) 2013-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015, 2016, 2019. +// Modifications copyright (c) 2013-2019 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -305,7 +305,7 @@ erase(Range & rng, it = boost::begin(rng) + std::distance(boost::const_begin(rng), cit); - return erase(rng, it); + return range::erase(rng, it); } /*! @@ -366,7 +366,7 @@ erase(Range & rng, last = boost::begin(rng) + std::distance(boost::const_begin(rng), clast); - return erase(rng, first, last); + return range::erase(rng, first, last); } // back_inserter From f30578bda548d770286c0c416826d73d0813192d Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 5 Aug 2019 17:50:17 +0200 Subject: [PATCH 33/49] [meta] Set "status" of extensions and index sublibs to "hidden". --- extensions/meta/libraries.json | 1 + index/meta/libraries.json | 1 + 2 files changed, 2 insertions(+) diff --git a/extensions/meta/libraries.json b/extensions/meta/libraries.json index 24d0f9d98..caaed3a4b 100644 --- a/extensions/meta/libraries.json +++ b/extensions/meta/libraries.json @@ -3,6 +3,7 @@ "key": "geometry/extensions", "boost-version": "1.47.0", "name": "Geometry Extensions", + "status": "hidden", "authors": [ "Barend Gehrels", "Bruno Lalande", diff --git a/index/meta/libraries.json b/index/meta/libraries.json index 4b0f24537..76763eaa8 100644 --- a/index/meta/libraries.json +++ b/index/meta/libraries.json @@ -3,6 +3,7 @@ "key": "geometry/index", "boost-version": "1.54.0", "name": "Geometry Index", + "status": "hidden", "authors": [ "Barend Gehrels", "Bruno Lalande", From 4a94c7ec52eab5d7ff2eb795b05ffa4e9837d2d7 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 6 Aug 2019 01:44:34 +0200 Subject: [PATCH 34/49] [meta] Set "boost-version" of extensions and index sublibs to "hidden". --- extensions/meta/libraries.json | 2 +- index/meta/libraries.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/meta/libraries.json b/extensions/meta/libraries.json index caaed3a4b..52e0d4029 100644 --- a/extensions/meta/libraries.json +++ b/extensions/meta/libraries.json @@ -1,7 +1,7 @@ [ { "key": "geometry/extensions", - "boost-version": "1.47.0", + "boost-version": "hidden", "name": "Geometry Extensions", "status": "hidden", "authors": [ diff --git a/index/meta/libraries.json b/index/meta/libraries.json index 76763eaa8..46b6fd932 100644 --- a/index/meta/libraries.json +++ b/index/meta/libraries.json @@ -1,7 +1,7 @@ [ { "key": "geometry/index", - "boost-version": "1.54.0", + "boost-version": "hidden", "name": "Geometry Index", "status": "hidden", "authors": [ From 0eb4d319e297c2efcaffe66e047beb8e003a6350 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 7 Aug 2019 20:25:32 +0200 Subject: [PATCH 35/49] [doc] Update release notes (1.71). --- doc/release_notes.qbk | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 9d859a22e..8d98af080 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -19,6 +19,25 @@ [section:release_notes Release Notes] +[/=================] +[heading Boost 1.71] +[/=================] + +[*Improvements] + +* [@https://github.com/boostorg/geometry/pull/568 568] Add a constructor that takes matrix_type to matrix_transformer. +* [@https://github.com/boostorg/geometry/pull/605 605] Improvement of handling of coordinate systems in various algorithms. +* Various improvements related to robustness of set and relational operation. + +[*Solved issues] + +* [@https://github.com/boostorg/geometry/issues/596 596] boost::geometry::buffer generates (multi)polygon with spike. + +[*Bugfixes] + +* [@https://github.com/boostorg/geometry/pull/595 595] Fix inaccuracy in geographic point-segment distance computation. +* Fix various compiler warnings. + [/=================] [heading Boost 1.70] [/=================] From 06836bc3e128be5ed4f8463638683aea7390aae0 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 14 Aug 2019 10:36:25 +0200 Subject: [PATCH 36/49] Pass subranges to intersections, to avoid passing recalculated points which are not used by geographic/spherical and new approach of cartesian, nor by linear/linear and linear/areal in current cartesian --- .../detail/disjoint/linear_linear.hpp | 5 +- .../overlay/enrich_intersection_points.hpp | 6 - .../overlay/get_intersection_points.hpp | 34 +--- .../detail/overlay/get_turn_info.hpp | 143 +------------ .../detail/overlay/get_turn_info_helpers.hpp | 192 +++++++++++------- .../algorithms/detail/overlay/get_turns.hpp | 1 - .../detail/overlay/handle_colocations.hpp | 83 -------- .../detail/overlay/intersection_insert.hpp | 41 ++-- .../detail/overlay/segment_as_subrange.hpp | 54 +++++ .../policies/robustness/no_rescale_policy.hpp | 2 +- .../robustness/rescale_policy_tags.hpp | 43 ++++ .../strategies/cartesian/intersection.hpp | 117 ++++++----- .../strategies/geographic/intersection.hpp | 65 +++--- .../strategies/spherical/intersection.hpp | 56 ++--- 14 files changed, 358 insertions(+), 484 deletions(-) create mode 100644 include/boost/geometry/algorithms/detail/overlay/segment_as_subrange.hpp create mode 100644 include/boost/geometry/policies/robustness/rescale_policy_tags.hpp diff --git a/include/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp b/include/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp index 7b830f7d1..744fa400f 100644 --- a/include/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -78,7 +79,9 @@ struct disjoint_segment intersection_return_type > intersection_policy; - intersection_return_type is = strategy.apply(segment1, segment2, + detail::segment_as_subrange sub_range1(segment1); + detail::segment_as_subrange sub_range2(segment2); + intersection_return_type is = strategy.apply(sub_range1, sub_range2, intersection_policy(), robust_policy); diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index d82c34cc7..fbb126bbc 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -426,12 +426,6 @@ inline void enrich_intersection_points(Turns& turns, std::vector > mapped_vector_type; - // As long as turn indexes are not used yet, turns might be erased from - // the vector - // For now start turns are disabled. - // TODO: remove code or fix inconsistencies within validity and relations - // detail::overlay::erase_colocated_start_turns(turns, geometry1, geometry2); - // From here on, turn indexes are used (in clusters, next_index, etc) // and may only be flagged as discarded diff --git a/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp index 5a3360375..2ed5d9a8c 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp @@ -22,11 +22,10 @@ #include #include +#include #include -#include - namespace boost { namespace geometry { @@ -59,6 +58,13 @@ struct get_turn_without_info RobustPolicy const& robust_policy, OutputIterator out) { + // Make sure this is only called with no rescaling + BOOST_STATIC_ASSERT((boost::is_same + < + no_rescale_policy_tag, + typename rescale_policy_type::type + >::value)); + typedef typename TurnInfo::point_type turn_point_type; typedef policies::relate::segments_intersection_points @@ -73,28 +79,8 @@ struct get_turn_without_info > > policy_type; - typedef model::referring_segment segment_type1; - typedef model::referring_segment segment_type2; - Point1 const& pi = range_p.at(0); - Point1 const& pj = range_p.at(1); - Point2 const& qi = range_q.at(0); - Point2 const& qj = range_q.at(1); - segment_type1 p1(pi, pj); - segment_type2 q1(qi, qj); - - typedef typename geometry::robust_point_type - < - Point1, RobustPolicy - >::type robust_point_type; - - robust_point_type pi_rob, pj_rob, qi_rob, qj_rob; - geometry::recalculate(pi_rob, pi, robust_policy); - geometry::recalculate(pj_rob, pj, robust_policy); - geometry::recalculate(qi_rob, qi, robust_policy); - geometry::recalculate(qj_rob, qj, robust_policy); - typename policy_type::return_type result - = strategy.apply(p1, q1, policy_type(), robust_policy, - pi_rob, pj_rob, qi_rob, qj_rob); + typename policy_type::return_type const result + = strategy.apply(range_p, range_q, policy_type(), robust_policy); for (std::size_t i = 0; i < result.count; i++) { diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index c6429bc55..7404dabb4 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -619,133 +619,6 @@ struct equal : public base_turn_handler } }; - -template -< - typename TurnInfo -> -struct start : public base_turn_handler -{ - template - < - typename UniqueSubRange1, - typename UniqueSubRange2, - typename IntersectionInfo, - typename DirInfo, - typename SideCalculator, - typename UmbrellaStrategy - > - static inline bool apply(UniqueSubRange1 const& range_p, - UniqueSubRange2 const& range_q, - TurnInfo& ti, - IntersectionInfo const& info, - DirInfo const& dir_info, - SideCalculator const& side, - UmbrellaStrategy const& ) - { - // For now disabled. TODO: remove all code or fix inconsistencies - // within validity and relations - return false; - - if (dir_info.opposite) - { - // They should not be collinear - return false; - } - - int const side_pj_q1 = side.pj_wrt_q1(); - int const side_qj_p1 = side.qj_wrt_p1(); - - // Get side values at starting point - typedef detail::distance_measure - < - typename select_coordinate_type - < - typename UniqueSubRange1::point_type, - typename UniqueSubRange2::point_type - >::type - > dm_type; - - typedef typename UmbrellaStrategy::cs_tag cs_tag; - dm_type const dm_pi_q1 = get_distance_measure(range_q.at(0), range_q.at(1), range_p.at(0)); - dm_type const dm_qi_p1 = get_distance_measure(range_p.at(0), range_p.at(1), range_q.at(0)); - - if (dir_info.how_a == -1 && dir_info.how_b == -1) - { - // Both p and q leave - if (dm_pi_q1.is_zero() && dm_qi_p1.is_zero()) - { - // Exactly collinear, not necessary to handle it - return false; - } - - if (! (dm_pi_q1.is_small() && dm_qi_p1.is_small())) - { - // Not nearly collinear - return false; - } - - if (side_qj_p1 == 0) - { - // Collinear is not handled - return false; - } - - ui_else_iu(side_qj_p1 == -1, ti); - } - else if (dir_info.how_b == -1) - { - // p ---------------> - // | - // | q q leaves - // v - // - - if (dm_qi_p1.is_zero() || ! dm_qi_p1.is_small()) - { - // Exactly collinear - return false; - } - - if (side_qj_p1 == 0) - { - // Collinear is not handled - return false; - } - - ui_else_iu(side_qj_p1 == -1, ti); - } - else if (dir_info.how_a == -1) - { - if (dm_pi_q1.is_zero() || ! dm_pi_q1.is_small()) - { - // It starts exactly, not necessary to handle it - return false; - } - - // p leaves - if (side_pj_q1 == 0) - { - // Collinear is not handled - return false; - } - - ui_else_iu(side_pj_q1 == 1, ti); - } - else - { - // Not supported - return false; - } - - // Copy intersection point - assign_point(ti, method_start, info, 0); - return true; - } - -}; - - template < typename TurnInfo, @@ -1169,6 +1042,8 @@ struct get_turn_info switch(method) { case 'a' : // "angle" + case 'f' : // "from" + case 's' : // "start" do_only_convert = true; break; @@ -1210,20 +1085,6 @@ struct get_turn_info *out++ = tp; } break; - case 'f' : - case 's' : - { - // "from" or "start" without rescaling, it is in some cases necessary to handle - if (start::apply(range_p, range_q, tp, inters.i_info(), inters.d_info(), inters.sides(), umbrella_strategy)) - { - *out++ = tp; - } - else - { - do_only_convert = true; - } - } - break; case 'e': { if ( ! inters.d_info().opposite ) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp index 478811860..c485abaa7 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include namespace boost { namespace geometry { @@ -153,18 +153,20 @@ template typename UniqueSubRange1, typename UniqueSubRange2, typename RobustPolicy > -struct robust_points +struct robust_point_calculator { typedef typename geometry::robust_point_type < typename UniqueSubRange1::point_type, RobustPolicy >::type robust_point1_type; + typedef typename geometry::robust_point_type + < + typename UniqueSubRange2::point_type, RobustPolicy + >::type robust_point2_type; - typedef robust_point1_type robust_point2_type; - - inline robust_points(UniqueSubRange1 const& range_p, - UniqueSubRange2 const& range_q, - RobustPolicy const& robust_policy) + inline robust_point_calculator(UniqueSubRange1 const& range_p, + UniqueSubRange2 const& range_q, + RobustPolicy const& robust_policy) : m_robust_policy(robust_policy) , m_range_p(range_p) , m_range_q(range_q) @@ -214,18 +216,55 @@ private : mutable bool m_qk_retrieved; }; +// Default version (empty - specialized below) template < typename UniqueSubRange1, typename UniqueSubRange2, - typename TurnPoint, typename UmbrellaStrategy, typename RobustPolicy> -class intersection_info_base - : private robust_points + typename TurnPoint, typename UmbrellaStrategy, + typename RobustPolicy, + typename Tag = typename rescale_policy_type::type +> +class intersection_info_base {}; + +// Version with rescaling, having robust points +template +< + typename UniqueSubRange1, typename UniqueSubRange2, + typename TurnPoint, typename UmbrellaStrategy, + typename RobustPolicy +> +class intersection_info_base { - typedef robust_points base; + typedef robust_point_calculator + < + UniqueSubRange1, UniqueSubRange2, + RobustPolicy + > + robust_calc_type; public: - typedef typename base::robust_point1_type robust_point1_type; - typedef typename base::robust_point2_type robust_point2_type; + typedef segment_intersection_points + < + TurnPoint, + typename geometry::segment_ratio_type + < + TurnPoint, RobustPolicy + >::type + > intersection_point_type; + typedef policies::relate::segments_tupled + < + policies::relate::segments_intersection_points + < + intersection_point_type + >, + policies::relate::segments_direction + > intersection_policy_type; + + typedef typename intersection_policy_type::return_type result_type; + + typedef typename robust_calc_type::robust_point1_type robust_point1_type; + typedef typename robust_calc_type::robust_point2_type robust_point2_type; typedef robust_subrange_adapter robust_subrange1; typedef robust_subrange_adapter robust_subrange2; @@ -246,28 +285,32 @@ public: UniqueSubRange2 const& range_q, UmbrellaStrategy const& umbrella_strategy, RobustPolicy const& robust_policy) - : base(range_p, range_q, robust_policy) - , m_range_p(range_p) + : m_range_p(range_p) , m_range_q(range_q) - , m_robust_range_p(range_p, base::m_rpi, base::m_rpj, robust_policy) - , m_robust_range_q(range_q, base::m_rqi, base::m_rqj, robust_policy) + , m_robust_calc(range_p, range_q, robust_policy) + , m_robust_range_p(range_p, m_robust_calc.m_rpi, m_robust_calc.m_rpj, robust_policy) + , m_robust_range_q(range_q, m_robust_calc.m_rqi, m_robust_calc.m_rqj, robust_policy) , m_side_calc(m_robust_range_p, m_robust_range_q, umbrella_strategy.get_side_strategy()) + , m_result(umbrella_strategy.apply(range_p, range_q, + intersection_policy_type(), + robust_policy, + m_robust_range_p, m_robust_range_q)) {} - inline typename UniqueSubRange1::point_type const& pi() const { return m_range_p.at(0); } - inline typename UniqueSubRange2::point_type const& qi() const { return m_range_q.at(0); } + inline bool p_is_last_segment() const { return m_range_p.is_last_segment(); } + inline bool q_is_last_segment() const { return m_range_q.is_last_segment(); } - inline robust_point1_type const& rpi() const { return base::m_rpi; } - inline robust_point1_type const& rpj() const { return base::m_rpj; } - inline robust_point1_type const& rpk() const { return base::get_rpk(); } + inline robust_point1_type const& rpi() const { return m_robust_calc.m_rpi; } + inline robust_point1_type const& rpj() const { return m_robust_calc.m_rpj; } + inline robust_point1_type const& rpk() const { return m_robust_calc.get_rpk(); } - inline robust_point2_type const& rqi() const { return base::m_rqi; } - inline robust_point2_type const& rqj() const { return base::m_rqj; } - inline robust_point2_type const& rqk() const { return base::get_rqk(); } + inline robust_point2_type const& rqi() const { return m_robust_calc.m_rqi; } + inline robust_point2_type const& rqj() const { return m_robust_calc.m_rqj; } + inline robust_point2_type const& rqk() const { return m_robust_calc.get_rqk(); } inline side_calculator_type const& sides() const { return m_side_calc; } - + robust_swapped_side_calculator_type get_swapped_sides() const { robust_swapped_side_calculator_type result( @@ -276,31 +319,55 @@ public: return result; } +private : + // Owned by get_turns UniqueSubRange1 const& m_range_p; UniqueSubRange2 const& m_range_q; -private : + // Owned by this class + robust_calc_type m_robust_calc; robust_subrange1 m_robust_range_p; robust_subrange2 m_robust_range_q; side_calculator_type m_side_calc; + +protected : + result_type m_result; }; +// Version without rescaling template < typename UniqueSubRange1, typename UniqueSubRange2, - typename TurnPoint, typename UmbrellaStrategy + typename TurnPoint, typename UmbrellaStrategy, + typename RobustPolicy > class intersection_info_base + TurnPoint, UmbrellaStrategy, RobustPolicy, no_rescale_policy_tag> { public: + typedef segment_intersection_points + < + TurnPoint, + typename geometry::segment_ratio_type + < + TurnPoint, RobustPolicy + >::type + > intersection_point_type; + typedef policies::relate::segments_tupled + < + policies::relate::segments_intersection_points + < + intersection_point_type + >, + policies::relate::segments_direction + > intersection_policy_type; + + typedef typename intersection_policy_type::return_type result_type; + typedef typename UniqueSubRange1::point_type point1_type; typedef typename UniqueSubRange2::point_type point2_type; - typedef typename UniqueSubRange1::point_type robust_point1_type; - typedef typename UniqueSubRange2::point_type robust_point2_type; - typedef typename UmbrellaStrategy::cs_tag cs_tag; typedef typename UmbrellaStrategy::side_strategy_type side_strategy_type; @@ -315,13 +382,19 @@ public: intersection_info_base(UniqueSubRange1 const& range_p, UniqueSubRange2 const& range_q, UmbrellaStrategy const& umbrella_strategy, - no_rescale_policy const& /*robust_policy*/) + no_rescale_policy const& robust_policy) : m_range_p(range_p) , m_range_q(range_q) , m_side_calc(range_p, range_q, umbrella_strategy.get_side_strategy()) + , m_result(umbrella_strategy.apply(range_p, range_q, + intersection_policy_type(), + robust_policy)) {} + inline bool p_is_last_segment() const { return m_range_p.is_last_segment(); } + inline bool q_is_last_segment() const { return m_range_q.is_last_segment(); } + inline point1_type const& rpi() const { return m_side_calc.get_pi(); } inline point1_type const& rpj() const { return m_side_calc.get_pj(); } inline point1_type const& rpk() const { return m_side_calc.get_pk(); } @@ -340,13 +413,16 @@ public: return result; } -protected : +private : // Owned by get_turns UniqueSubRange1 const& m_range_p; UniqueSubRange2 const& m_range_q; -private : - // Owned here, passed by .get_side_strategy() + + // Owned by this class side_calculator_type m_side_calc; + +protected : + result_type m_result; }; @@ -365,37 +441,17 @@ class intersection_info TurnPoint, UmbrellaStrategy, RobustPolicy> base; public: - typedef segment_intersection_points - < - TurnPoint, - typename geometry::segment_ratio_type - < - TurnPoint, RobustPolicy - >::type - > intersection_point_type; typedef typename UniqueSubRange1::point_type point1_type; typedef typename UniqueSubRange2::point_type point2_type; - // NOTE: formerly defined in intersection_strategies - typedef policies::relate::segments_tupled - < - policies::relate::segments_intersection_points - < - intersection_point_type - >, - policies::relate::segments_direction - > intersection_policy_type; - typedef UmbrellaStrategy intersection_strategy_type; typedef typename UmbrellaStrategy::side_strategy_type side_strategy_type; typedef typename UmbrellaStrategy::cs_tag cs_tag; - typedef model::referring_segment segment_type1; - typedef model::referring_segment segment_type2; typedef typename base::side_calculator_type side_calculator_type; + typedef typename base::result_type result_type; - typedef typename intersection_policy_type::return_type result_type; typedef typename boost::tuples::element<0, result_type>::type i_info_type; // intersection_info typedef typename boost::tuples::element<1, result_type>::type d_info_type; // dir_info @@ -405,20 +461,13 @@ public: RobustPolicy const& robust_policy) : base(range_p, range_q, umbrella_strategy, robust_policy) - , m_result(umbrella_strategy.apply( - segment_type1(range_p.at(0), range_p.at(1)), - segment_type2(range_q.at(0), range_q.at(1)), - intersection_policy_type(), - robust_policy, - base::rpi(), base::rpj(), - base::rqi(), base::rqj())) , m_intersection_strategy(umbrella_strategy) , m_robust_policy(robust_policy) {} - inline result_type const& result() const { return m_result; } - inline i_info_type const& i_info() const { return m_result.template get<0>(); } - inline d_info_type const& d_info() const { return m_result.template get<1>(); } + inline result_type const& result() const { return base::m_result; } + inline i_info_type const& i_info() const { return base::m_result.template get<0>(); } + inline d_info_type const& d_info() const { return base::m_result.template get<1>(); } inline side_strategy_type get_side_strategy() const { @@ -428,7 +477,7 @@ public: // TODO: it's more like is_spike_ip_p inline bool is_spike_p() const { - if (base::m_range_p.is_last_segment()) + if (base::p_is_last_segment()) { return false; } @@ -443,7 +492,7 @@ public: } // TODO: why is q used to determine spike property in p? - bool const has_qk = ! base::m_range_q.is_last_segment(); + bool const has_qk = ! base::q_is_last_segment(); int const qk_p1 = has_qk ? base::sides().qk_wrt_p1() : 0; int const qk_p2 = has_qk ? base::sides().qk_wrt_p2() : 0; @@ -467,7 +516,7 @@ public: inline bool is_spike_q() const { - if (base::m_range_q.is_last_segment()) + if (base::q_is_last_segment()) { return false; } @@ -481,7 +530,7 @@ public: } // TODO: why is p used to determine spike property in q? - bool const has_pk = ! base::m_range_p.is_last_segment(); + bool const has_pk = ! base::p_is_last_segment(); int const pk_q1 = has_pk ? base::sides().pk_wrt_q1() : 0; int const pk_q2 = has_pk ? base::sides().pk_wrt_q2() : 0; @@ -523,7 +572,6 @@ private: } } - result_type m_result; UmbrellaStrategy const& m_intersection_strategy; RobustPolicy const& m_robust_policy; }; diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 886f8b683..a19691d61 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -209,7 +209,6 @@ private : RobustPolicy m_robust_policy; }; - template < typename Geometry1, typename Geometry2, diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 5ae246ec9..d948f9f27 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -84,15 +84,6 @@ struct turn_operation_index signed_size_type op_index; // only 0,1 }; -struct is_discarded -{ - template - inline bool operator()(Turn const& turn) const - { - return turn.discarded; - } -}; - template struct less_by_fraction_and_type { @@ -534,80 +525,6 @@ inline segment_identifier get_preceding_segment_id(segment_identifier const& id, return result; } -// Turns marked with method can be generated but are often duplicate, -// unless (by floating point precision) the preceding touching turn is just missed. -// This means that all (nearly) colocated with preceding touching turn -// can be deleted. This is done before colocation itself (because in colocated, -// they are only discarded, and that can give issues in traversal) -template -inline void erase_colocated_start_turns(Turns& turns, - Geometry0 const& geometry0, Geometry1 const& geometry1) -{ - typedef std::pair seg_id_pair; - typedef std::map map_type; - - typedef typename boost::range_value::type turn_type; - typedef typename boost::range_iterator::type turn_it; - typedef map_type::const_iterator map_it; - - // Collect starting turns into map - map_type preceding_segments; - std::size_t turn_index = 0; - for (turn_it it = boost::begin(turns); it != boost::end(turns); ++it, ++turn_index) - { - turn_type const& turn = *it; - if (turn.method == method_start) - { - // Insert identifiers for preceding segments of both operations. - // (For self turns geometry1 == geometry2) - seg_id_pair const pair( - get_preceding_segment_id(turn.operations[0].seg_id, geometry0, geometry1), - get_preceding_segment_id(turn.operations[1].seg_id, geometry0, geometry1)); - - // There should exist only one turn with such ids - BOOST_GEOMETRY_ASSERT(preceding_segments.find(pair) == preceding_segments.end()); - - preceding_segments[pair] = turn_index; - } - } - - if (preceding_segments.empty()) - { - return; - } - - // Find touching turns on preceding segment id combinations - bool has_discarded = false; - for (turn_it it = boost::begin(turns); it != boost::end(turns); ++it) - { - turn_type const& turn = *it; - if (turn.method == method_touch) - { - seg_id_pair const pair(turn.operations[0].seg_id, - turn.operations[1].seg_id); - - map_it mit = preceding_segments.find(pair); - - if (mit != preceding_segments.end()) - { - // The found touching turn precedes the found starting turn. - // (To be completely sure we could verify if turn.point is (nearly) equal) - // These turns are duplicate, discard the starting turn. - has_discarded = true; - turn_type& extra_turn = turns[mit->second]; - extra_turn.discarded = true; - } - } - } - - if (has_discarded) - { - turns.erase(std::remove_if(boost::begin(turns), boost::end(turns), - is_discarded()), - boost::end(turns)); - } -} - template < overlay_type OverlayType, diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index 5e55d9db2..1f9b39502 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -35,8 +35,9 @@ #include #include #include +#include -#include +#include #include #include @@ -75,29 +76,15 @@ struct intersection_segment_segment_point OutputIterator out, Strategy const& strategy) { + // Make sure this is only called with no rescaling + BOOST_STATIC_ASSERT((boost::is_same + < + no_rescale_policy_tag, + typename rescale_policy_type::type + >::value)); + typedef typename point_type::type point_type; - typedef typename geometry::robust_point_type - < - typename geometry::point_type::type, - RobustPolicy - >::type robust_point_type; - - // TODO: rescale segment -> robust points - robust_point_type pi_rob, pj_rob, qi_rob, qj_rob; - { - // Workaround: - point_type pi, pj, qi, qj; - assign_point_from_index<0>(segment1, pi); - assign_point_from_index<1>(segment1, pj); - assign_point_from_index<0>(segment2, qi); - assign_point_from_index<1>(segment2, qj); - geometry::recalculate(pi_rob, pi, robust_policy); - geometry::recalculate(pj_rob, pj, robust_policy); - geometry::recalculate(qi_rob, qi, robust_policy); - geometry::recalculate(qj_rob, qj, robust_policy); - } - // Get the intersection point (or two points) typedef segment_intersection_points < @@ -113,10 +100,12 @@ struct intersection_segment_segment_point intersection_return_type > policy_type; + detail::segment_as_subrange sub_range1(segment1); + detail::segment_as_subrange sub_range2(segment2); + intersection_return_type - is = strategy.apply(segment1, segment2, - policy_type(), robust_policy, - pi_rob, pj_rob, qi_rob, qj_rob); + is = strategy.apply(sub_range1, sub_range2, + policy_type(), robust_policy); for (std::size_t i = 0; i < is.count; i++) { @@ -1290,7 +1279,7 @@ inline OutputIterator intersection_insert(Geometry1 const& geometry1, typedef typename geometry::rescale_policy_type < - typename geometry::point_type::type, // TODO from both + typename geometry::point_type::type, typename Strategy::cs_tag >::type rescale_policy_type; diff --git a/include/boost/geometry/algorithms/detail/overlay/segment_as_subrange.hpp b/include/boost/geometry/algorithms/detail/overlay/segment_as_subrange.hpp new file mode 100644 index 000000000..deac13ba4 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/segment_as_subrange.hpp @@ -0,0 +1,54 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2019-2019 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_AS_SUBRANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_AS_SUBRANGE_HPP + + +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +struct segment_as_subrange +{ + segment_as_subrange(Segment const& s) + : m_segment(s) + { + geometry::set<0>(m_p1, geometry::get<0, 0>(m_segment)); + geometry::set<1>(m_p1, geometry::get<0, 1>(m_segment)); + geometry::set<0>(m_p2, geometry::get<1, 0>(m_segment)); + geometry::set<1>(m_p2, geometry::get<1, 1>(m_segment)); + } + + typedef typename geometry::point_type::type point_type; + + point_type const& at(std::size_t index) const + { + return index == 0 ? m_p1 : m_p2; + } + + point_type m_p1, m_p2; + + Segment const& m_segment; +}; + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_AS_SUBRANGE_HPP diff --git a/include/boost/geometry/policies/robustness/no_rescale_policy.hpp b/include/boost/geometry/policies/robustness/no_rescale_policy.hpp index a7899842c..8d23cb370 100644 --- a/include/boost/geometry/policies/robustness/no_rescale_policy.hpp +++ b/include/boost/geometry/policies/robustness/no_rescale_policy.hpp @@ -26,7 +26,7 @@ namespace boost { namespace geometry namespace detail { -// Probably this will be moved out of namespace detail +// Redudant later. struct no_rescale_policy { static bool const enabled = false; diff --git a/include/boost/geometry/policies/robustness/rescale_policy_tags.hpp b/include/boost/geometry/policies/robustness/rescale_policy_tags.hpp new file mode 100644 index 000000000..03f90760e --- /dev/null +++ b/include/boost/geometry/policies/robustness/rescale_policy_tags.hpp @@ -0,0 +1,43 @@ +// Boost.Geometry + +// Copyright (c) 2019-2019 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_POLICIES_ROBUSTNESS_RESCALE_POLICY_TYPE_HPP +#define BOOST_GEOMETRY_POLICIES_ROBUSTNESS_RESCALE_POLICY_TYPE_HPP + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +struct no_rescale_policy_tag {}; +struct rescale_policy_tag {}; + +template +struct rescale_policy_type +{ + typedef rescale_policy_tag type; +}; + +// Specialization +template <> +struct rescale_policy_type +{ + typedef no_rescale_policy_tag type; +}; + +} // namespace detail +#endif + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_POLICIES_ROBUSTNESS_RESCALE_POLICY_TYPE_HPP diff --git a/include/boost/geometry/strategies/cartesian/intersection.hpp b/include/boost/geometry/strategies/cartesian/intersection.hpp index 33c3da2b6..6adbef6b4 100644 --- a/include/boost/geometry/strategies/cartesian/intersection.hpp +++ b/include/boost/geometry/strategies/cartesian/intersection.hpp @@ -50,6 +50,7 @@ #include #include +#include #include #include @@ -301,62 +302,68 @@ struct cartesian_segments // IntersectionPoint = (x1 + r * dx_a, y1 + r * dy_a) } - - // Relate segments a and b + // Version for non-rescaled policies template < - typename Segment1, - typename Segment2, + typename UniqueSubRange1, + typename UniqueSubRange2, typename Policy, typename RobustPolicy > static inline typename Policy::return_type - apply(Segment1 const& a, Segment2 const& b, - Policy const& policy, RobustPolicy const& robust_policy) + apply(UniqueSubRange1 const& range_p, + UniqueSubRange2 const& range_q, + Policy const& policy, + RobustPolicy const& robust_policy) { - // type them all as in Segment1 - TODO reconsider this, most precise? - typedef typename geometry::point_type::type point_type; + // Make sure this is only called when there is no rescaling + BOOST_STATIC_ASSERT((boost::is_same + < + detail::no_rescale_policy_tag, + typename detail::rescale_policy_type::type + >::value)); - typedef typename geometry::robust_point_type - < - point_type, RobustPolicy - >::type robust_point_type; - - point_type a0, a1, b0, b1; - robust_point_type a0_rob, a1_rob, b0_rob, b1_rob; - - detail::assign_point_from_index<0>(a, a0); - detail::assign_point_from_index<1>(a, a1); - detail::assign_point_from_index<0>(b, b0); - detail::assign_point_from_index<1>(b, b1); - - geometry::recalculate(a0_rob, a0, robust_policy); - geometry::recalculate(a1_rob, a1, robust_policy); - geometry::recalculate(b0_rob, b0, robust_policy); - geometry::recalculate(b1_rob, b1, robust_policy); - - return apply(a, b, policy, robust_policy, a0_rob, a1_rob, b0_rob, b1_rob); + // Pass the same ranges both as normal ranges and as robust ranges + return apply(range_p, range_q, policy, robust_policy, range_p, range_q); } - // The main entry-routine, calculating intersections of segments a / b - // NOTE: Robust* types may be the same as Segments' point types + // Main entry-routine, calculating intersections of segments p / q template < - typename Segment1, - typename Segment2, + typename UniqueSubRange1, + typename UniqueSubRange2, typename Policy, typename RobustPolicy, - typename RobustPoint1, - typename RobustPoint2 + typename RobustUniqueSubRange1, + typename RobustUniqueSubRange2 > static inline typename Policy::return_type - apply(Segment1 const& a, Segment2 const& b, - Policy const&, RobustPolicy const& /*robust_policy*/, - RobustPoint1 const& robust_a1, RobustPoint1 const& robust_a2, - RobustPoint2 const& robust_b1, RobustPoint2 const& robust_b2) + apply(UniqueSubRange1 const& range_p, + UniqueSubRange2 const& range_q, + Policy const&, + RobustPolicy const& , + RobustUniqueSubRange1 const& robust_range_p, + RobustUniqueSubRange2 const& robust_range_q) { - BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); - BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); + typedef typename UniqueSubRange1::point_type point1_type; + typedef typename UniqueSubRange2::point_type point2_type; + + BOOST_CONCEPT_ASSERT( (concepts::ConstPoint) ); + BOOST_CONCEPT_ASSERT( (concepts::ConstPoint) ); + + // Get robust points (to be omitted later) + typedef typename RobustUniqueSubRange1::point_type robust_point1_type; + typedef typename RobustUniqueSubRange2::point_type robust_point2_type; + + point1_type const& p1 = range_p.at(0); + point1_type const& p2 = range_p.at(1); + point2_type const& q1 = range_q.at(0); + point2_type const& q2 = range_q.at(1); + + robust_point1_type const& robust_a1 = robust_range_p.at(0); + robust_point1_type const& robust_a2 = robust_range_p.at(1); + robust_point2_type const& robust_b1 = robust_range_q.at(0); + robust_point2_type const& robust_b2 = robust_range_q.at(1); using geometry::detail::equals::equals_point_point; bool const a_is_point = equals_point_point(robust_a1, robust_a2, point_in_point_strategy_type()); @@ -364,8 +371,11 @@ struct cartesian_segments if(a_is_point && b_is_point) { + // Take either a or b + model::referring_segment const d(p1, p2); + return equals_point_point(robust_a1, robust_b2, point_in_point_strategy_type()) - ? Policy::degenerate(a, true) + ? Policy::degenerate(d, true) : Policy::disjoint() ; } @@ -393,26 +403,26 @@ struct cartesian_segments typedef typename select_most_precise < - typename geometry::coordinate_type::type, - typename geometry::coordinate_type::type + typename geometry::coordinate_type::type, + typename geometry::coordinate_type::type >::type robust_coordinate_type; typedef typename segment_ratio_type < - typename geometry::point_type::type, // TODO: most precise point? + point1_type, // TODO: most precise point? RobustPolicy >::type ratio_type; segment_intersection_info < - typename select_calculation_type::type, + typename select_calculation_type::type, ratio_type > sinfo; - sinfo.dx_a = get<1, 0>(a) - get<0, 0>(a); // distance in x-dir - sinfo.dx_b = get<1, 0>(b) - get<0, 0>(b); - sinfo.dy_a = get<1, 1>(a) - get<0, 1>(a); // distance in y-dir - sinfo.dy_b = get<1, 1>(b) - get<0, 1>(b); + sinfo.dx_a = get<0>(p2) - get<0>(p1); // distance in x-dir + sinfo.dx_b = get<0>(q2) - get<0>(q1); + sinfo.dy_a = get<1>(p2) - get<1>(p1); // distance in y-dir + sinfo.dy_b = get<1>(q2) - get<1>(q1); robust_coordinate_type const robust_dx_a = get<0>(robust_a2) - get<0>(robust_a1); robust_coordinate_type const robust_dx_b = get<0>(robust_b2) - get<0>(robust_b1); @@ -455,6 +465,11 @@ struct cartesian_segments } } + // Declare segments, currently necessary for the policies + // (segment_crosses, segment_colinear, degenerate, one_degenerate, etc) + model::referring_segment const p(p1, p2); + model::referring_segment const q(q1, q2); + if (collinear) { std::pair const collinear_use_first @@ -471,21 +486,21 @@ struct cartesian_segments if (collinear_use_first.first) { - return relate_collinear<0, Policy, ratio_type>(a, b, + return relate_collinear<0, Policy, ratio_type>(p, q, robust_a1, robust_a2, robust_b1, robust_b2, a_is_point, b_is_point); } else { // Y direction contains larger segments (maybe dx is zero) - return relate_collinear<1, Policy, ratio_type>(a, b, + return relate_collinear<1, Policy, ratio_type>(p, q, robust_a1, robust_a2, robust_b1, robust_b2, a_is_point, b_is_point); } } } - return Policy::segments_crosses(sides, sinfo, a, b); + return Policy::segments_crosses(sides, sinfo, p, q); } private: diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index 1d6993350..cfeb9df04 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -246,44 +246,21 @@ struct geographic_segments // Relate segments a and b template < - typename Segment1, - typename Segment2, + typename UniqueSubRange1, + typename UniqueSubRange2, typename Policy, typename RobustPolicy > - inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b, - Policy const& policy, - RobustPolicy const& robust_policy) const + inline typename Policy::return_type apply(UniqueSubRange1 const& range_p, + UniqueSubRange2 const& range_q, + Policy const&, RobustPolicy const&) const { - typedef typename point_type::type point1_t; - typedef typename point_type::type point2_t; - point1_t a1, a2; - point2_t b1, b2; + typedef typename UniqueSubRange1::point_type point1_type; + typedef typename UniqueSubRange2::point_type point2_type; - detail::assign_point_from_index<0>(a, a1); - detail::assign_point_from_index<1>(a, a2); - detail::assign_point_from_index<0>(b, b1); - detail::assign_point_from_index<1>(b, b2); + BOOST_CONCEPT_ASSERT( (concepts::ConstPoint) ); + BOOST_CONCEPT_ASSERT( (concepts::ConstPoint) ); - return apply(a, b, policy, robust_policy, a1, a2, b1, b2); - } - - // Relate segments a and b - template - < - typename Segment1, - typename Segment2, - typename Policy, - typename RobustPolicy, - typename Point1, - typename Point2 - > - inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b, - Policy const&, RobustPolicy const&, - Point1 a1, Point1 a2, Point2 b1, Point2 b2) const - { - bool is_a_reversed = get<1>(a1) > get<1>(a2); - bool is_b_reversed = get<1>(b1) > get<1>(b2); /* typename coordinate_type::type const a1_lon = get<0>(a1), @@ -293,18 +270,22 @@ struct geographic_segments const b2_lon = get<0>(b2); bool is_a_reversed = a1_lon > a2_lon || a1_lon == a2_lon && get<1>(a1) > get<1>(a2); bool is_b_reversed = b1_lon > b2_lon || b1_lon == b2_lon && get<1>(b1) > get<1>(b2); - */ - if (is_a_reversed) - { - std::swap(a1, a2); - } + */ - if (is_b_reversed) - { - std::swap(b1, b2); - } + bool const is_p_reversed = get<1>(range_p.at(0)) > get<1>(range_p.at(1)); + bool const is_q_reversed = get<1>(range_q.at(0)) > get<1>(range_q.at(1)); - return apply(a, b, a1, a2, b1, b2, is_a_reversed, is_b_reversed); + point1_type const& p1 = range_p.at(is_p_reversed ? 1 : 0); + point1_type const& p2 = range_p.at(is_p_reversed ? 0 : 1); + point2_type const& q1 = range_q.at(is_q_reversed ? 1 : 0); + point2_type const& q2 = range_q.at(is_q_reversed ? 0 : 1); + + typedef model::referring_segment segment_type1; + typedef model::referring_segment segment_type2; + segment_type1 const p(p1, p2); + segment_type2 const q(q1, q2); + + return apply(p, q, p1, p2, q1, q2, is_p_reversed, is_q_reversed); } private: diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index e0d807a96..4f2bc0e67 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -252,43 +252,14 @@ struct ecef_segments // Relate segments a and b template < - typename Segment1, - typename Segment2, + typename UniqueSubRange1, + typename UniqueSubRange2, typename Policy, typename RobustPolicy > static inline typename Policy::return_type - apply(Segment1 const& a, Segment2 const& b, - Policy const& policy, RobustPolicy const& robust_policy) - { - typedef typename point_type::type point1_t; - typedef typename point_type::type point2_t; - point1_t a1, a2; - point2_t b1, b2; - - // TODO: use indexed_point_view if possible? - detail::assign_point_from_index<0>(a, a1); - detail::assign_point_from_index<1>(a, a2); - detail::assign_point_from_index<0>(b, b1); - detail::assign_point_from_index<1>(b, b2); - - return apply(a, b, policy, robust_policy, a1, a2, b1, b2); - } - - // Relate segments a and b - template - < - typename Segment1, - typename Segment2, - typename Policy, - typename RobustPolicy, - typename Point1, - typename Point2 - > - static inline typename Policy::return_type - apply(Segment1 const& a, Segment2 const& b, - Policy const&, RobustPolicy const&, - Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2) + apply(UniqueSubRange1 const& range_p, UniqueSubRange2 const& range_q, + Policy const&, RobustPolicy const&) { // For now create it using default constructor. In the future it could // be stored in strategy. However then apply() wouldn't be static and @@ -296,8 +267,21 @@ struct ecef_segments // Initialize explicitly to prevent compiler errors in case of PoD type CalcPolicy const calc_policy = CalcPolicy(); - BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); - BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); + typedef typename UniqueSubRange1::point_type point1_type; + typedef typename UniqueSubRange2::point_type point2_type; + + BOOST_CONCEPT_ASSERT( (concepts::ConstPoint) ); + BOOST_CONCEPT_ASSERT( (concepts::ConstPoint) ); + + point1_type const& a1 = range_p.at(0); + point1_type const& a2 = range_p.at(1); + point2_type const& b1 = range_q.at(0); + point2_type const& b2 = range_q.at(1); + + typedef model::referring_segment segment1_type; + typedef model::referring_segment segment2_type; + segment1_type const a(a1, a2); + segment2_type const b(b1, b2); // TODO: check only 2 first coordinates here? bool a_is_point = equals_point_point(a1, a2); @@ -312,7 +296,7 @@ struct ecef_segments } typedef typename select_calculation_type - ::type calc_t; + ::type calc_t; calc_t const c0 = 0; calc_t const c1 = 1; From 28eb87b4025a99b62dd90b7390773f938310544e Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 21 Aug 2019 10:43:58 +0200 Subject: [PATCH 37/49] [intersection] remove segment_ratio_type in most of the cases, and where they are still used, its definition is changed making use of the tags now available, and moving it to detail. It is to be deprecated --- .../buffer/buffered_piece_collection.hpp | 13 ++-- .../detail/disjoint/linear_linear.hpp | 31 +++------ .../detail/intersects/implementation.hpp | 11 +--- .../algorithms/detail/is_simple/linear.hpp | 9 +-- .../detail/is_valid/has_valid_self_turns.hpp | 2 +- .../overlay/get_intersection_points.hpp | 13 +--- .../detail/overlay/get_turn_info_helpers.hpp | 22 ++----- .../detail/overlay/intersection_insert.hpp | 63 +++++++++++-------- .../algorithms/detail/overlay/overlay.hpp | 2 +- .../algorithms/detail/overlay/turn_info.hpp | 4 +- .../algorithms/detail/relate/turns.hpp | 11 ++-- .../detail/touches/implementation.hpp | 20 ++---- .../algorithms/detail/overlay/dissolver.hpp | 1 + .../extensions/algorithms/dissolve.hpp | 2 + .../policies/robustness/no_rescale_policy.hpp | 11 ---- .../policies/robustness/rescale_policy.hpp | 8 --- .../robustness/segment_ratio_type.hpp | 31 +++++++-- .../strategies/cartesian/intersection.hpp | 25 ++------ .../strategies/geographic/intersection.hpp | 5 +- .../strategies/intersection_result.hpp | 11 ++-- .../strategies/intersection_strategies.hpp | 2 +- .../strategies/spherical/intersection.hpp | 5 +- test/algorithms/buffer/test_buffer_svg.hpp | 2 +- test/algorithms/overlay/get_turn_info.cpp | 2 +- test/algorithms/overlay/get_turns.cpp | 2 +- .../overlay/self_intersection_points.cpp | 14 +---- test/algorithms/overlay/sort_by_side.cpp | 2 +- .../algorithms/overlay/sort_by_side_basic.cpp | 2 +- test/algorithms/overlay/test_get_turns.hpp | 2 +- test/algorithms/overlay/traverse.cpp | 2 +- test/algorithms/overlay/traverse_ccw.cpp | 2 +- test/strategies/segment_intersection.cpp | 25 ++++---- .../segment_intersection_collinear.cpp | 40 +++++------- test/strategies/segment_intersection_sph.hpp | 8 ++- 34 files changed, 158 insertions(+), 247 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index b21dd4af0..de3f3c2b4 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -179,23 +179,22 @@ struct buffered_piece_collection typename IntersectionStrategy::cs_tag >::type rescale_policy_type; - typedef typename geometry::segment_ratio_type + typedef geometry::segment_ratio < - point_type, - RobustPolicy - >::type segment_ratio_type; + typename geometry::coordinate_type::type + > ratio_type; typedef buffer_turn_info < point_type, robust_point_type, - segment_ratio_type + ratio_type > buffer_turn_info_type; typedef buffer_turn_operation < point_type, - segment_ratio_type + ratio_type > buffer_turn_operation_type; typedef std::vector turn_vector_type; @@ -206,7 +205,7 @@ struct buffered_piece_collection int operation_index; robust_point_type point; segment_identifier seg_id; - segment_ratio_type fraction; + ratio_type fraction; }; struct piece diff --git a/include/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp b/include/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp index 744fa400f..cefa5e9f8 100644 --- a/include/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp @@ -38,7 +38,6 @@ #include #include -#include #include @@ -60,19 +59,7 @@ struct disjoint_segment { typedef typename point_type::type point_type; - // We don't need to rescale to detect disjointness - typedef no_rescale_policy rescale_policy_type; - rescale_policy_type robust_policy; - - typedef segment_intersection_points - < - point_type, - typename segment_ratio_type - < - point_type, - rescale_policy_type - >::type - > intersection_return_type; + typedef segment_intersection_points intersection_return_type; typedef policies::relate::segments_intersection_points < @@ -82,8 +69,7 @@ struct disjoint_segment detail::segment_as_subrange sub_range1(segment1); detail::segment_as_subrange sub_range2(segment2); intersection_return_type is = strategy.apply(sub_range1, sub_range2, - intersection_policy(), - robust_policy); + intersection_policy()); return is.count == 0; } @@ -108,18 +94,17 @@ struct disjoint_linear Strategy const& strategy) { typedef typename geometry::point_type::type point_type; - typedef detail::no_rescale_policy rescale_policy_type; - typedef typename geometry::segment_ratio_type + typedef geometry::segment_ratio < - point_type, rescale_policy_type - >::type segment_ratio_type; + typename coordinate_type::type + > ratio_type; typedef overlay::turn_info < point_type, - segment_ratio_type, + ratio_type, typename detail::get_turns::turn_operation_type < - Geometry1, Geometry2, segment_ratio_type + Geometry1, Geometry2, ratio_type >::type > turn_info_type; @@ -142,7 +127,7 @@ struct disjoint_linear Geometry1, Geometry2, assign_disjoint_policy > >::apply(0, geometry1, 1, geometry2, - strategy, rescale_policy_type(), turns, interrupt_policy); + strategy, detail::no_rescale_policy(), turns, interrupt_policy); return !interrupt_policy.has_intersections; } diff --git a/include/boost/geometry/algorithms/detail/intersects/implementation.hpp b/include/boost/geometry/algorithms/detail/intersects/implementation.hpp index f9b5402d0..cbb442ce4 100644 --- a/include/boost/geometry/algorithms/detail/intersects/implementation.hpp +++ b/include/boost/geometry/algorithms/detail/intersects/implementation.hpp @@ -29,7 +29,6 @@ #include #include #include -#include #include @@ -53,13 +52,8 @@ struct self_intersects < Geometry, Geometry >::type strategy_type; - typedef detail::no_rescale_policy rescale_policy_type; - typedef detail::overlay::turn_info - < - point_type, - typename segment_ratio_type::type - > turn_info; + typedef detail::overlay::turn_info turn_info; std::deque turns; @@ -69,14 +63,13 @@ struct self_intersects > turn_policy; strategy_type strategy; - rescale_policy_type robust_policy; detail::disjoint::disjoint_interrupt_policy policy; // TODO: skip_adjacent should be set to false detail::self_get_turn_points::get_turns < false, turn_policy - >::apply(geometry, strategy, robust_policy, turns, policy, 0, true); + >::apply(geometry, strategy, detail::no_rescale_policy(), turns, policy, 0, true); return policy.has_intersections; } }; diff --git a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp index 57bc295e5..a9a3aaf31 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -206,14 +206,7 @@ inline bool has_self_intersections(Linear const& linear, Strategy const& strateg typedef typename point_type::type point_type; // compute self turns - typedef detail::overlay::turn_info - < - point_type, - geometry::segment_ratio - < - typename geometry::coordinate_type::type - > - > turn_info; + typedef detail::overlay::turn_info turn_info; std::deque turns; diff --git a/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp b/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp index 09a0d3745..e99e41133 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp @@ -63,7 +63,7 @@ public: typedef detail::overlay::turn_info < point_type, - typename geometry::segment_ratio_type + typename segment_ratio_type < point_type, rescale_policy_type diff --git a/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp index 2ed5d9a8c..4b0e9ed79 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp @@ -55,7 +55,7 @@ struct get_turn_without_info UniqueSubRange2 const& range_q, TurnInfo const& , Strategy const& strategy, - RobustPolicy const& robust_policy, + RobustPolicy const& , OutputIterator out) { // Make sure this is only called with no rescaling @@ -69,18 +69,11 @@ struct get_turn_without_info typedef policies::relate::segments_intersection_points < - segment_intersection_points - < - turn_point_type, - typename geometry::segment_ratio_type - < - turn_point_type, RobustPolicy - >::type - > + segment_intersection_points > policy_type; typename policy_type::return_type const result - = strategy.apply(range_p, range_q, policy_type(), robust_policy); + = strategy.apply(range_p, range_q, policy_type()); for (std::size_t i = 0; i < result.count; i++) { diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp index c485abaa7..734b3051b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -247,10 +247,7 @@ public: typedef segment_intersection_points < TurnPoint, - typename geometry::segment_ratio_type - < - TurnPoint, RobustPolicy - >::type + geometry::segment_ratio > intersection_point_type; typedef policies::relate::segments_tupled < @@ -294,7 +291,6 @@ public: umbrella_strategy.get_side_strategy()) , m_result(umbrella_strategy.apply(range_p, range_q, intersection_policy_type(), - robust_policy, m_robust_range_p, m_robust_range_q)) {} @@ -346,14 +342,8 @@ class intersection_info_base { public: - typedef segment_intersection_points - < - TurnPoint, - typename geometry::segment_ratio_type - < - TurnPoint, RobustPolicy - >::type - > intersection_point_type; + + typedef segment_intersection_points intersection_point_type; typedef policies::relate::segments_tupled < policies::relate::segments_intersection_points @@ -382,14 +372,12 @@ public: intersection_info_base(UniqueSubRange1 const& range_p, UniqueSubRange2 const& range_q, UmbrellaStrategy const& umbrella_strategy, - no_rescale_policy const& robust_policy) + no_rescale_policy const& ) : m_range_p(range_p) , m_range_q(range_q) , m_side_calc(range_p, range_q, umbrella_strategy.get_side_strategy()) - , m_result(umbrella_strategy.apply(range_p, range_q, - intersection_policy_type(), - robust_policy)) + , m_result(umbrella_strategy.apply(range_p, range_q, intersection_policy_type())) {} inline bool p_is_last_segment() const { return m_range_p.is_last_segment(); } diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index 1f9b39502..f7ed38773 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -72,7 +72,7 @@ struct intersection_segment_segment_point > static inline OutputIterator apply(Segment1 const& segment1, Segment2 const& segment2, - RobustPolicy const& robust_policy, + RobustPolicy const& , OutputIterator out, Strategy const& strategy) { @@ -86,14 +86,7 @@ struct intersection_segment_segment_point typedef typename point_type::type point_type; // Get the intersection point (or two points) - typedef segment_intersection_points - < - point_type, - typename segment_ratio_type - < - point_type, RobustPolicy - >::type - > intersection_return_type; + typedef segment_intersection_points intersection_return_type; typedef policies::relate::segments_intersection_points < @@ -104,8 +97,7 @@ struct intersection_segment_segment_point detail::segment_as_subrange sub_range2(segment2); intersection_return_type - is = strategy.apply(sub_range1, sub_range2, - policy_type(), robust_policy); + is = strategy.apply(sub_range1, sub_range2, policy_type()); for (std::size_t i = 0; i < is.count; i++) { @@ -133,13 +125,14 @@ struct intersection_linestring_linestring_point OutputIterator out, Strategy const& strategy) { - typedef typename point_type::type point_type; + // Make sure this is only called with no rescaling + BOOST_STATIC_ASSERT((boost::is_same + < + no_rescale_policy_tag, + typename rescale_policy_type::type + >::value)); - typedef detail::overlay::turn_info - < - point_type, - typename segment_ratio_type::type - > turn_info; + typedef detail::overlay::turn_info turn_info; std::deque turns; geometry::get_intersection_points(linestring1, linestring2, @@ -397,6 +390,13 @@ struct intersection_of_linestring_with_areal OutputIterator out, Strategy const& strategy) { + // Make sure this is only called with no rescaling + BOOST_STATIC_ASSERT((boost::is_same + < + no_rescale_policy_tag, + typename rescale_policy_type::type + >::value)); + if (boost::size(linestring) == 0) { return out; @@ -412,21 +412,26 @@ struct intersection_of_linestring_with_areal > follower; typedef typename point_type::type point_type; + + typedef geometry::segment_ratio + < + typename coordinate_type::type + > ratio_type; + #ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR typedef detail::overlay::traversal_turn_info < - point_type, - typename geometry::segment_ratio_type::type + point_type, ratio_type > turn_info; #else typedef detail::overlay::turn_info < point_type, - typename geometry::segment_ratio_type::type, + ratio_type, detail::overlay::turn_operation_linear < point_type, - typename geometry::segment_ratio_type::type + ratio_type > > turn_info; #endif @@ -591,19 +596,23 @@ struct intersection_linear_areal_point OutputIterator out, Strategy const& strategy) { - typedef typename geometry::segment_ratio_type - < - PointOut, RobustPolicy - >::type segment_ratio_type; + // Make sure this is only called with no rescaling + BOOST_STATIC_ASSERT((boost::is_same + < + no_rescale_policy_tag, + typename rescale_policy_type::type + >::value)); + + typedef geometry::segment_ratio::type> ratio_type; typedef detail::overlay::turn_info < PointOut, - segment_ratio_type, + ratio_type, detail::overlay::turn_operation_linear < PointOut, - segment_ratio_type + ratio_type > > turn_info; diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 6bda53e93..ab2d1c406 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -287,7 +287,7 @@ struct overlay typedef detail::overlay::traversal_turn_info < point_type, - typename geometry::segment_ratio_type::type + typename segment_ratio_type::type > turn_info; typedef std::deque turn_container_type; diff --git a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp index e976bdb03..5f4569bd6 100644 --- a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace boost { namespace geometry { @@ -33,7 +34,6 @@ enum method_type method_touch_interior, method_collinear, method_equal, - method_start, method_error }; @@ -77,7 +77,7 @@ struct turn_operation template < typename Point, - typename SegmentRatio, + typename SegmentRatio = geometry::segment_ratio::type>, typename Operation = turn_operation, typename Container = boost::array > diff --git a/include/boost/geometry/algorithms/detail/relate/turns.hpp b/include/boost/geometry/algorithms/detail/relate/turns.hpp index 1ea6c4dba..d4439c64a 100644 --- a/include/boost/geometry/algorithms/detail/relate/turns.hpp +++ b/include/boost/geometry/algorithms/detail/relate/turns.hpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include @@ -70,17 +70,14 @@ struct get_turns > struct turn_info_type { + typedef typename segment_ratio_type::type ratio_type; typedef overlay::turn_info < point1_type, - typename segment_ratio_type::type, + ratio_type, typename detail::get_turns::turn_operation_type < - Geometry1, Geometry2, - typename segment_ratio_type - < - point1_type, RobustPolicy - >::type + Geometry1, Geometry2, ratio_type >::type > type; }; diff --git a/include/boost/geometry/algorithms/detail/touches/implementation.hpp b/include/boost/geometry/algorithms/detail/touches/implementation.hpp index 0cdb8ad1d..3ecab2d51 100644 --- a/include/boost/geometry/algorithms/detail/touches/implementation.hpp +++ b/include/boost/geometry/algorithms/detail/touches/implementation.hpp @@ -254,23 +254,17 @@ struct areal_areal Geometry2 const& geometry2, IntersectionStrategy const& strategy) { - typedef detail::no_rescale_policy rescale_policy_type; typedef typename geometry::point_type::type point_type; - typedef detail::overlay::turn_info - < - point_type, - typename segment_ratio_type::type - > turn_info; + typedef detail::overlay::turn_info turn_info; std::deque turns; detail::touches::areal_interrupt_policy policy; - rescale_policy_type robust_policy; boost::geometry::get_turns < detail::overlay::do_reverse::value>::value, detail::overlay::do_reverse::value>::value, detail::overlay::assign_null_policy - >(geometry1, geometry2, strategy, robust_policy, turns, policy); + >(geometry1, geometry2, strategy, detail::no_rescale_policy(), turns, policy); return policy.result() && ! geometry::detail::touches::rings_containing(geometry1, geometry2, strategy) @@ -427,13 +421,8 @@ struct self_touches < Geometry, Geometry >::type strategy_type; - typedef detail::no_rescale_policy rescale_policy_type; typedef typename geometry::point_type::type point_type; - typedef detail::overlay::turn_info - < - point_type, - typename segment_ratio_type::type - > turn_info; + typedef detail::overlay::turn_info turn_info; typedef detail::overlay::get_turn_info < @@ -443,12 +432,11 @@ struct self_touches std::deque turns; detail::touches::areal_interrupt_policy policy; strategy_type strategy; - rescale_policy_type robust_policy; // TODO: skip_adjacent should be set to false detail::self_get_turn_points::get_turns < false, policy_type - >::apply(geometry, strategy, robust_policy, turns, policy, 0, true); + >::apply(geometry, strategy, detail::no_rescale_policy(), turns, policy, 0, true); return policy.result(); } diff --git a/include/boost/geometry/extensions/algorithms/detail/overlay/dissolver.hpp b/include/boost/geometry/extensions/algorithms/detail/overlay/dissolver.hpp index fa0c15975..b3ed0a2d4 100644 --- a/include/boost/geometry/extensions/algorithms/detail/overlay/dissolver.hpp +++ b/include/boost/geometry/extensions/algorithms/detail/overlay/dissolver.hpp @@ -33,6 +33,7 @@ #include #include +#include #include diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index 902531cbc..11649edfd 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -43,6 +43,8 @@ #include +#include + #include #include diff --git a/include/boost/geometry/policies/robustness/no_rescale_policy.hpp b/include/boost/geometry/policies/robustness/no_rescale_policy.hpp index 8d23cb370..211c45a1b 100644 --- a/include/boost/geometry/policies/robustness/no_rescale_policy.hpp +++ b/include/boost/geometry/policies/robustness/no_rescale_policy.hpp @@ -17,7 +17,6 @@ #include #include #include -#include namespace boost { namespace geometry { @@ -51,16 +50,6 @@ struct robust_point_type typedef Point type; }; -template -struct segment_ratio_type -{ - // Define a segment_ratio defined on coordinate type, e.g. - // int/int or float/float - typedef typename geometry::coordinate_type::type coordinate_type; - typedef segment_ratio type; -}; - - }} // namespace boost::geometry #endif // BOOST_GEOMETRY_POLICIES_ROBUSTNESS_NO_RESCALE_POLICY_HPP diff --git a/include/boost/geometry/policies/robustness/rescale_policy.hpp b/include/boost/geometry/policies/robustness/rescale_policy.hpp index e4a909056..9394f8d9d 100644 --- a/include/boost/geometry/policies/robustness/rescale_policy.hpp +++ b/include/boost/geometry/policies/robustness/rescale_policy.hpp @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -77,13 +76,6 @@ struct robust_point_type -struct segment_ratio_type > -{ - typedef segment_ratio type; -}; - }} // namespace boost::geometry diff --git a/include/boost/geometry/policies/robustness/segment_ratio_type.hpp b/include/boost/geometry/policies/robustness/segment_ratio_type.hpp index 19e935bbb..fa861c206 100644 --- a/include/boost/geometry/policies/robustness/segment_ratio_type.hpp +++ b/include/boost/geometry/policies/robustness/segment_ratio_type.hpp @@ -12,17 +12,38 @@ #ifndef BOOST_GEOMETRY_POLICIES_ROBUSTNESS_SEGMENT_RATIO_TYPE_HPP #define BOOST_GEOMETRY_POLICIES_ROBUSTNESS_SEGMENT_RATIO_TYPE_HPP -#include +#include +#include -namespace boost { namespace geometry +#include +#include + +namespace boost { namespace geometry { namespace detail { -// Meta-function to access segment-ratio for a policy +// Temporary meta-function to access segment-ratio for a policy template -struct segment_ratio_type {}; // : not_implemented<> {}; +struct segment_ratio_type +{ + // Type in segment ratio is either the coordinate type, or for + // deprecated robust point types it is a long_long type + typedef typename boost::mpl::if_c + < + boost::is_same + < + typename rescale_policy_type::type, + no_rescale_policy_tag + >::value, + typename geometry::coordinate_type::type, + boost::long_long_type + >::type coordinate_type; + + // Define segment ratio based on the coordinate type + typedef geometry::segment_ratio type; +}; -}} // namespace boost::geometry +}}} // namespace boost::geometry::deatil #endif // BOOST_GEOMETRY_POLICIES_ROBUSTNESS_SEGMENT_RATIO_TYPE_HPP diff --git a/include/boost/geometry/strategies/cartesian/intersection.hpp b/include/boost/geometry/strategies/cartesian/intersection.hpp index 6adbef6b4..246f68ce2 100644 --- a/include/boost/geometry/strategies/cartesian/intersection.hpp +++ b/include/boost/geometry/strategies/cartesian/intersection.hpp @@ -52,8 +52,6 @@ #include #include -#include - #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) # include @@ -307,24 +305,15 @@ struct cartesian_segments < typename UniqueSubRange1, typename UniqueSubRange2, - typename Policy, - typename RobustPolicy + typename Policy > static inline typename Policy::return_type apply(UniqueSubRange1 const& range_p, UniqueSubRange2 const& range_q, - Policy const& policy, - RobustPolicy const& robust_policy) + Policy const& policy) { - // Make sure this is only called when there is no rescaling - BOOST_STATIC_ASSERT((boost::is_same - < - detail::no_rescale_policy_tag, - typename detail::rescale_policy_type::type - >::value)); - // Pass the same ranges both as normal ranges and as robust ranges - return apply(range_p, range_q, policy, robust_policy, range_p, range_q); + return apply(range_p, range_q, policy, range_p, range_q); } // Main entry-routine, calculating intersections of segments p / q @@ -333,7 +322,6 @@ struct cartesian_segments typename UniqueSubRange1, typename UniqueSubRange2, typename Policy, - typename RobustPolicy, typename RobustUniqueSubRange1, typename RobustUniqueSubRange2 > @@ -341,7 +329,6 @@ struct cartesian_segments apply(UniqueSubRange1 const& range_p, UniqueSubRange2 const& range_q, Policy const&, - RobustPolicy const& , RobustUniqueSubRange1 const& robust_range_p, RobustUniqueSubRange2 const& robust_range_q) { @@ -407,11 +394,7 @@ struct cartesian_segments typename geometry::coordinate_type::type >::type robust_coordinate_type; - typedef typename segment_ratio_type - < - point1_type, // TODO: most precise point? - RobustPolicy - >::type ratio_type; + typedef segment_ratio ratio_type; segment_intersection_info < diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index cfeb9df04..345a722f2 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -248,12 +248,11 @@ struct geographic_segments < typename UniqueSubRange1, typename UniqueSubRange2, - typename Policy, - typename RobustPolicy + typename Policy > inline typename Policy::return_type apply(UniqueSubRange1 const& range_p, UniqueSubRange2 const& range_q, - Policy const&, RobustPolicy const&) const + Policy const&) const { typedef typename UniqueSubRange1::point_type point1_type; typedef typename UniqueSubRange2::point_type point2_type; diff --git a/include/boost/geometry/strategies/intersection_result.hpp b/include/boost/geometry/strategies/intersection_result.hpp index db2a46e6a..4b5aa1c46 100644 --- a/include/boost/geometry/strategies/intersection_result.hpp +++ b/include/boost/geometry/strategies/intersection_result.hpp @@ -13,12 +13,9 @@ #ifndef BOOST_GEOMETRY_STRATEGIES_INTERSECTION_RESULT_HPP #define BOOST_GEOMETRY_STRATEGIES_INTERSECTION_RESULT_HPP -#if defined(HAVE_MATRIX_AS_STRING) -#include -#endif - #include +#include namespace boost { namespace geometry @@ -57,7 +54,11 @@ struct fraction_type \brief return-type for segment-intersection \note Set in intersection_points.hpp, from segment_intersection_info */ -template +template +< + typename Point, + typename SegmentRatio = segment_ratio::type> +> struct segment_intersection_points { std::size_t count; // The number of intersection points diff --git a/include/boost/geometry/strategies/intersection_strategies.hpp b/include/boost/geometry/strategies/intersection_strategies.hpp index a17350580..5b7f85e73 100644 --- a/include/boost/geometry/strategies/intersection_strategies.hpp +++ b/include/boost/geometry/strategies/intersection_strategies.hpp @@ -63,7 +63,7 @@ private : typedef segment_intersection_points < IntersectionPoint, - typename geometry::segment_ratio_type + typename detail::segment_ratio_type < IntersectionPoint, RobustPolicy >::type diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index 4f2bc0e67..8c9014cfd 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -254,12 +254,11 @@ struct ecef_segments < typename UniqueSubRange1, typename UniqueSubRange2, - typename Policy, - typename RobustPolicy + typename Policy > static inline typename Policy::return_type apply(UniqueSubRange1 const& range_p, UniqueSubRange2 const& range_q, - Policy const&, RobustPolicy const&) + Policy const&) { // For now create it using default constructor. In the future it could // be stored in strategy. However then apply() wouldn't be static and diff --git a/test/algorithms/buffer/test_buffer_svg.hpp b/test/algorithms/buffer/test_buffer_svg.hpp index 9642d6233..a898bd4ef 100644 --- a/test/algorithms/buffer/test_buffer_svg.hpp +++ b/test/algorithms/buffer/test_buffer_svg.hpp @@ -390,7 +390,7 @@ public : typedef bg::detail::overlay::turn_info < Point, - typename bg::segment_ratio_type::type + typename bg::detail::segment_ratio_type::type > turn_info; std::vector turns; diff --git a/test/algorithms/overlay/get_turn_info.cpp b/test/algorithms/overlay/get_turn_info.cpp index 0a815f8f1..3835f6f47 100644 --- a/test/algorithms/overlay/get_turn_info.cpp +++ b/test/algorithms/overlay/get_turn_info.cpp @@ -84,7 +84,7 @@ void test_with_point(std::string const& caseid, typedef bg::detail::overlay::turn_info < P, - typename bg::segment_ratio_type::type + typename bg::detail::segment_ratio_type::type > turn_info; typedef std::vector tp_vector; turn_info model; diff --git a/test/algorithms/overlay/get_turns.cpp b/test/algorithms/overlay/get_turns.cpp index 303903bfc..b32e2eba3 100644 --- a/test/algorithms/overlay/get_turns.cpp +++ b/test/algorithms/overlay/get_turns.cpp @@ -80,7 +80,7 @@ struct test_get_turns typedef bg::detail::overlay::turn_info < point_type, - typename bg::segment_ratio_type::type + typename bg::detail::segment_ratio_type::type > turn_info; std::vector turns; diff --git a/test/algorithms/overlay/self_intersection_points.cpp b/test/algorithms/overlay/self_intersection_points.cpp index bf10e2fae..2d7a42039 100644 --- a/test/algorithms/overlay/self_intersection_points.cpp +++ b/test/algorithms/overlay/self_intersection_points.cpp @@ -60,22 +60,12 @@ static void test_self_intersection_points(std::string const& case_id, typename bg::cs_tag::type >::type strategy_type; typedef bg::detail::no_rescale_policy rescale_policy_type; - typedef bg::detail::overlay::turn_info - < - point_type, - typename bg::segment_ratio_type - < - point_type, rescale_policy_type - >::type - > turn_info; + typedef bg::detail::overlay::turn_info turn_info; std::vector turns; strategy_type strategy; - rescale_policy_type rescale_policy - ; - // = bg::get_rescale_policy(geometry); - ///bg::get_intersection_points(geometry, turns); + rescale_policy_type rescale_policy; bg::detail::self_get_turn_points::no_interrupt_policy policy; bg::self_turns diff --git a/test/algorithms/overlay/sort_by_side.cpp b/test/algorithms/overlay/sort_by_side.cpp index 8acfe73df..1e0f42258 100644 --- a/test/algorithms/overlay/sort_by_side.cpp +++ b/test/algorithms/overlay/sort_by_side.cpp @@ -137,7 +137,7 @@ std::vector apply_overlay( typedef bg::detail::overlay::traversal_turn_info < point_type, - typename bg::segment_ratio_type::type + typename bg::detail::segment_ratio_type::type > turn_info; typedef std::deque turn_container_type; diff --git a/test/algorithms/overlay/sort_by_side_basic.cpp b/test/algorithms/overlay/sort_by_side_basic.cpp index d77e83ddc..f37bd38c6 100644 --- a/test/algorithms/overlay/sort_by_side_basic.cpp +++ b/test/algorithms/overlay/sort_by_side_basic.cpp @@ -74,7 +74,7 @@ std::vector apply_get_turns(std::string const& case_id, typedef bg::detail::overlay::turn_info < point_type, - typename bg::segment_ratio_type::type + typename bg::detail::segment_ratio_type::type > turn_info; typedef std::deque turn_container_type; diff --git a/test/algorithms/overlay/test_get_turns.hpp b/test/algorithms/overlay/test_get_turns.hpp index 4bc226b57..f761a996d 100644 --- a/test/algorithms/overlay/test_get_turns.hpp +++ b/test/algorithms/overlay/test_get_turns.hpp @@ -171,7 +171,7 @@ void check_geometry_range(Geometry1 const& g1, typedef bg::detail::no_rescale_policy robust_policy_type; typedef typename bg::point_type::type point_type; - typedef typename bg::segment_ratio_type + typedef typename bg::detail::segment_ratio_type < point_type, robust_policy_type >::type segment_ratio_type; diff --git a/test/algorithms/overlay/traverse.cpp b/test/algorithms/overlay/traverse.cpp index 256d905f9..8f5225091 100644 --- a/test/algorithms/overlay/traverse.cpp +++ b/test/algorithms/overlay/traverse.cpp @@ -164,7 +164,7 @@ struct test_traverse typedef bg::detail::overlay::traversal_turn_info < point_type, - typename bg::segment_ratio_type::type + typename bg::detail::segment_ratio_type::type > turn_info; std::vector turns; diff --git a/test/algorithms/overlay/traverse_ccw.cpp b/test/algorithms/overlay/traverse_ccw.cpp index c3d5d2068..f93980860 100644 --- a/test/algorithms/overlay/traverse_ccw.cpp +++ b/test/algorithms/overlay/traverse_ccw.cpp @@ -59,7 +59,7 @@ intersect(Geometry1 const& g1, Geometry2 const& g2, std::string const& name, typedef bg::detail::overlay::traversal_turn_info < point_type, - typename bg::segment_ratio_type::type + typename bg::detail::segment_ratio_type::type > turn_info; std::vector turns; diff --git a/test/strategies/segment_intersection.cpp b/test/strategies/segment_intersection.cpp index daf6e13ea..5bccc1d43 100644 --- a/test/strategies/segment_intersection.cpp +++ b/test/strategies/segment_intersection.cpp @@ -30,7 +30,7 @@ #include #include - +#include #include #include @@ -60,6 +60,9 @@ static void test_segment_intersection(int caseid, segment_type s12(p1,p2); segment_type s34(p3,p4); + bg::detail::segment_as_subrange sr12(s12); + bg::detail::segment_as_subrange sr34(s34); + std::size_t expected_count = 0; if (expected_x1 != -99 && expected_y1 != -99) @@ -74,17 +77,12 @@ static void test_segment_intersection(int caseid, // Using intersection_insert std::vector

out; - bg::detail::intersection::intersection_insert

(s12, s34, std::back_inserter(out)); + bg::detail::intersection::intersection_insert

(s12, s34, + std::back_inserter(out)); // Using strategy - typedef bg::detail::no_rescale_policy rescale_policy_type; - rescale_policy_type rescale_policy; - typedef typename bg::segment_ratio_type::type ratio_type; - typedef bg::segment_intersection_points - < - P, - ratio_type - > result_type; + typedef bg::segment_intersection_points

result_type; + typedef bg::policies::relate::segments_intersection_points < result_type @@ -92,14 +90,13 @@ static void test_segment_intersection(int caseid, result_type is = bg::strategy::intersection::cartesian_segments<> - ::apply(s12, s34, points_policy_type(), rescale_policy, p1, p2, p3, p4); + ::apply(sr12, sr34, points_policy_type()); bg::policies::relate::direction_type dir = bg::strategy::intersection::cartesian_segments<> - ::apply(s12, s34, bg::policies::relate::segments_direction(), - rescale_policy, p1, p2, p3, p4); + ::apply(sr12, sr34, bg::policies::relate::segments_direction()); - BOOST_CHECK_EQUAL(boost::size(out), expected_count); + //BOOST_CHECK_EQUAL(boost::size(out), expected_count); BOOST_CHECK_EQUAL(is.count, expected_count); BOOST_CHECK_MESSAGE(dir.how == expected_how, caseid diff --git a/test/strategies/segment_intersection_collinear.cpp b/test/strategies/segment_intersection_collinear.cpp index 9ab27c94f..287888f92 100644 --- a/test/strategies/segment_intersection_collinear.cpp +++ b/test/strategies/segment_intersection_collinear.cpp @@ -29,7 +29,7 @@ #include #include - +#include #include #include @@ -72,21 +72,13 @@ static void test_segment_intersection(std::string const& case_id, bg::assign_values(p3, x3, y3); bg::assign_values(p4, x4, y4); - segment_type s12(p1,p2); - segment_type s34(p3,p4); + segment_type s12(p1, p2); + segment_type s34(p3, p4); - typedef bg::detail::no_rescale_policy rescale_policy_type; - rescale_policy_type rescale_policy; + bg::detail::segment_as_subrange sr12(s12); + bg::detail::segment_as_subrange sr34(s34); - typedef bg::segment_intersection_points - < - P, - typename bg::segment_ratio_type - < - P, - rescale_policy_type - >::type - > result_type; + typedef bg::segment_intersection_points

result_type; typedef bg::policies::relate::segments_intersection_points < @@ -96,13 +88,12 @@ static void test_segment_intersection(std::string const& case_id, // Get the intersection point (or two points) result_type is = bg::strategy::intersection::cartesian_segments<> - ::apply(s12, s34, points_policy_type(), rescale_policy, p1, p2, p3, p4); + ::apply(sr12, sr34, points_policy_type()); // Get just a character for Left/Right/intersects/etc, purpose is more for debugging bg::policies::relate::direction_type dir = bg::strategy::intersection::cartesian_segments<> - ::apply(s12, s34, bg::policies::relate::segments_direction(), - rescale_policy, p1, p2, p3, p4); + ::apply(sr12, sr34, bg::policies::relate::segments_direction()); std::size_t expected_count = check(is, 0, expected_x1, expected_y1) @@ -138,15 +129,10 @@ static void test_segment_ratio(std::string const& case_id, segment_type s12(p1, p2); segment_type s34(p3, p4); - typedef bg::detail::no_rescale_policy rescale_policy_type; - rescale_policy_type rescale_policy; + bg::detail::segment_as_subrange sr12(s12); + bg::detail::segment_as_subrange sr34(s34); - typedef typename bg::segment_ratio_type::type ratio_type; - typedef bg::segment_intersection_points - < - P, - ratio_type - > result_type; + typedef bg::segment_intersection_points

result_type; typedef bg::policies::relate::segments_intersection_points < @@ -156,7 +142,9 @@ static void test_segment_ratio(std::string const& case_id, // Get the intersection point (or two points) result_type is = bg::strategy::intersection::cartesian_segments<> - ::apply(s12, s34, points_policy_type(), rescale_policy, p1, p2, p3, p4); + ::apply(sr12, sr34, points_policy_type()); + + typedef bg::segment_ratio::type> ratio_type; ratio_type expected_a1(expected_pair_a1.first, expected_pair_a1.second); ratio_type expected_a2(expected_pair_a2.first, expected_pair_a2.second); diff --git a/test/strategies/segment_intersection_sph.hpp b/test/strategies/segment_intersection_sph.hpp index bb12c16f8..687d34022 100644 --- a/test/strategies/segment_intersection_sph.hpp +++ b/test/strategies/segment_intersection_sph.hpp @@ -26,6 +26,8 @@ #include #include +#include + template bool equals_relaxed_val(T const& v1, T const& v2, T const& eps_scale) { @@ -75,8 +77,10 @@ void test_strategy_one(S1 const& s1, S2 const& s2, typedef typename policy_t::return_type return_type; - // NOTE: robust policy is currently ignored - return_type res = strategy.apply(s1, s2, policy_t(), 0); + bg::detail::segment_as_subrange sr1(s1); + bg::detail::segment_as_subrange sr2(s2); + + return_type res = strategy.apply(sr1, sr2, policy_t()); size_t const res_count = boost::get<0>(res).count; char const res_method = boost::get<1>(res).how; From b04e7fe3d622996ac45fcd8896ee1311d7e94274 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 21 Aug 2019 17:15:30 +0200 Subject: [PATCH 38/49] [intersection] fix policy type and geographic intersection strategy, which need to pass original segments and ordered points --- .../detail/overlay/intersection_insert.hpp | 5 +++-- .../strategies/geographic/intersection.hpp | 21 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index f7ed38773..9232b0f40 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -1286,9 +1286,10 @@ inline OutputIterator intersection_insert(Geometry1 const& geometry1, concepts::check(); concepts::check(); - typedef typename geometry::rescale_policy_type + typedef typename geometry::rescale_overlay_policy_type < - typename geometry::point_type::type, + Geometry1, + Geometry2, typename Strategy::cs_tag >::type rescale_policy_type; diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index 345a722f2..36fcbfa97 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -256,6 +256,8 @@ struct geographic_segments { typedef typename UniqueSubRange1::point_type point1_type; typedef typename UniqueSubRange2::point_type point2_type; + typedef model::referring_segment segment_type1; + typedef model::referring_segment segment_type2; BOOST_CONCEPT_ASSERT( (concepts::ConstPoint) ); BOOST_CONCEPT_ASSERT( (concepts::ConstPoint) ); @@ -274,17 +276,14 @@ struct geographic_segments bool const is_p_reversed = get<1>(range_p.at(0)) > get<1>(range_p.at(1)); bool const is_q_reversed = get<1>(range_q.at(0)) > get<1>(range_q.at(1)); - point1_type const& p1 = range_p.at(is_p_reversed ? 1 : 0); - point1_type const& p2 = range_p.at(is_p_reversed ? 0 : 1); - point2_type const& q1 = range_q.at(is_q_reversed ? 1 : 0); - point2_type const& q2 = range_q.at(is_q_reversed ? 0 : 1); - - typedef model::referring_segment segment_type1; - typedef model::referring_segment segment_type2; - segment_type1 const p(p1, p2); - segment_type2 const q(q1, q2); - - return apply(p, q, p1, p2, q1, q2, is_p_reversed, is_q_reversed); + // Call apply with original segments and ordered points + return apply(segment_type1(range_p.at(0), range_p.at(1)), + segment_type2(range_q.at(0), range_q.at(1)), + range_p.at(is_p_reversed ? 1 : 0), + range_p.at(is_p_reversed ? 0 : 1), + range_q.at(is_q_reversed ? 1 : 0), + range_q.at(is_q_reversed ? 0 : 1), + is_p_reversed, is_q_reversed); } private: From a39bf3ff13751add2cbcbbb69f2ba904a0c79f2e Mon Sep 17 00:00:00 2001 From: Tinko Bartels Date: Tue, 13 Aug 2019 00:11:09 +0200 Subject: [PATCH 39/49] Add robust cartesian side and in_circle predicates. --- extensions/test/Jamfile.v2 | 2 +- extensions/test/triangulation/Jamfile.v2 | 12 + .../test/triangulation/in_circle_robust.cpp | 56 ++ extensions/test/triangulation/side_robust.cpp | 59 ++ .../cartesian/detail/precise_math.hpp | 588 ++++++++++++++++++ .../strategies/cartesian/in_circle_robust.hpp | 53 ++ .../strategies/cartesian/side_robust.hpp | 92 +++ 7 files changed, 861 insertions(+), 1 deletion(-) create mode 100644 extensions/test/triangulation/Jamfile.v2 create mode 100644 extensions/test/triangulation/in_circle_robust.cpp create mode 100644 extensions/test/triangulation/side_robust.cpp create mode 100644 include/boost/geometry/extensions/triangulation/strategies/cartesian/detail/precise_math.hpp create mode 100644 include/boost/geometry/extensions/triangulation/strategies/cartesian/in_circle_robust.hpp create mode 100644 include/boost/geometry/extensions/triangulation/strategies/cartesian/side_robust.hpp diff --git a/extensions/test/Jamfile.v2 b/extensions/test/Jamfile.v2 index f35b9a1f9..7b0af8bed 100644 --- a/extensions/test/Jamfile.v2 +++ b/extensions/test/Jamfile.v2 @@ -27,4 +27,4 @@ build-project algorithms ; build-project gis ; build-project iterators ; build-project nsphere ; - +build-project triangulation ; diff --git a/extensions/test/triangulation/Jamfile.v2 b/extensions/test/triangulation/Jamfile.v2 new file mode 100644 index 000000000..ad0b333c8 --- /dev/null +++ b/extensions/test/triangulation/Jamfile.v2 @@ -0,0 +1,12 @@ +# Boost.Geometry (aka GGL, Generic Geometry Library) +# +# Use, modification and distribution is subject to the Boost Software License, +# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +test-suite boost-geometry-extensions-triangulation + : + [ run side_robust.cpp ] + [ run in_circle_robust.cpp ] + ; + diff --git a/extensions/test/triangulation/in_circle_robust.cpp b/extensions/test/triangulation/in_circle_robust.cpp new file mode 100644 index 000000000..ea2d14a01 --- /dev/null +++ b/extensions/test/triangulation/in_circle_robust.cpp @@ -0,0 +1,56 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2019 Tinko Bartels, Berlin, Germany. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include + +template +void test_all() +{ + typedef bg::strategy::in_circle::in_circle_robust inc2; + typedef bg::strategy::in_circle::in_circle_robust inc1; + typedef bg::strategy::in_circle::in_circle_robust inc0; + + P col1(0.0, 0.0), col2(1.0, 0.0), col3(0.0, 1.0); + P in(0.5,0.5) , on(1.0, 0.0), out(-0.5, -0.5); + int in2 = inc2::apply(col1, col2, col3, in); + BOOST_CHECK_GT(in2, 0); + int in1 = inc1::apply(col1, col2, col3, in); + BOOST_CHECK_GT(in1, 0); + int in0 = inc0::apply(col1, col2, col3, in); + BOOST_CHECK_GT(in0, 0); + + int on2 = inc2::apply(col1, col2, col3, on); + BOOST_CHECK_EQUAL(on2, 0); + int on1 = inc1::apply(col1, col2, col3, on); + BOOST_CHECK_EQUAL(on1, 0); + int on0 = inc0::apply(col1, col2, col3, on); + BOOST_CHECK_EQUAL(on0, 0); + + int out2 = inc2::apply(col1, col2, col3, out); + BOOST_CHECK_GT(0, out2); + int out1 = inc1::apply(col1, col2, col3, out); + BOOST_CHECK_GT(0, out1); + int out0 = inc0::apply(col1, col2, col3, out); + BOOST_CHECK_GT(0, out0); + + P hard1(0, 0), hard2(1e20, 0), hard3(0, 1e20); + P inhard(0.5, 0.5); + int hardr = inc2::apply(hard1, hard2, hard3, inhard); + BOOST_CHECK_GT(hardr, 0); +} + + +int test_main(int, char* []) +{ + test_all >(); + return 0; +} diff --git a/extensions/test/triangulation/side_robust.cpp b/extensions/test/triangulation/side_robust.cpp new file mode 100644 index 000000000..dd940971a --- /dev/null +++ b/extensions/test/triangulation/side_robust.cpp @@ -0,0 +1,59 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2019 Tinko Bartels, Berlin, Germany. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include + +template +void test_all() +{ + typedef bg::strategy::side::side_robust side3; + typedef bg::strategy::side::side_robust side2; + typedef bg::strategy::side::side_robust side1; + typedef bg::strategy::side::side_robust side0; + + P col1(1.0, 1.0), col2(2.0, 2.0), col3(3.0, 3.0); + int col3r = side3::apply(col1, col2, col3); + BOOST_CHECK_EQUAL(0, col3r); + int col2r = side2::apply(col1, col2, col3); + BOOST_CHECK_EQUAL(0, col2r); + int col1r = side1::apply(col1, col2, col3); + BOOST_CHECK_EQUAL(0, col1r); + int col0r = side0::apply(col1, col2, col3); + BOOST_CHECK_EQUAL(0, col0r); + + P easy1(0.0, 0.0), easy2(1.0, 1.0), easy3(0.0, 1.0); + int easy3r = side3::apply(easy1, easy2, easy3); + BOOST_CHECK_GT(easy3r, 0); + int easy2r = side2::apply(easy1, easy2, easy3); + BOOST_CHECK_GT(easy2r, 0); + int easy1r = side1::apply(easy1, easy2, easy3); + BOOST_CHECK_GT(easy1r, 0); + int easy0r = side0::apply(easy1, easy2, easy3); + BOOST_CHECK_GT(easy0r, 0); + + P medium1(1.0, 1.0), medium2(1.0e20, 1.0e20), medium3(1.0, 2.0); + int medium3r = side3::apply(medium1, medium2, medium3); + BOOST_CHECK_GT(medium3r, 0); + int medium2r = side2::apply(medium1, medium2, medium3); + BOOST_CHECK_GT(medium2r, 0); + + P hard1(1.0e-20, 1.0e-20), hard2(1.0e20, 1.0e20), hard3(1.0, 2.0); + int hard3r = side3::apply(hard1, hard2, hard3); + BOOST_CHECK_GT(hard3r, 0); +} + + +int test_main(int, char* []) +{ + test_all >(); + return 0; +} diff --git a/include/boost/geometry/extensions/triangulation/strategies/cartesian/detail/precise_math.hpp b/include/boost/geometry/extensions/triangulation/strategies/cartesian/detail/precise_math.hpp new file mode 100644 index 000000000..b194019fd --- /dev/null +++ b/include/boost/geometry/extensions/triangulation/strategies/cartesian/detail/precise_math.hpp @@ -0,0 +1,588 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2019 Tinko Bartels, Berlin, Germany. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_DETAIL_PRECISE_MATH_HPP +#define BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_DETAIL_PRECISE_MATH_HPP + +#include +#include + +// The following code is based on "Adaptive Precision Floating-Point Arithmetic +// and Fast Robust Geometric Predicates" by Richard Shewchuk, +// J. Discrete Comput Geom (1997) 18: 305. https://doi.org/10.1007/PL00009321 + +namespace boost { namespace geometry +{ + +namespace detail { namespace precise_math +{ + +// See Theorem 6, page 6 +template +< + typename RealNumber +> +inline std::array fast_two_sum(RealNumber const a, + RealNumber const b) +{ + RealNumber x = a + b; + RealNumber b_virtual = x - a; + return {{x, b - b_virtual}}; +} + +// See Theorem 7, page 7 - 8 +template +< + typename RealNumber +> +inline std::array two_sum(RealNumber const a, + RealNumber const b) +{ + RealNumber x = a + b; + RealNumber b_virtual = x - a; + RealNumber a_virtual = x - b_virtual; + RealNumber b_roundoff = b - b_virtual; + RealNumber a_roundoff = a - a_virtual; + RealNumber y = a_roundoff + b_roundoff; + return {{ x, y }}; +} + +// See bottom of page 8 +template +< + typename RealNumber +> +inline RealNumber two_diff_tail(RealNumber const a, + RealNumber const b, + RealNumber const x) +{ + RealNumber b_virtual = a - x; + RealNumber a_virtual = x + b_virtual; + RealNumber b_roundoff = b_virtual - b; + RealNumber a_roundoff = a - a_virtual; + return a_roundoff + b_roundoff; +} + +// see bottom of page 8 +template +< + typename RealNumber +> +inline std::array two_diff(RealNumber const a, + RealNumber const b) +{ + RealNumber x = a - b; + RealNumber y = two_diff_tail(a, b, x); + return {{ x, y }}; +} + +// constexpr power-method, helper for splitter +template +< + typename RealNumber +> +constexpr RealNumber int_pow(RealNumber const base, + int exp, + RealNumber out = 1.0) +{ + return exp < 1 ? out : + int_pow(base*base, exp/2, (exp % 2) ? out*base : out); +} + +// consexpr method to compute 2^s + 1 as in Theorem 17, page 18 +template +< + typename RealNumber +> +constexpr RealNumber splitter() +{ + return int_pow(2.0, + (std::numeric_limits::digits + 1) / 2) + 1; +} + +// see theorem 17, page 18 +template +< + typename RealNumber +> +inline std::array split(RealNumber const a) { + RealNumber c = splitter() * a; + RealNumber a_big = c - a; + RealNumber a_hi = c - a_big; + return {{ a_hi, a - a_hi }}; +} + +// see theorem 18, page 19 +template +< + typename RealNumber +> +inline RealNumber two_product_tail(RealNumber const a, + RealNumber const b, + RealNumber const x) +{ + std::array a_expansion = split(a); + std::array b_expansion = split(b); + RealNumber err1 = x - (a_expansion[0] * b_expansion[0]); + RealNumber err2 = err1 - (a_expansion[1] * b_expansion[0]); + RealNumber err3 = err2 - (a_expansion[0] * b_expansion[1]); + return (a_expansion[1] * b_expansion[1]) - err3; +} + +// see theorem 18, page 19 +template +< + typename RealNumber +> +inline std::array two_product(RealNumber const a, + RealNumber const b) +{ + RealNumber x = a * b; + RealNumber y = two_product_tail(a, b, x); + return {{ x , y }}; +} + +// see theorem 12, figure 7, page 11 - 12, +// this is the 2 by 2 case for the corresponding diff-method +// note that this method takes input in descending order of magnitude and +// returns components in ascending order of magnitude +template +< + typename RealNumber +> +inline std::array two_two_expansion_diff( + std::array const a, + std::array const b) +{ + std::array h; + std::array Qh = two_diff(a[1], b[1]); + h[0] = Qh[1]; + Qh = two_sum( a[0], Qh[0] ); + RealNumber _j = Qh[0]; + Qh = two_diff(Qh[1], b[0]); + h[1] = Qh[1]; + Qh = two_sum( _j, Qh[0] ); + h[2] = Qh[1]; + h[3] = Qh[0]; + return h; +} + +// see theorem 13, figure 8. This implementation uses zero elimination as +// suggested on page 17, second to last paragraph. Returns the number of +// non-zero components in the result and writes the result to h. +// the merger into a single sequence g is done implicitly +template +< + typename RealNumber, + std::size_t InSize1, + std::size_t InSize2, + std::size_t OutSize +> +inline int fast_expansion_sum_zeroelim( + std::array const& e, + std::array const& f, + std::array & h, + int m = InSize1, + int n = InSize2) +{ + std::array Qh; + int i_e = 0, i_f = 0, i_h = 0; + if (std::abs(f[0]) > std::abs(e[0])) { + Qh[0] = e[i_e++]; + } else { + Qh[0] = f[i_f++]; + } + i_h = 0; + if ((i_e < m) && (i_f < n)) { + if (std::abs(f[i_f]) > std::abs(e[i_e])) { + Qh = fast_two_sum(e[i_e++], Qh[0]); + } else { + Qh = fast_two_sum(f[i_f++], Qh[0]); + } + if (Qh[1] != 0.0) { + h[i_h++] = Qh[1]; + } + while ((i_e < m) && (i_f < n)) { + if (std::abs(f[i_f]) > std::abs(e[i_e])) { + Qh = two_sum(Qh[0], e[i_e++]); + } else { + Qh = two_sum(Qh[0], f[i_f++]); + } + if (Qh[1] != 0.0) { + h[i_h++] = Qh[1]; + } + } + } + while (i_e < m) { + Qh = two_sum(Qh[0], e[i_e++]); + if (Qh[1] != 0.0) { + h[i_h++] = Qh[1]; + } + } + while (i_f < n) { + Qh = two_sum(Qh[0], f[i_f++]); + if (Qh[1] != 0.0) { + h[i_h++] = Qh[1]; + } + } + if ((Qh[0] != 0.0) || (i_h == 0)) { + h[i_h++] = Qh[0]; + } + return i_h; +} + +// see theorem 19, figure 13, page 20 - 21. This implementation uses zero +// elimination as suggested on page 17, second to last paragraph. Returns the +// number of non-zero components in the result and writes the result to h. +template +< + typename RealNumber, + std::size_t InSize +> +inline int scale_expansion_zeroelim( + std::array const& e, + RealNumber const b, + std::array & h, + int e_non_zeros = InSize) +{ + std::array Qh = two_product(e[0], b); + int i_h = 0; + if (Qh[1] != 0) { + h[i_h++] = Qh[1]; + } + for (int i_e = 1; i_e < e_non_zeros; i_e++) { + std::array Tt = two_product(e[i_e], b); + Qh = two_sum(Qh[0], Tt[1]); + if (Qh[1] != 0) { + h[i_h++] = Qh[1]; + } + Qh = fast_two_sum(Tt[0], Qh[0]); + if (Qh[1] != 0) { + h[i_h++] = Qh[1]; + } + } + if ((Qh[0] != 0.0) || (i_h == 0)) { + h[i_h++] = Qh[0]; + } + return i_h; +} + +// see page 38, Figure 21 for the calculations, notation follows the notation in the figure. +template +< + typename RealNumber, + int robustness = 3 +> +inline RealNumber orient2d(std::array const& p1, + std::array const& p2, + std::array const& p3) +{ + if(robustness == 0) { + return (p1[0]-p3[0])*(p2[1]-p3[1]) - (p1[1]-p3[1])*(p2[0] - p3[0]); + } + std::array t1, t2, t3, t4; + t1[0] = p1[0] - p3[0]; + t2[0] = p2[1] - p3[1]; + t3[0] = p1[1] - p3[1]; + t4[0] = p2[0] - p3[0]; + std::array t5_01, t6_01; + t5_01[0] = t1[0] * t2[0]; + t6_01[0] = t3[0] * t4[0]; + RealNumber det = t5_01[0] - t6_01[0]; + if ( (t5_01[0] > 0 && t6_01[0] <= 0) || (t5_01[0] < 0 && t6_01[0] >= 0) ) { + //if diagonal and antidiagonal have different sign, the sign of det is + //obvious + return det; + } + RealNumber const magnitude = std::abs(t5_01[0]) + std::abs(t6_01[0]); + + // see p.39, mind the different definition of epsilon for error bound + RealNumber const A_relative_bound = + (1.5 + 4 * std::numeric_limits::epsilon()) + * std::numeric_limits::epsilon(); + RealNumber absolute_bound = A_relative_bound * magnitude; + if ( std::abs(det) >= absolute_bound ) { + return det; //A estimate + } + + t5_01[1] = two_product_tail(t1[0], t2[0], t5_01[0]); + t6_01[1] = two_product_tail(t3[0], t4[0], t6_01[0]); + std::array tA_03 = two_two_expansion_diff(t5_01, t6_01); + det = std::accumulate(tA_03.begin(), tA_03.end(), static_cast(0)); + if(robustness == 1) return det; + // see p.39, mind the different definition of epsilon for error bound + RealNumber B_relative_bound = + (1 + 3 * std::numeric_limits::epsilon()) + * std::numeric_limits::epsilon(); + absolute_bound = B_relative_bound * magnitude; + if (std::abs(det) >= absolute_bound) { + return det; //B estimate + } + t1[1] = two_diff_tail(p1[0], p3[0], t1[0]); + t2[1] = two_diff_tail(p2[1], p3[1], t2[0]); + t3[1] = two_diff_tail(p1[1], p3[1], t3[0]); + t4[1] = two_diff_tail(p2[0], p3[0], t4[0]); + + if ((t1[1] == 0) && (t3[1] == 0) && (t2[1] == 0) && (t4[1] == 0)) { + return det; //If all tails are zero, there is noething else to compute + } + RealNumber sub_bound = + (1.5 + 2 * std::numeric_limits::epsilon()) + * std::numeric_limits::epsilon(); + // see p.39, mind the different definition of epsilon for error bound + RealNumber C_relative_bound = + (2.25 + 8 * std::numeric_limits::epsilon()) + * std::numeric_limits::epsilon() + * std::numeric_limits::epsilon(); + absolute_bound = C_relative_bound * magnitude + sub_bound * std::abs(det); + det += (t1[0] * t2[1] + t2[0] * t1[1]) - (t3[0] * t4[1] + t4[0] * t3[1]); + if (robustness == 2 || std::abs(det) >= absolute_bound) { + return det; //C estimate + } + std::array D_left; + int D_left_nz; + { + std::array t5_23 = two_product(t1[1], t2[0]); + std::array t6_23 = two_product(t3[1], t4[0]); + std::array tA_47 = two_two_expansion_diff(t5_23, t6_23); + D_left_nz = fast_expansion_sum_zeroelim(tA_03, tA_47, D_left); + } + std::array D_right; + int D_right_nz; + { + std::array t5_45 = two_product(t1[0], t2[1]); + std::array t6_45 = two_product(t3[0], t4[1]); + std::array tA_8_11 = two_two_expansion_diff(t5_45, t6_45); + std::array t5_67 = two_product(t1[1], t2[1]); + std::array t6_67 = two_product(t3[1], t4[1]); + std::array tA_12_15 = two_two_expansion_diff(t5_67, t6_67); + D_right_nz = fast_expansion_sum_zeroelim(tA_8_11, tA_12_15, D_right); + } + std::array D; + int D_nz = fast_expansion_sum_zeroelim(D_left, D_right, D, D_left_nz, D_right_nz); + // only return component of highest magnitude because we mostly care about the sign. + return(D[D_nz - 1]); +} + +// This method adaptively computes increasingly precise approximations of the following +// determinant using Laplace expansion along the last column. +// det A = +// | p1_x - p4_x p1_y - p4_y ( p1_x - p4_x ) ^ 2 + ( p1_y - p4_y ) ^ 2 | +// | p2_x - p4_x p2_y - p4_y ( p2_x - p4_x ) ^ 2 + ( p1_y - p4_y ) ^ 2 | +// | p3_x - p4_x p3_y - p4_y ( p3_x - p4_x ) ^ 2 + ( p3_y - p4_y ) ^ 2 | +// = a_13 * C_13 + a_23 * C_23 + a_33 * C_33 +// where a_ij is the i-j-entry and C_ij is the i_j Cofactor + +template +< + typename RealNumber, + int robustness = 2 +> +RealNumber incircle(std::array const& p1, + std::array const& p2, + std::array const& p3, + std::array const& p4) +{ + RealNumber A_11 = p1[0] - p4[0]; + RealNumber A_21 = p2[0] - p4[0]; + RealNumber A_31 = p3[0] - p4[0]; + RealNumber A_12 = p1[1] - p4[1]; + RealNumber A_22 = p2[1] - p4[1]; + RealNumber A_32 = p3[1] - p4[1]; + + std::array A_21_x_A_32, + A_31_x_A_22, + A_31_x_A_12, + A_11_x_A_32, + A_11_x_A_22, + A_21_x_A_12; + A_21_x_A_32[0] = A_21 * A_32; + A_31_x_A_22[0] = A_31 * A_22; + RealNumber A_13 = A_11 * A_11 + A_12 * A_12; + + A_31_x_A_12[0] = A_31 * A_12; + A_11_x_A_32[0] = A_11 * A_32; + RealNumber A_23 = A_21 * A_21 + A_22 * A_22; + + A_11_x_A_22[0] = A_11 * A_22; + A_21_x_A_12[0] = A_21 * A_12; + RealNumber A_33 = A_31 * A_31 + A_32 * A_32; + + RealNumber det = A_13 * (A_21_x_A_32[0] - A_31_x_A_22[0]) + + A_23 * (A_31_x_A_12[0] - A_11_x_A_32[0]) + + A_33 * (A_11_x_A_22[0] - A_21_x_A_12[0]); + if(robustness == 0) return det; + + RealNumber magnitude = + (std::abs(A_21_x_A_32[0]) + std::abs(A_31_x_A_22[0])) * A_13 + + (std::abs(A_31_x_A_12[0]) + std::abs(A_11_x_A_32[0])) * A_23 + + (std::abs(A_11_x_A_22[0]) + std::abs(A_21_x_A_12[0])) * A_33; + RealNumber A_relative_bound = + (5 + 24 * std::numeric_limits::epsilon()) + * std::numeric_limits::epsilon(); + RealNumber absolute_bound = A_relative_bound * magnitude; + if (std::abs(det) > absolute_bound) { + return det; + } + // (p2_x - p4_x) * (p3_y - p4_y) + A_21_x_A_32[1] = two_product_tail(A_21, A_32, A_21_x_A_32[0]); + // (p3_x - p4_x) * (p2_y - p4_y) + A_31_x_A_22[1] = two_product_tail(A_31, A_22, A_31_x_A_22[0]); + // (bx - dx) * (cy - dy) - (cx - dx) * (by - dy) + std::array C_13 = two_two_expansion_diff(A_21_x_A_32, A_31_x_A_22); + std::array C_13_x_A11; + // ( (bx - dx) * (cy - dy) - (cx - dx) * (by - dy) ) * ( ax - dx ) + int C_13_x_A11_nz = scale_expansion_zeroelim(C_13, A_11, C_13_x_A11); + std::array C_13_x_A11_sq; + // ( (bx - dx) * (cy - dy) - (cx - dx) * (by - dy) ) * ( ax - dx ) * (ax - dx) + int C_13_x_A11_sq_nz = scale_expansion_zeroelim(C_13_x_A11, + A_11, + C_13_x_A11_sq, + C_13_x_A11_nz); + + std::array C_13_x_A12; + // ( (bx - dx) * (cy - dy) - (cx - dx) * (by - dy) ) * ( ay - dy ) + int C_13_x_A12_nz = scale_expansion_zeroelim(C_13, A_12, C_13_x_A12); + + std::array C_13_x_A12_sq; + // ( (bx - dx) * (cy - dy) - (cx - dx) * (by - dy) ) * ( ay - dy ) * ( ay - dy ) + int C_13_x_A12_sq_nz = scale_expansion_zeroelim(C_13_x_A12, A_12, + C_13_x_A12_sq, + C_13_x_A12_nz); + + std::array A_13_x_C13; + // ( (bx - dx) * (cy - dy) - (cx - dx) * (by - dy) ) + // * ( ( ay - dy ) * ( ay - dy ) + ( ax - dx ) * (ax - dx) ) + int A_13_x_C13_nz = fast_expansion_sum_zeroelim(C_13_x_A11_sq, + C_13_x_A12_sq, + A_13_x_C13, + C_13_x_A11_sq_nz, + C_13_x_A12_sq_nz); + + // (cx - dx) * (ay - dy) + A_31_x_A_12[1] = two_product_tail(A_31, A_12, A_31_x_A_12[0]); + // (ax - dx) * (cy - dy) + A_11_x_A_32[1] = two_product_tail(A_11, A_32, A_11_x_A_32[0]); + // (cx - dx) * (ay - dy) - (ax - dx) * (cy - dy) + std::array C_23 = two_two_expansion_diff(A_31_x_A_12, + A_11_x_A_32); + std::array C_23_x_A_21; + // ( (cx - dx) * (ay - dy) - (ax - dx) * (cy - dy) ) * ( bx - dx ) + int C_23_x_A_21_nz = scale_expansion_zeroelim(C_23, A_21, C_23_x_A_21); + std::array C_23_x_A_21_sq; + // ( (cx - dx) * (ay - dy) - (ax - dx) * (cy - dy) ) * ( bx - dx ) * ( bx - dx ) + int C_23_x_A_21_sq_nz = scale_expansion_zeroelim(C_23_x_A_21, A_21, + C_23_x_A_21_sq, + C_23_x_A_21_nz); + std::array C_23_x_A_22; + // ( (cx - dx) * (ay - dy) - (ax - dx) * (cy - dy) ) * ( by - dy ) + int C_23_x_A_22_nz = scale_expansion_zeroelim(C_23, A_22, C_23_x_A_22); + std::array C_23_x_A_22_sq; + // ( (cx - dx) * (ay - dy) - (ax - dx) * (cy - dy) ) * ( by - dy ) * ( by - dy ) + int C_23_x_A_22_sq_nz = scale_expansion_zeroelim(C_23_x_A_22, A_22, + C_23_x_A_22_sq, + C_23_x_A_22_nz); + std::array A_23_x_C_23; + // ( (cx - dx) * (ay - dy) - (ax - dx) * (cy - dy) ) + // * ( ( bx - dx ) * ( bx - dx ) + ( by - dy ) * ( by - dy ) ) + int A_23_x_C_23_nz = fast_expansion_sum_zeroelim(C_23_x_A_21_sq, + C_23_x_A_22_sq, + A_23_x_C_23, + C_23_x_A_21_sq_nz, + C_23_x_A_22_sq_nz); + + // (ax - dx) * (by - dy) + A_11_x_A_22[1] = two_product_tail(A_11, A_22, A_11_x_A_22[0]); + // (bx - dx) * (ay - dy) + A_21_x_A_12[1] = two_product_tail(A_21, A_12, A_21_x_A_12[0]); + // (ax - dx) * (by - dy) - (bx - dx) * (ay - dy) + std::array C_33 = two_two_expansion_diff(A_11_x_A_22, + A_21_x_A_12); + std::array C_33_x_A31; + // ( (ax - dx) * (by - dy) - (bx - dx) * (ay - dy) ) * ( cx - dx ) + int C_33_x_A31_nz = scale_expansion_zeroelim(C_33, A_31, C_33_x_A31); + std::array C_33_x_A31_sq; + // ( (ax - dx) * (by - dy) - (bx - dx) * (ay - dy) ) * ( cx - dx ) * ( cx - dx ) + int C_33_x_A31_sq_nz = scale_expansion_zeroelim(C_33_x_A31, A_31, + C_33_x_A31_sq, + C_33_x_A31_nz); + std::array C_33_x_A_32; + // ( (ax - dx) * (by - dy) - (bx - dx) * (ay - dy) ) * ( cy - dy ) + int C_33_x_A_32_nz = scale_expansion_zeroelim(C_33, A_32, C_33_x_A_32); + std::array C_33_x_A_32_sq; + // ( (ax - dx) * (by - dy) - (bx - dx) * (ay - dy) ) * ( cy - dy ) * ( cy - dy ) + int C_33_x_A_32_sq_nz = scale_expansion_zeroelim(C_33_x_A_32, A_32, + C_33_x_A_32_sq, + C_33_x_A_32_nz); + + std::array A_33_x_C_33; + int A_33_x_C_33_nz = fast_expansion_sum_zeroelim(C_33_x_A31_sq, + C_33_x_A_32_sq, + A_33_x_C_33, + C_33_x_A31_sq_nz, + C_33_x_A_32_sq_nz); + std::array A_13_x_C13_p_A_13_x_C13; + int A_13_x_C13_p_A_13_x_C13_nz = fast_expansion_sum_zeroelim( + A_13_x_C13, A_23_x_C_23, + A_13_x_C13_p_A_13_x_C13, + A_13_x_C13_nz, + A_23_x_C_23_nz); + std::array det_expansion; + int det_expansion_nz = fast_expansion_sum_zeroelim( + A_13_x_C13_p_A_13_x_C13, + A_33_x_C_33, + det_expansion, + A_13_x_C13_p_A_13_x_C13_nz, + A_33_x_C_33_nz); + + det = std::accumulate(det_expansion.begin(), + det_expansion.begin() + det_expansion_nz, + static_cast(0)); + if(robustness == 1) return det; + RealNumber B_relative_bound = + (2 + 12 * std::numeric_limits::epsilon()) + * std::numeric_limits::epsilon(); + absolute_bound = B_relative_bound * magnitude; + if (std::abs(det) >= absolute_bound) { + return det; + } + RealNumber A_11tail = two_diff_tail(p1[0], p4[0], A_11); + RealNumber A_12tail = two_diff_tail(p1[1], p4[1], A_12); + RealNumber A_21tail = two_diff_tail(p2[0], p4[0], A_21); + RealNumber A_22tail = two_diff_tail(p2[1], p4[1], A_22); + RealNumber A_31tail = two_diff_tail(p3[0], p4[0], A_31); + RealNumber A_32tail = two_diff_tail(p3[1], p4[1], A_32); + if ((A_11tail == 0) && (A_21tail == 0) && (A_31tail == 0) + && (A_12tail == 0) && (A_22tail == 0) && (A_32tail == 0)) { + return det; + } + // RealNumber sub_bound = (1.5 + 2.0 * std::numeric_limits::epsilon()) + // * std::numeric_limits::epsilon(); + // RealNumber C_relative_bound = (11.0 + 72.0 * std::numeric_limits::epsilon()) + // * std::numeric_limits::epsilon() + // * std::numeric_limits::epsilon(); + //absolute_bound = C_relative_bound * magnitude + sub_bound * std::abs(det); + det += ((A_11 * A_11 + A_12 * A_12) * ((A_21 * A_32tail + A_32 * A_21tail) + - (A_22 * A_31tail + A_31 * A_22tail)) + + 2 * (A_11 * A_11tail + A_12 * A_12tail) * (A_21 * A_32 - A_22 * A_31)) + + ((A_21 * A_21 + A_22 * A_22) * ((A_31 * A_12tail + A_12 * A_31tail) + - (A_32 * A_11tail + A_11 * A_32tail)) + + 2 * (A_21 * A_21tail + A_22 * A_22tail) * (A_31 * A_12 - A_32 * A_11)) + + ((A_31 * A_31 + A_32 * A_32) * ((A_11 * A_22tail + A_22 * A_11tail) + - (A_12 * A_21tail + A_21 * A_12tail)) + + 2 * (A_31 * A_31tail + A_32 * A_32tail) * (A_11 * A_22 - A_12 * A_21)); + //if (std::abs(det) >= absolute_bound) { + return det; + //} +} + +}} // namespace detail::precise_math + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_DETAIL_PRECISE_MATH_HPP diff --git a/include/boost/geometry/extensions/triangulation/strategies/cartesian/in_circle_robust.hpp b/include/boost/geometry/extensions/triangulation/strategies/cartesian/in_circle_robust.hpp new file mode 100644 index 000000000..6578f1854 --- /dev/null +++ b/include/boost/geometry/extensions/triangulation/strategies/cartesian/in_circle_robust.hpp @@ -0,0 +1,53 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2019 Tinko Bartels, Berlin, Germany. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_IN_CIRCLE_ROBUST_HPP +#define BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_IN_CIRCLE_ROBUST_HPP + +#include + +namespace boost { namespace geometry +{ + +namespace strategy { namespace in_circle +{ + +template +class in_circle_robust +{ +public: + template + static inline int apply(P1 const& p1, P2 const& p2, P3 const& p3, P const& p) + { + std::array pa { + { boost::geometry::get<0>(p1), boost::geometry::get<1>(p1) }}; + std::array pb { + { boost::geometry::get<0>(p2), boost::geometry::get<1>(p2) }}; + std::array pc { + { boost::geometry::get<0>(p3), boost::geometry::get<1>(p3) }}; + std::array pd { + { boost::geometry::get<0>(p), boost::geometry::get<1>(p) }}; + CalculationType det = + boost::geometry::detail::precise_math::incircle + < + CalculationType, + robustness + >(pa, pb, pc, pd); + return det > 0 ? 1 + : det < 0 ? -1 : 0; + } + +}; + +} // namespace in_circle + +} // namespace strategy + +}} // namespace boost::geometry::strategy + +#endif // BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_IN_CIRCLE_ROBUST_HPP diff --git a/include/boost/geometry/extensions/triangulation/strategies/cartesian/side_robust.hpp b/include/boost/geometry/extensions/triangulation/strategies/cartesian/side_robust.hpp new file mode 100644 index 000000000..ada9cc916 --- /dev/null +++ b/include/boost/geometry/extensions/triangulation/strategies/cartesian/side_robust.hpp @@ -0,0 +1,92 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2019 Tinko Bartels, Berlin, Germany. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_SIDE_ROBUST_HPP +#define BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_SIDE_ROBUST_HPP + +#include +#include + +namespace boost { namespace geometry +{ + +namespace strategy { namespace side +{ + +template +< + typename CalculationType = void, + int robustness = 3 +> +struct side_robust +{ +public: + template + < + typename CoordinateType, + typename PromotedType, + typename P1, + typename P2, + typename P + > + static inline PromotedType side_value(P1 const& p1, P2 const& p2, + P const& p) + { + std::array pa {{ get<0>(p1), get<1>(p1) }}; + std::array pb {{ get<0>(p2), get<1>(p2) }}; + std::array pc {{ get<0>(p), get<1>(p) }}; + return ::boost::geometry::detail::precise_math::orient2d + (pa, pb, pc); + } + + template + < + typename P1, + typename P2, + typename P + > + static inline int apply(P1 const& p1, P2 const& p2, P const& p) + { + typedef typename coordinate_type::type coordinate_type1; + typedef typename coordinate_type::type coordinate_type2; + typedef typename coordinate_type

::type coordinate_type3; + + typedef typename boost::mpl::if_c + < + boost::is_void::type::value, + typename select_most_precise + < + typename select_most_precise + < + coordinate_type1, coordinate_type2 + >::type, + coordinate_type3 + >::type, + CalculationType + >::type coordinate_type; + typedef typename select_most_precise + < + coordinate_type, + double + >::type promoted_type; + + + promoted_type sv = + side_value(p1, p2, p); + return sv > 0 ? 1 + : sv < 0 ? -1 + : 0; + } + +}; + +}} // namespace strategy::side + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_SIDE_ROBUST_HPP From 0c0f365116da47d053b1d4cfd93648dc3b6fbc70 Mon Sep 17 00:00:00 2001 From: Tinko Bartels Date: Sun, 18 Aug 2019 14:27:16 +0200 Subject: [PATCH 40/49] Document robust predicates. --- .../strategies/cartesian/in_circle_robust.hpp | 8 ++++++++ .../strategies/cartesian/side_robust.hpp | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/boost/geometry/extensions/triangulation/strategies/cartesian/in_circle_robust.hpp b/include/boost/geometry/extensions/triangulation/strategies/cartesian/in_circle_robust.hpp index 6578f1854..e1d9eeda1 100644 --- a/include/boost/geometry/extensions/triangulation/strategies/cartesian/in_circle_robust.hpp +++ b/include/boost/geometry/extensions/triangulation/strategies/cartesian/in_circle_robust.hpp @@ -17,6 +17,14 @@ namespace boost { namespace geometry namespace strategy { namespace in_circle { +/*! +\brief Adaptive precision predicate to check whether a fourth point lies inside the circumcircle of the first three points: + inside (>0), outside (< 0), on the boundary (0). +\ingroup strategies +\tparam CalculationType \tparam_calculation (numeric_limits::epsilon() and numeric_limits::digits must be supported for calculation type ct) +\tparam robustness Number that determines maximum precision. Values from 0 to 2 may make the calculation terminate faster for inputs that may require higher precision to ensure correctness. Full robustness (3) is not yet implemented. +\details This predicate determines whether a fourth point lies inside the circumcircle of the first three points using an algorithm that is adapted from incircle as described in "Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates" by Jonathan Richard Shewchuk ( https://dl.acm.org/citation.cfm?doid=237218.237337 ). More information and copies of the paper can also be found at https://www.cs.cmu.edu/~quake/robust.html . It is designed to be adaptive in the sense that it should be fast for inputs that lead to correct results with plain float operations but robust for inputs that require higher precision arithmetics. + */ template class in_circle_robust { diff --git a/include/boost/geometry/extensions/triangulation/strategies/cartesian/side_robust.hpp b/include/boost/geometry/extensions/triangulation/strategies/cartesian/side_robust.hpp index ada9cc916..04b4c424d 100644 --- a/include/boost/geometry/extensions/triangulation/strategies/cartesian/side_robust.hpp +++ b/include/boost/geometry/extensions/triangulation/strategies/cartesian/side_robust.hpp @@ -18,6 +18,14 @@ namespace boost { namespace geometry namespace strategy { namespace side { +/*! +\brief Adaptive precision predicate to check at which side of a segment a point lies: + left of segment (>0), right of segment (< 0), on segment (0). +\ingroup strategies +\tparam CalculationType \tparam_calculation (numeric_limits::epsilon() and numeric_limits::digits must be supported for calculation type ct) +\tparam robustness Number that determines maximum precision. Values from 0 to 2 may make the calculation terminate faster for inputs that may require higher precision to ensure correctness. +\details This predicate determines at which side of a segment a point lies using an algorithm that is adapted from orient2d as described in "Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates" by Jonathan Richard Shewchuk ( https://dl.acm.org/citation.cfm?doid=237218.237337 ). More information and copies of the paper can also be found at https://www.cs.cmu.edu/~quake/robust.html . It is designed to be adaptive in the sense that it should be fast for inputs that lead to correct results with plain float operations but robust for inputs that require higher precision arithmetics. + */ template < typename CalculationType = void, @@ -26,6 +34,7 @@ template struct side_robust { public: + //! \brief Computes double the signed area of the CCW triangle p1, p2, p template < typename CoordinateType, @@ -44,6 +53,7 @@ public: (pa, pb, pc); } +#ifndef DOXYGEN_SHOULD_SKIP_THIS template < typename P1, @@ -82,6 +92,7 @@ public: : sv < 0 ? -1 : 0; } +#endif }; From e3fc7cae15b304ae0b87a792e9b078478e239518 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 1 Sep 2019 00:27:17 +0200 Subject: [PATCH 41/49] [strategies] Fix spherical point_in_point for points having different units. --- .../boost/geometry/strategies/spherical/point_in_point.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/strategies/spherical/point_in_point.hpp b/include/boost/geometry/strategies/spherical/point_in_point.hpp index b6e6973bd..4e155a888 100644 --- a/include/boost/geometry/strategies/spherical/point_in_point.hpp +++ b/include/boost/geometry/strategies/spherical/point_in_point.hpp @@ -96,8 +96,10 @@ private: Point1, calculation_type, radian >::type helper_point1, helper_point2; - Point1 point1_normalized = return_normalized(point1); - Point2 point2_normalized = return_normalized(point2); + Point1 point1_normalized; + strategy::normalize::spherical_point::apply(point1, point1_normalized); + Point2 point2_normalized; + strategy::normalize::spherical_point::apply(point2, point2_normalized); geometry::transform(point1_normalized, helper_point1); geometry::transform(point2_normalized, helper_point2); From 06a5435917532c0af53f85d25374b9c5cb26bcb5 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 20 Sep 2019 00:56:52 +0200 Subject: [PATCH 42/49] [formulas] Fix division by 0 in sjoberg intersection. --- .../formulas/sjoberg_intersection.hpp | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/formulas/sjoberg_intersection.hpp b/include/boost/geometry/formulas/sjoberg_intersection.hpp index 723c42f2c..49358fcc1 100644 --- a/include/boost/geometry/formulas/sjoberg_intersection.hpp +++ b/include/boost/geometry/formulas/sjoberg_intersection.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2016-2017 Oracle and/or its affiliates. +// Copyright (c) 2016-2019 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -819,8 +819,14 @@ private: // Newton-Raphson method for (int i = 0; i < max_iterations_02; ++i) { - CT const sin_beta = sin(beta); CT const cos_beta = cos(beta); + + if (math::equals(cos_beta, c0)) + { + return false; + } + + CT const sin_beta = sin(beta); CT const cos_beta_sqr = math::sqr(cos_beta); CT const G = c1 - e_sqr * cos_beta_sqr; @@ -834,6 +840,10 @@ private: if (is_beta_ok) { CT const H = cos_beta_sqr - geod1.Cj_sqr; + if (math::equals(H, c0)) + { + return false; + } f1 = geod1.Cj / cos_beta * math::sqrt(G / H); } else @@ -849,6 +859,15 @@ private: if (is_beta_ok) { CT const H = cos_beta_sqr - geod2.Cj_sqr; + if (math::equals(H, c0)) + { + // NOTE: This may happen for segment nearly + // at the equator. Detected for (radian): + // (-0.0872665 -0.0872665, -0.0872665 0.0872665) + // x + // (0 1.57e-07, -0.392699 1.57e-07) + return false; + } f2 = geod2.Cj / cos_beta * math::sqrt(G / H); } else From a03155b46dcb31fc5dcdf6b7c968afc736ba839b Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 20 Sep 2019 00:57:21 +0200 Subject: [PATCH 43/49] [test][formulas] Add test case (issue 612). --- test/formulas/intersection.cpp | 18 +++++++++- test/formulas/test_formula.hpp | 61 +++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/test/formulas/intersection.cpp b/test/formulas/intersection.cpp index 8bf0c2fca..4ab53aa24 100644 --- a/test/formulas/intersection.cpp +++ b/test/formulas/intersection.cpp @@ -1,7 +1,7 @@ // Boost.Geometry // Unit Test -// Copyright (c) 2016-2018 Oracle and/or its affiliates. +// Copyright (c) 2016-2019 Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -171,6 +171,20 @@ void test_all(expected_results const& results) #endif } +void test_bugs() +{ + // https://github.com/boostorg/geometry/issues/612 + { + double lon, lat; + bg::formula::sjoberg_intersection + ::apply(-0.0872665, -0.0872665, -0.0872665, 0.0872665, + 0.0, 1.57e-07, -0.392699, 1.57e-07, + lon, lat, bg::srs::spheroid()); + check_one("issue 612", lon, -0.087266500535674751); + check_one("issue 612", lat, 1.5892499139622920e-07); + } +} + int test_main(int, char*[]) { for (size_t i = 0; i < expected_size; ++i) @@ -178,5 +192,7 @@ int test_main(int, char*[]) test_all(expected[i]); } + test_bugs(); + return 0; } diff --git a/test/formulas/test_formula.hpp b/test/formulas/test_formula.hpp index 8efb428cc..9e41bfcae 100644 --- a/test/formulas/test_formula.hpp +++ b/test/formulas/test_formula.hpp @@ -1,7 +1,7 @@ // Boost.Geometry // Unit Test -// Copyright (c) 2016-2017 Oracle and/or its affiliates. +// Copyright (c) 2016-2019 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -29,6 +29,38 @@ void normalize_deg(double & deg) ::boost::test_tools::check_is_close_t(), M, CHECK, CHECK_MSG, (L)(R)(::boost::math::fpc::percent_tolerance(T)) ) +void check_one(std::string const& name, double result, double expected) +{ + std::string id = name.empty() ? "" : (name + " : "); + + double eps = std::numeric_limits::epsilon(); + double abs_result = bg::math::abs(result); + double abs_expected = bg::math::abs(expected); + double res_max = (std::max)(abs_result, abs_expected); + double res_min = (std::min)(abs_result, abs_expected); + if (res_min <= eps) // including 0 + { + bool is_close = abs_result <= 30 * eps && abs_expected <= 30 * eps; + BOOST_CHECK_MESSAGE((is_close), + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); + } + else if (res_max > 100 * eps) + { + BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 0.1, + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); + } + else if (res_max > 10 * eps) + { + BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 10, + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); + } + else if (res_max > eps) + { + BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 1000, + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); + } +} + void check_one(std::string const& name, double result, double expected, double reference, double reference_error, bool normalize = false, bool check_reference_only = false) @@ -44,32 +76,7 @@ void check_one(std::string const& name, if (! check_reference_only) { - double eps = std::numeric_limits::epsilon(); - double abs_result = bg::math::abs(result); - double abs_expected = bg::math::abs(expected); - double res_max = (std::max)(abs_result, abs_expected); - double res_min = (std::min)(abs_result, abs_expected); - if (res_min <= eps) // including 0 - { - bool is_close = abs_result <= 30 * eps && abs_expected <= 30 * eps; - BOOST_CHECK_MESSAGE((is_close), - id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); - } - else if (res_max > 100 * eps) - { - BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 0.1, - id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); - } - else if (res_max > 10 * eps) - { - BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 10, - id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); - } - else if (res_max > eps) - { - BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 1000, - id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); - } + check_one(name, result, expected); } // NOTE: in some cases it probably will be necessary to normalize From 9fbd9d9d422049b1ebc8d023107273f33396d7c9 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 8 Oct 2019 02:47:20 +0200 Subject: [PATCH 44/49] [partition] Fix integral overflow while dividing bounding box in half. --- .../geometry/algorithms/detail/partition.hpp | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/partition.hpp b/include/boost/geometry/algorithms/detail/partition.hpp index 8eb0c7b1f..b8a50007b 100644 --- a/include/boost/geometry/algorithms/detail/partition.hpp +++ b/include/boost/geometry/algorithms/detail/partition.hpp @@ -3,8 +3,8 @@ // Copyright (c) 2011-2015 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2015, 2017. -// Modifications copyright (c) 2015-2017 Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017, 2018, 2019. +// Modifications copyright (c) 2015-2019 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -19,6 +19,8 @@ #include #include #include +#include + #include #include #include @@ -30,15 +32,35 @@ namespace boost { namespace geometry namespace detail { namespace partition { +template ::value> +struct divide_interval +{ + static inline T apply(T const& mi, T const& ma) + { + static T const two = 2; + return (mi + ma) / two; + } +}; + +template +struct divide_interval +{ + static inline T apply(T const& mi, T const& ma) + { + // avoid overflow + return mi / 2 + ma / 2 + (mi % 2 + ma % 2) / 2; + } +}; + template inline void divide_box(Box const& box, Box& lower_box, Box& upper_box) { typedef typename coordinate_type::type ctype; // Divide input box into two parts, e.g. left/right - ctype two = 2; - ctype mid = (geometry::get(box) - + geometry::get(box)) / two; + ctype mid = divide_interval::apply( + geometry::get(box), + geometry::get(box)); lower_box = box; upper_box = box; From 73dc6dbed54cf9284da047a1407a55029d4c3c4e Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 10 Oct 2019 18:27:23 +0200 Subject: [PATCH 45/49] [algorithms] Implement relops for arguments always resulting in false. --- .../detail/equals/implementation.hpp | 88 +++++++++++++++---- .../algorithms/detail/equals/interface.hpp | 12 ++- .../algorithms/detail/relate/de9im.hpp | 29 +++--- .../algorithms/detail/relate/relate_impl.hpp | 70 ++++++++++----- .../algorithms/detail/relate/result.hpp | 8 +- .../detail/touches/implementation.hpp | 26 ++++-- 6 files changed, 167 insertions(+), 66 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/equals/implementation.hpp b/include/boost/geometry/algorithms/detail/equals/implementation.hpp index f39ae0b8b..4088478d5 100644 --- a/include/boost/geometry/algorithms/detail/equals/implementation.hpp +++ b/include/boost/geometry/algorithms/detail/equals/implementation.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2014, 2015, 2016, 2017, 2018. -// Modifications copyright (c) 2014-2018 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2015, 2016, 2017, 2018, 2019. +// Modifications copyright (c) 2014-2019 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -275,6 +275,15 @@ private: } }; +struct equals_always_false +{ + template + static inline bool apply(Geometry1 const& , Geometry2 const& , Strategy const& ) + { + return false; + } +}; + }} // namespace detail::equals #endif // DOXYGEN_NO_DETAIL @@ -285,81 +294,92 @@ namespace dispatch { template -struct equals +struct equals : detail::equals::point_point<0, DimensionCount> {}; template -struct equals +struct equals : detail::equals::equals_by_relate {}; template -struct equals +struct equals : detail::equals::equals_by_relate {}; template -struct equals +struct equals : detail::equals::box_box<0, DimensionCount> {}; template -struct equals +struct equals : detail::equals::equals_by_collection_or_relate {}; template -struct equals +struct equals : detail::equals::equals_by_collection_or_relate {}; template -struct equals +struct equals : detail::equals::equals_by_collection_or_relate {}; template -struct equals +struct equals : detail::equals::equals_by_collection {}; template -struct equals +struct equals : detail::equals::equals_by_collection {}; template -struct equals +struct equals : detail::equals::segment_segment {}; template -struct equals +struct equals : detail::equals::equals_by_relate {}; template -struct equals +struct equals : detail::equals::equals_by_relate {}; template -struct equals +struct equals : detail::equals::equals_by_relate {}; +template +struct equals + : detail::equals::equals_by_relate +{}; + +template +struct equals + : detail::equals::equals_by_relate +{}; + template struct equals < MultiPolygon1, MultiPolygon2, multi_polygon_tag, multi_polygon_tag, + areal_tag, areal_tag, 2, Reverse > @@ -372,6 +392,7 @@ struct equals < Polygon, MultiPolygon, polygon_tag, multi_polygon_tag, + areal_tag, areal_tag, 2, Reverse > @@ -383,6 +404,7 @@ struct equals < MultiPolygon, Ring, multi_polygon_tag, ring_tag, + areal_tag, areal_tag, 2, Reverse > @@ -390,6 +412,42 @@ struct equals {}; +// NOTE: degenerated linear geometries, e.g. segment or linestring containing +// 2 equal points, are considered to be invalid. Though theoretically +// degenerated segments and linestrings could be treated as points and +// multi-linestrings as multi-points. +// This reasoning could also be applied to boxes. + +template +struct equals + : detail::equals::equals_always_false +{}; + +template +struct equals + : detail::equals::equals_always_false +{}; + +template +struct equals + : detail::equals::equals_always_false +{}; + +template +struct equals + : detail::equals::equals_always_false +{}; + +template +struct equals + : detail::equals::equals_always_false +{}; + +template +struct equals + : detail::equals::equals_always_false +{}; + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH diff --git a/include/boost/geometry/algorithms/detail/equals/interface.hpp b/include/boost/geometry/algorithms/detail/equals/interface.hpp index eacf95e9f..1e4918836 100644 --- a/include/boost/geometry/algorithms/detail/equals/interface.hpp +++ b/include/boost/geometry/algorithms/detail/equals/interface.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2014, 2015, 2016, 2017. -// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2015, 2016, 2017, 2019. +// Modifications copyright (c) 2014-2019 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -52,6 +52,8 @@ template typename Geometry2, typename Tag1 = typename tag::type, typename Tag2 = typename tag::type, + typename CastedTag1 = typename tag_cast::type, + typename CastedTag2 = typename tag_cast::type, std::size_t DimensionCount = dimension::type::value, bool Reverse = reverse_dispatch::type::value > @@ -64,10 +66,11 @@ template < typename Geometry1, typename Geometry2, typename Tag1, typename Tag2, + typename CastedTag1, typename CastedTag2, std::size_t DimensionCount > -struct equals - : equals +struct equals + : equals { template static inline bool apply(Geometry1 const& g1, Geometry2 const& g2, Strategy const& strategy) @@ -76,6 +79,7 @@ struct equals < Geometry2, Geometry1, Tag2, Tag1, + CastedTag2, CastedTag1, DimensionCount, false >::apply(g2, g1, strategy); diff --git a/include/boost/geometry/algorithms/detail/relate/de9im.hpp b/include/boost/geometry/algorithms/detail/relate/de9im.hpp index 713a3fc8f..be745bfda 100644 --- a/include/boost/geometry/algorithms/detail/relate/de9im.hpp +++ b/include/boost/geometry/algorithms/detail/relate/de9im.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014, 2015. -// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015, 2019. +// Modifications copyright (c) 2013-2019 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -318,9 +317,9 @@ struct static_mask_touches_impl // Using the above mask the result would be always false template struct static_mask_touches_impl - : not_implemented::type, - typename geometry::tag::type> -{}; +{ + typedef geometry::detail::relate::false_mask type; +}; template struct static_mask_touches_type @@ -377,12 +376,9 @@ template typename Geometry1, typename Geometry2, std::size_t Dim > struct static_mask_crosses_impl - : not_implemented - < - typename geometry::tag::type, - typename geometry::tag::type - > -{}; +{ + typedef geometry::detail::relate::false_mask type; +}; // dim(G1) == 1 && dim(G2) == 1 - L/L template struct static_mask_crosses_impl @@ -406,12 +402,9 @@ template std::size_t Dim2 = geometry::topological_dimension::value > struct static_mask_overlaps_impl - : not_implemented - < - typename geometry::tag::type, - typename geometry::tag::type - > -{}; +{ + typedef geometry::detail::relate::false_mask type; +}; // dim(G1) == D && dim(G2) == D - P/P A/A template struct static_mask_overlaps_impl diff --git a/include/boost/geometry/algorithms/detail/relate/relate_impl.hpp b/include/boost/geometry/algorithms/detail/relate/relate_impl.hpp index 2ec2361c0..ae963c4b0 100644 --- a/include/boost/geometry/algorithms/detail/relate/relate_impl.hpp +++ b/include/boost/geometry/algorithms/detail/relate/relate_impl.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014, 2015. -// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015, 2019. +// Modifications copyright (c) 2013-2019 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -30,25 +30,19 @@ namespace detail { namespace relate { struct implemented_tag {}; -template