diff --git a/doc/acknowledgments.qbk b/doc/acknowledgments.qbk index cd0c212c2..929fa8d2a 100644 --- a/doc/acknowledgments.qbk +++ b/doc/acknowledgments.qbk @@ -28,6 +28,9 @@ We also thank all people who discussed on the mailing lists (either at boost, or at osgeo) about __boost_geometry__, in preview stage, or in review stage, or after that. +Furthermore we are thankful to people supplying patches: Arnold Metselaar, +Aleksey Tulinov, Christophe Henry + Finally I (Barend) would like to thank my former employer, Geodan. They allowed me to start a geographic library in 1995, which after a number of incarnations, redesigns, refactorings, previews, a review and even more diff --git a/doc/reference/algorithms/touches.qbk b/doc/reference/algorithms/touches.qbk new file mode 100644 index 000000000..56e502c93 --- /dev/null +++ b/doc/reference/algorithms/touches.qbk @@ -0,0 +1,19 @@ +[/============================================================================ + Boost.Geometry (aka GGL, Generic Geometry Library) + + Copyright (c) 2009-2012 Barend Gehrels, Amsterdam, the Netherlands. + Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + Copyright (c) 2009-2012 Bruno Lalande, Paris, France. + + 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) +=============================================================================/] + +[def __this_function__ touches] + +[heading_conformance_ogc __this_function__..Touches] +[? __one_parameter__ The version with one parameter is additional and not described in the OGC standard] + +[note only implemented for polygon/multi_polygon] + diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 752616e69..fb8226c0c 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -12,6 +12,34 @@ [section:release_notes Release Notes] +[/=================] +[heading Boost 1.50] +[/=================] + +[*Breaking changes] + +[*Bugfixes] +* the return type of comparable projected point strategy for integer points was wrong (integer), fixed +* several robustness issues in intersection of segments and polygons, fixed +* invalid intersection output is filtered out +* disjoint for multi_polygon's might incorrectly return true, fixed + +[*Solved tickets] + +* [@https://svn.boost.org/trac/boost/ticket/6585 6585] patch for alternative syntax multipoint, applied +* [@https://svn.boost.org/trac/boost/ticket/6584 6584] patch for bug in distance, applied +* [@https://svn.boost.org/trac/boost/ticket/5730 5730] same issue as 6584, fixed +* [@https://svn.boost.org/trac/boost/ticket/6166 6166] patch for missing transformation, applied +* [@https://svn.boost.org/trac/boost/ticket/6696 6696] invalid intersection output, was (by chance) already fixed in Trunk before reported + +[*Additional functionality] +* added algorithm "touches" (OGC SF compliant) for *polygon/*polygon + +[*Documentation] + +[*Internal changes] + + [/=================] [heading Boost 1.49] [/=================] diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index 7a17e8425..17fae5000 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -29,6 +29,7 @@ exe c02_custom_box_example : c02_custom_box_example.cpp ; exe c03_custom_linestring_example : c03_custom_linestring_example.cpp ; exe c04_a_custom_triangle_example : c04_a_custom_triangle_example.cpp ; exe c04_b_custom_triangle_example : c04_b_custom_triangle_example.cpp ; +exe c05_custom_point_pointer_example : c05_custom_point_pointer_example.cpp ; exe c06_custom_polygon_example : c06_custom_polygon_example.cpp ; exe c07_custom_ring_pointer_example : c07_custom_ring_pointer_example.cpp ; exe c08_custom_non_std_example : c08_custom_non_std_example.cpp ; diff --git a/include/boost/geometry/algorithms/area.hpp b/include/boost/geometry/algorithms/area.hpp index 919ab66fe..8193200ab 100644 --- a/include/boost/geometry/algorithms/area.hpp +++ b/include/boost/geometry/algorithms/area.hpp @@ -92,7 +92,7 @@ struct ring_area // An open ring has at least three points, // A closed ring has at least four points, // if not, there is no (zero) area - if (boost::size(ring) + if (int(boost::size(ring)) < core_detail::closure::minimum_ring_size::value) { return type(); diff --git a/include/boost/geometry/algorithms/detail/for_each_range.hpp b/include/boost/geometry/algorithms/detail/for_each_range.hpp index b10017c20..7cb01fa9b 100644 --- a/include/boost/geometry/algorithms/detail/for_each_range.hpp +++ b/include/boost/geometry/algorithms/detail/for_each_range.hpp @@ -22,6 +22,7 @@ #include #include +#include namespace boost { namespace geometry diff --git a/include/boost/geometry/algorithms/detail/get_left_turns.hpp b/include/boost/geometry/algorithms/detail/get_left_turns.hpp new file mode 100644 index 000000000..62f0f7f0f --- /dev/null +++ b/include/boost/geometry/algorithms/detail/get_left_turns.hpp @@ -0,0 +1,367 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012 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_GET_LEFT_TURNS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP + +#include + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +// TODO: move this to /util/ +template +static inline std::pair ordered_pair(T const& first, T const& second) +{ + return first < second ? std::make_pair(first, second) : std::make_pair(second, first); +} + +template +inline void debug_left_turn(AngleInfo const& ai, AngleInfo const& previous) +{ +#ifdef BOOST_GEOMETRY_DEBUG_BUFFER_OCCUPATION + std::cout << "Angle: " << (ai.incoming ? "i" : "o") + << " " << si(ai.seg_id) + << " " << (math::r2d * (ai.angle) ) + << " turn: " << ai.turn_index << "[" << ai.operation_index << "]" + ; + + if (ai.turn_index != previous.turn_index + || ai.operation_index != previous.operation_index) + { + std::cout << " diff: " << math::r2d * math::abs(previous.angle - ai.angle); + } + std::cout << std::endl; +#endif +} + +template +inline void debug_left_turn(std::string const& caption, AngleInfo const& ai, AngleInfo const& previous, + int code = -99, int code2 = -99, int code3 = -99, int code4 = -99) +{ +#ifdef BOOST_GEOMETRY_DEBUG_BUFFER_OCCUPATION + std::cout << " " << caption + << " turn: " << ai.turn_index << "[" << ai.operation_index << "]" + << " " << si(ai.seg_id) + << " " << (ai.incoming ? "i" : "o") + << " " << (math::r2d * (ai.angle) ) + << " turn: " << previous.turn_index << "[" << previous.operation_index << "]" + << " " << si(previous.seg_id) + << " " << (previous.incoming ? "i" : "o") + << " " << (math::r2d * (previous.angle) ) + ; + + if (code != -99) + { + std::cout << " code: " << code << " , " << code2 << " , " << code3 << " , " << code4; + } + std::cout << std::endl; +#endif +} + + +template +inline bool include_operation(Operation const& op, + segment_identifier const& outgoing_seg_id, + segment_identifier const& incoming_seg_id) +{ + return op.seg_id == outgoing_seg_id + && op.other_id == incoming_seg_id + && (op.operation == detail::overlay::operation_union + ||op.operation == detail::overlay::operation_continue) + ; +} + +template +inline bool process_include(segment_identifier const& outgoing_seg_id, segment_identifier const& incoming_seg_id, + int turn_index, Turn& turn, + std::set& keep_indices, int priority) +{ + bool result = false; + for (int i = 0; i < 2; i++) + { + if (include_operation(turn.operations[i], outgoing_seg_id, incoming_seg_id)) + { + turn.operations[i].include_in_occupation_map = true; + if (priority > turn.priority) + { + turn.priority = priority; + } + keep_indices.insert(turn_index); + result = true; + } + } + return result; +} + +template +inline bool include_left_turn_of_all( + AngleInfo const& outgoing, AngleInfo const& incoming, + Turns& turns, TurnSegmentIndices const& turn_segment_indices, + std::set& keep_indices, int priority) +{ + segment_identifier const& outgoing_seg_id = turns[outgoing.turn_index].operations[outgoing.operation_index].seg_id; + segment_identifier const& incoming_seg_id = turns[incoming.turn_index].operations[incoming.operation_index].seg_id; + + if (outgoing.turn_index == incoming.turn_index) + { + return process_include(outgoing_seg_id, incoming_seg_id, outgoing.turn_index, turns[outgoing.turn_index], keep_indices, priority); + } + + bool result = false; + std::pair pair = ordered_pair(outgoing_seg_id, incoming_seg_id); + auto it = turn_segment_indices.find(pair); + if (it != turn_segment_indices.end()) + { + for (auto sit = it->second.begin(); sit != it->second.end(); ++sit) + { + if (process_include(outgoing_seg_id, incoming_seg_id, *sit, turns[*sit], keep_indices, priority)) + { + result = true; + } + } + } + return result; +} + +template +inline bool corresponds(Turn const& turn, segment_identifier const& seg_id) +{ + return turn.operations[Index].operation == detail::overlay::operation_union + && turn.operations[Index].seg_id == seg_id; +} + + +template +inline bool prefer_by_other(Turns const& turns, + TurnSegmentIndices const& turn_segment_indices, + std::set& indices) +{ + std::map map; + for (auto sit = indices.begin(); sit != indices.end(); ++sit) + { + map[turns[*sit].operations[0].seg_id]++; + map[turns[*sit].operations[1].seg_id]++; + } + + std::set segment_occuring_once; + for (auto mit = map.begin(); mit != map.end(); ++mit) + { + if (mit->second == 1) + { + segment_occuring_once.insert(mit->first); + } +#ifdef BOOST_GEOMETRY_DEBUG_BUFFER_PREFER + std::cout << si(mit->first) << " " << mit->second << std::endl; +#endif + } + + if (segment_occuring_once.size() == 2) + { + // Try to find within all turns a turn with these two segments + + std::set::const_iterator soo_it = segment_occuring_once.begin(); + segment_identifier front = *soo_it; + soo_it++; + segment_identifier back = *soo_it; + + std::pair pair = ordered_pair(front, back); + auto it = turn_segment_indices.find(pair); + if (it != turn_segment_indices.end()) + { + // debug_turns_by_indices("Found", it->second); + // Check which is the union/continue + segment_identifier good; + for (auto sit = it->second.begin(); sit != it->second.end(); ++sit) + { + if (turns[*sit].operations[0].operation == detail::overlay::operation_union) + { + good = turns[*sit].operations[0].seg_id; + } + else if (turns[*sit].operations[1].operation == detail::overlay::operation_union) + { + good = turns[*sit].operations[1].seg_id; + } + } + +#ifdef BOOST_GEOMETRY_DEBUG_BUFFER_PREFER + std::cout << "Good: " << si(good) << std::endl; +#endif + + // Find in indexes-to-keep this segment with the union. Discard the other one + std::set ok_indices; + for (auto sit = indices.begin(); sit != indices.end(); ++sit) + { + if (corresponds<0>(turns[*sit], good) || corresponds<1>(turns[*sit], good)) + { + ok_indices.insert(*sit); + } + } + if (ok_indices.size() > 0 && ok_indices.size() < indices.size()) + { + indices = ok_indices; + std::cout << "^"; + return true; + } + } + } + return false; +} + +template +inline void prefer_by_priority(Turns const& turns, std::set& indices) +{ + // Find max prio + int min_prio = 1024, max_prio = 0; + for (auto sit = indices.begin(); sit != indices.end(); ++sit) + { + if (turns[*sit].priority > max_prio) + { + max_prio = turns[*sit].priority; + } + if (turns[*sit].priority < min_prio) + { + min_prio = turns[*sit].priority; + } + } + + if (min_prio == max_prio) + { + return; + } + + // Only keep indices with high prio + std::set ok_indices; + for (auto sit = indices.begin(); sit != indices.end(); ++sit) + { + if (turns[*sit].priority >= max_prio) + { + ok_indices.insert(*sit); + } + } + if (ok_indices.size() > 0 && ok_indices.size() < indices.size()) + { + indices = ok_indices; + std::cout << "%"; + } +} + +template +inline void calculate_left_turns(Angles const& angles, + Turns& turns, TurnSegmentIndices const& turn_segment_indices, + std::set& keep_indices) +{ + bool debug_indicate_size = false; + + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + std::size_t i = 0; + std::size_t n = boost::size(angles); + + typedef geometry::ever_circling_range_iterator circling_iterator; + circling_iterator cit(angles); + + debug_left_turn(*cit, *cit); + for(circling_iterator prev = cit++; i < n; prev = cit++, i++) + { + debug_left_turn(*cit, *prev); + + bool const include = ! geometry::math::equals(prev->angle, cit->angle) + && ! prev->incoming + && cit->incoming; + + if (include) + { + // Go back to possibly include more same left outgoing angles: + // Because we check on side too we can take a large "epsilon" + circling_iterator back = prev - 1; + + typename AngleInfo::angle_type eps = 0.00001; + int b = 1; + for(std::size_t d = 0; + math::abs(prev->angle - back->angle) < eps + && ! back->incoming + && d < n; + d++) + { + --back; + ++b; + } + + // Same but forward to possibly include more incoming angles + int f = 1; + circling_iterator forward = cit + 1; + for(std::size_t d = 0; + math::abs(cit->angle - forward->angle) < eps + && forward->incoming + && d < n; + d++) + { + ++forward; + ++f; + } + +#ifdef BOOST_GEOMETRY_DEBUG_BUFFER_OCCUPATION + std::cout << "HANDLE " << b << "/" << f << " ANGLES" << std::endl; +#endif + for(circling_iterator bit = prev; bit != back; --bit) + { + int code = side_strategy::apply(bit->direction_point, prev->intersection_point, prev->direction_point); + int code2 = side_strategy::apply(prev->direction_point, bit->intersection_point, bit->direction_point); + for(circling_iterator fit = cit; fit != forward; ++fit) + { + int code3 = side_strategy::apply(fit->direction_point, cit->intersection_point, cit->direction_point); + int code4 = side_strategy::apply(cit->direction_point, fit->intersection_point, fit->direction_point); + + int priority = 2; + if (code == -1 && code2 == 1) + { + // This segment is lying right of the other one. + // Cannot ignore it (because of robustness, see a.o. #rt_p21 from unit test). + // But if we find more we can prefer later by priority + // (a.o. necessary for #rt_o2 from unit test) + priority = 1; + } + + bool included = include_left_turn_of_all(*bit, *fit, turns, turn_segment_indices, keep_indices, priority); + debug_left_turn(included ? "KEEP" : "SKIP", *fit, *bit, code, code2, code3, code4); + } + } + } + } + + if (debug_indicate_size) + { + std::cout << " size=" << keep_indices.size(); + } + + if (keep_indices.size() >= 2) + { + prefer_by_other(turns, turn_segment_indices, keep_indices); + } + if (keep_indices.size() >= 2) + { + prefer_by_priority(turns, keep_indices); + } +} + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP diff --git a/include/boost/geometry/algorithms/detail/occupation_info.hpp b/include/boost/geometry/algorithms/detail/occupation_info.hpp new file mode 100644 index 000000000..e147ba12d --- /dev/null +++ b/include/boost/geometry/algorithms/detail/occupation_info.hpp @@ -0,0 +1,329 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012 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_OCCUPATION_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OCCUPATION_INFO_HPP + +#if ! defined(NDEBUG) + #define BOOST_GEOMETRY_DEBUG_BUFFER_OCCUPATION +#endif + +#include +#include + +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +class relaxed_less +{ + typedef typename geometry::coordinate_type

::type coordinate_type; + + coordinate_type epsilon; + +public : + + inline relaxed_less() + { + // TODO: adapt for ttmath, and maybe build the map in another way + // (e.g. exact constellations of segment-id's), maybe adaptive. + epsilon = std::numeric_limits::epsilon() * 100.0; + } + + inline bool operator()(P const& a, P const& b) const + { + coordinate_type const dx = math::abs(geometry::get<0>(a) - geometry::get<0>(b)); + coordinate_type const dy = math::abs(geometry::get<1>(a) - geometry::get<1>(b)); + + + if (dx < epsilon && dy < epsilon) + { + return false; + } + if (dx < epsilon) + { + return geometry::get<1>(a) < geometry::get<1>(b); + } + + return geometry::get<0>(a) < geometry::get<0>(b); + } + + inline bool equals(P const& a, P const& b) const + { + typedef typename geometry::coordinate_type

::type coordinate_type; + + coordinate_type const dx = math::abs(geometry::get<0>(a) - geometry::get<0>(b)); + coordinate_type const dy = math::abs(geometry::get<1>(a) - geometry::get<1>(b)); + + return dx < epsilon && dy < epsilon; + }; +}; + + +template +inline T calculate_angle(P1 const& from_point, P2 const& to_point) +{ + typedef P1 vector_type; + vector_type v = from_point; + geometry::subtract_point(v, to_point); + return atan2(geometry::get<1>(v), geometry::get<0>(v)); +} + +template +inline Iterator advance_circular(Iterator it, Vector const& vector, segment_identifier& seg_id, bool forward = true) +{ + int const increment = forward ? 1 : -1; + if (it == boost::begin(vector) && increment < 0) + { + it = boost::end(vector); + seg_id.segment_index = boost::size(vector); + } + it += increment; + seg_id.segment_index += increment; + if (it == boost::end(vector)) + { + seg_id.segment_index = 0; + it = boost::begin(vector); + } + return it; +} + +template +struct angle_info +{ + typedef T angle_type; + typedef Point point_type; + + segment_identifier seg_id; + int turn_index; + int operation_index; + Point intersection_point; + Point direction_point; + T angle; + bool incoming; +}; + +template +class occupation_info +{ + typedef std::vector collection_type; + + struct angle_sort + { + inline bool operator()(AngleInfo const& left, AngleInfo const& right) const + { + // In this case we can compare even double using equals + // return geometry::math::equals(left.angle, right.angle) + return left.angle == right.angle + ? int(left.incoming) < int(right.incoming) + : left.angle < right.angle + ; + } + }; + +public : + collection_type angles; +private : + bool m_occupied; + bool m_calculated; + + inline bool is_occupied() + { + if (boost::size(angles) <= 1) + { + return false; + } + + std::sort(angles.begin(), angles.end(), angle_sort()); + + typedef geometry::closing_iterator closing_iterator; + closing_iterator vit(angles); + closing_iterator end(angles, true); + + closing_iterator prev = vit++; + for( ; vit != end; prev = vit++) + { + if (! geometry::math::equals(prev->angle, vit->angle) + && ! prev->incoming + && vit->incoming) + { + return false; + } + } + return true; + } + +public : + inline occupation_info() + : m_occupied(false) + , m_calculated(false) + {} + + template + inline void add(PointC const& map_point, Point1 const& direction_point, Point2 const& intersection_point, + int turn_index, int operation_index, + segment_identifier const& seg_id, bool incoming) + { + //std::cout << "-> adding angle " << geometry::wkt(direction_point) << " .. " << geometry::wkt(intersection_point) << " " << int(incoming) << std::endl; + if (geometry::equals(direction_point, intersection_point)) + { + //std::cout << "EQUAL! Skipping" << std::endl; + return; + } + + AngleInfo info; + info.incoming = incoming; + info.angle = calculate_angle(direction_point, map_point); + info.seg_id = seg_id; + info.turn_index = turn_index; + info.operation_index = operation_index; + info.intersection_point = intersection_point; + info.direction_point = direction_point; + angles.push_back(info); + + m_calculated = false; + } + + inline bool occupied() + { + if (! m_calculated) + { + m_occupied = is_occupied(); + m_calculated = true; + } + return m_occupied; + } + + template + inline void get_left_turns( + Turns& turns, TurnSegmentIndices const& turn_segment_indices, + std::set& keep_indices) + { + std::sort(angles.begin(), angles.end(), angle_sort()); + calculate_left_turns(angles, turns, turn_segment_indices, keep_indices); + } +}; + + +template +inline void add_incoming_and_outgoing_angles(Point const& map_point, Point const& intersection_point, + Ring const& ring, + int turn_index, + int operation_index, + segment_identifier seg_id, + Info& info) +{ + typedef typename boost::range_iterator + < + Ring const + >::type iterator_type; + + int const n = boost::size(ring); + if (seg_id.segment_index >= n || seg_id.segment_index < 0) + { + return; + } + + segment_identifier real_seg_id = seg_id; + iterator_type it = boost::begin(ring) + seg_id.segment_index; + + // TODO: if we use turn-info ("to", "middle"), we know if to advance without resorting to equals + relaxed_less comparator; + + if (comparator.equals(intersection_point, *it)) + { + // It should be equal only once. But otherwise we skip it (in "add") + it = advance_circular(it, ring, seg_id, false); + } + + info.add(map_point, *it, intersection_point, turn_index, operation_index, real_seg_id, true); + + if (comparator.equals(intersection_point, *it)) + { + it = advance_circular(it, ring, real_seg_id); + } + else + { + // Don't upgrade the ID + it = advance_circular(it, ring, seg_id); + } + for (int defensive_check = 0; + comparator.equals(intersection_point, *it) && defensive_check < n; + defensive_check++) + { + it = advance_circular(it, ring, real_seg_id); + } + + info.add(map_point, *it, intersection_point, turn_index, operation_index, real_seg_id, false); +} + + +// Map in two senses of the word: it is a std::map where the key is a point. +// Per point an "occupation_info" record is kept +// Used for the buffer (but will also be used for intersections/unions having complex self-tangencies) +template +class occupation_map +{ +public : + typedef std::map > map_type; + + map_type map; + std::set turn_indices; + + inline OccupationInfo& find_or_insert(Point const& point, Point& mapped_point) + { + typename map_type::iterator it = map.find(point); + if (it == boost::end(map)) + { + std::pair pair + = map.insert(std::make_pair(point, OccupationInfo())); + it = pair.first; + } + mapped_point = it->first; + return it->second; + } + + inline bool contains(Point const& point) const + { + typename map_type::const_iterator it = map.find(point); + return it != boost::end(map); + } + + inline bool contains_turn_index(int index) const + { + return turn_indices.count(index) > 0; + } + + inline void insert_turn_index(int index) + { + turn_indices.insert(index); + } +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OCCUPATION_INFO_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/add_rings.hpp b/include/boost/geometry/algorithms/detail/overlay/add_rings.hpp index eb3e60e48..74595fedd 100644 --- a/include/boost/geometry/algorithms/detail/overlay/add_rings.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/add_rings.hpp @@ -9,6 +9,8 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP +#include +#include #include #include @@ -73,6 +75,21 @@ inline OutputIterator add_rings(SelectionMap const& map, OutputIterator out) { typedef typename SelectionMap::const_iterator iterator; + typedef typename SelectionMap::mapped_type property_type; + typedef typename property_type::area_type area_type; + + area_type const zero = 0; + std::size_t const min_num_points = core_detail::closure::minimum_ring_size + < + geometry::closure + < + typename boost::range_value + < + RingCollection const + >::type + >::value + >::value; + for (iterator it = boost::begin(map); it != boost::end(map); @@ -99,7 +116,16 @@ inline OutputIterator add_rings(SelectionMap const& map, *child_it, mit->second.reversed, true); } } - *out++ = result; + + // Only add rings if they satisfy minimal requirements. + // This cannot be done earlier (during traversal), not + // everything is figured out yet (sum of positive/negative rings) + // TODO: individual rings can still contain less than 3 points. + if (geometry::num_points(result) >= min_num_points + && math::larger(geometry::area(result), zero)) + { + *out++ = result; + } } } return out; diff --git a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index 133530563..5063f49eb 100644 --- a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -130,7 +130,7 @@ struct assign_visitor return; } - if (outer.real_area > 0) + if (math::larger(outer.real_area, 0)) { if (inner.real_area < 0 || m_check_for_orientation) { @@ -317,13 +317,14 @@ template > inline void assign_parents(Geometry const& geometry, RingCollection const& collection, - RingMap& ring_map) + RingMap& ring_map, + bool check_for_orientation) { // Call it with an empty geometry // (ring_map should be empty for source_id==1) Geometry empty; - assign_parents(geometry, empty, collection, ring_map, true); + assign_parents(geometry, empty, collection, ring_map, check_for_orientation); } diff --git a/include/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp b/include/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp index 3e6a8897f..2003d2350 100644 --- a/include/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp @@ -32,9 +32,18 @@ struct calculate_distance_policy { static bool const include_no_turn = false; static bool const include_degenerate = false; + static bool const include_opposite = false; - template - static inline void apply(Info& info, Point1 const& p1, Point2 const& p2) + template + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply(Info& info, Point1 const& p1, Point2 const& p2, + IntersectionInfo const&, DirInfo const&) { info.operations[0].enriched.distance = geometry::comparable_distance(info.point, p1); diff --git a/include/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp b/include/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp index 379444428..5e18d0453 100644 --- a/include/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp @@ -55,14 +55,14 @@ struct copy_segment_point_range if (second) { index++; - if (index >= boost::size(range)) + if (index >= int(boost::size(range))) { index = 0; } } // Exception? - if (index >= boost::size(range)) + if (index >= int(boost::size(range))) { return false; } diff --git a/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp b/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp index b0183a3ac..805f3923e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp @@ -78,7 +78,7 @@ struct copy_segments_ring int const from_index = seg_id.segment_index + 1; // Sanity check - BOOST_ASSERT(from_index < boost::size(view)); + BOOST_ASSERT(from_index < int(boost::size(view))); ec_iterator it(boost::begin(view), boost::end(view), boost::begin(view) + from_index); @@ -89,7 +89,7 @@ struct copy_segments_ring typedef typename boost::range_difference::type size_type; size_type const count = from_index <= to_index ? to_index - from_index + 1 - : boost::size(view) - from_index + to_index + 1; + : int(boost::size(view)) - from_index + to_index + 1; for (size_type i = 0; i < count; ++i, ++it) { @@ -117,7 +117,7 @@ struct copy_segments_linestring int const from_index = seg_id.segment_index + 1; // Sanity check - if (from_index > to_index || from_index < 0 || to_index >= boost::size(ls)) + if (from_index > to_index || from_index < 0 || to_index >= int(boost::size(ls))) { return; } diff --git a/include/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp index 0bec81694..0cc34255c 100644 --- a/include/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp @@ -28,6 +28,7 @@ inline char method_char(detail::overlay::method_type const& method) case method_touch_interior : return 'm'; case method_collinear : return 'c'; case method_equal : return 'e'; + case method_error : return '!'; default : return '?'; } } @@ -42,6 +43,7 @@ inline char operation_char(detail::overlay::operation_type const& operation) case operation_intersection : return 'i'; case operation_blocked : return 'x'; case operation_continue : return 'c'; + case operation_opposite : return 'o'; default : return '?'; } } diff --git a/include/boost/geometry/algorithms/detail/overlay/follow.hpp b/include/boost/geometry/algorithms/detail/overlay/follow.hpp index 087092a5f..b110cc960 100644 --- a/include/boost/geometry/algorithms/detail/overlay/follow.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -279,12 +279,12 @@ class follow { // In case of turn point at the same location, we want to have continue/blocked LAST // because that should be followed (intersection) or skipped (difference). - // By chance the enumeration is ordered like that but we keep the safe way here. inline int operation_order(Turn const& turn) const { operation_type const& operation = turn.operations[0].operation; switch(operation) { + case operation_opposite : return 0; case operation_none : return 0; case operation_union : return 1; case operation_intersection : return 2; diff --git a/include/boost/geometry/algorithms/detail/overlay/get_ring.hpp b/include/boost/geometry/algorithms/detail/overlay/get_ring.hpp index ad4766550..c2c698057 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_ring.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_ring.hpp @@ -83,7 +83,7 @@ struct get_ring BOOST_ASSERT ( id.ring_index >= -1 - && id.ring_index < boost::size(interior_rings(polygon)) + && id.ring_index < int(boost::size(interior_rings(polygon))) ); return id.ring_index < 0 ? exterior_ring(polygon) 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 663d70d9a..b8320d9b7 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -250,9 +250,15 @@ struct touch : public base_turn_handler int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); int const side_pk_p = SideStrategy::apply(pi, pj, pk); int const side_qk_q = SideStrategy::apply(qi, qj, qk); + + bool const both_continue = side_pk_p == 0 && side_qk_q == 0; + bool const robustness_issue_in_continue = both_continue && side_pk_q2 != 0; + bool const q_turns_left = side_qk_q == 1; bool const block_q = side_qk_p1 == 0 - && ! same(side_qi_p1, side_qk_q); + && ! same(side_qi_p1, side_qk_q) + && ! robustness_issue_in_continue + ; // If Pk at same side as Qi/Qk // (the "or" is for collinear case) @@ -278,7 +284,7 @@ struct touch : public base_turn_handler if (side_pk_q1 == 0) { ti.operations[0].operation = operation_blocked; - // Q turns right -> union (both independant), + // Q turns right -> union (both independent), // Q turns left -> intersection ti.operations[1].operation = block_q ? operation_blocked : q_turns_left ? operation_intersection @@ -463,6 +469,45 @@ struct equal : public base_turn_handler }; +template +< + typename TurnInfo, + typename AssignPolicy +> +struct equal_opposite : public base_turn_handler +{ + template + < + typename Point1, + typename Point2, + typename OutputIterator, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply(Point1 const& pi, Point2 const& qi, + /* by value: */ TurnInfo tp, + OutputIterator& out, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + // For equal-opposite segments, normally don't do anything. + if (AssignPolicy::include_opposite) + { + tp.method = method_equal; + for (int i = 0; i < 2; i++) + { + tp.operations[i].operation = operation_opposite; + } + for (unsigned int i = 0; i < intersection_info.count; i++) + { + geometry::convert(intersection_info.intersections[i], tp.point); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + *out++ = tp; + } + } + } +}; + template < typename TurnInfo, @@ -494,6 +539,13 @@ struct collinear : public base_turn_handler - if P arrives and P turns right: intersection for P - if Q arrives and Q turns left: union for Q (=intersection for P) - if Q arrives and Q turns right: intersection for Q (=union for P) + + ROBUSTNESS: p and q are collinear, so you would expect + that side qk//p1 == pk//q1. But that is not always the case + in near-epsilon ranges. Then decision logic is different. + If p arrives, q is further, so the angle qk//p1 is (normally) + more precise than pk//p1 + */ template < @@ -516,12 +568,18 @@ struct collinear : public base_turn_handler // Should not be 0, this is checked before BOOST_ASSERT(arrival != 0); + int const side_p = SideStrategy::apply(pi, pj, pk); + int const side_q = SideStrategy::apply(qi, qj, qk); + // If p arrives, use p, else use q int const side_p_or_q = arrival == 1 - ? SideStrategy::apply(pi, pj, pk) - : SideStrategy::apply(qi, qj, qk) + ? side_p + : side_q ; + int const side_pk = SideStrategy::apply(qi, qj, pk); + int const side_qk = SideStrategy::apply(pi, pj, qk); + // See comments above, // resulting in a strange sort of mathematic rule here: // The arrival-info multiplied by the relevant side @@ -529,7 +587,15 @@ struct collinear : public base_turn_handler int const product = arrival * side_p_or_q; - if(product == 0) + // Robustness: side_p is supposed to be equal to side_pk (because p/q are collinear) + // and side_q to side_qk + bool const robustness_issue = side_pk != side_p || side_qk != side_q; + + if (robustness_issue) + { + handle_robustness(ti, arrival, side_p, side_q, side_pk, side_qk); + } + else if(product == 0) { both(ti, operation_continue); } @@ -538,6 +604,38 @@ struct collinear : public base_turn_handler ui_else_iu(product == 1, ti); } } + + static inline void handle_robustness(TurnInfo& ti, int arrival, + int side_p, int side_q, int side_pk, int side_qk) + { + // We take the longer one, i.e. if q arrives in p (arrival == -1), + // then p exceeds q and we should take p for a union... + + bool use_p_for_union = arrival == -1; + + // ... unless one of the sides consistently directs to the other side + int const consistent_side_p = side_p == side_pk ? side_p : 0; + int const consistent_side_q = side_q == side_qk ? side_q : 0; + if (arrival == -1 && (consistent_side_p == -1 || consistent_side_q == 1)) + { + use_p_for_union = false; + } + if (arrival == 1 && (consistent_side_p == 1 || consistent_side_q == -1)) + { + use_p_for_union = true; + } + + //std::cout << "ROBUSTNESS -> Collinear " + // << " arr: " << arrival + // << " dir: " << side_p << " " << side_q + // << " rev: " << side_pk << " " << side_qk + // << " cst: " << cside_p << " " << cside_q + // << std::boolalpha << " " << use_p_for_union + // << std::endl; + + ui_else_iu(use_p_for_union, ti); + } + }; template @@ -583,6 +681,7 @@ private : TurnInfo& tp, IntersectionInfo const& intersection_info) { int const side_rk_r = SideStrategy::apply(ri, rj, rk); + operation_type blocked = operation_blocked; switch(side_rk_r) { @@ -596,15 +695,24 @@ private : break; case 0 : // No turn on opposite collinear: block, do not traverse - // But this "xx" is ignored here, it is useless to include - // two operation blocked, so the whole point does not need + // But this "xx" is usually ignored, it is useless to include + // two operations blocked, so the whole point does not need // to be generated. // So return false to indicate nothing is to be done. - return false; + if (AssignPolicy::include_opposite) + { + tp.operations[Index].operation = operation_opposite; + blocked = operation_opposite; + } + else + { + return false; + } + break; } // The other direction is always blocked when collinear opposite - tp.operations[1 - Index].operation = operation_blocked; + tp.operations[1 - Index].operation = blocked; // If P arrives within Q, set info on P (which is done above, index=0), // this turn-info belongs to the second intersection point, index=1 @@ -633,32 +741,45 @@ public: IntersectionInfo const& intersection_info, DirInfo const& dir_info) { - /* - std::cout << "arrivals: " - << dir_info.arrival[0] - << "/" << dir_info.arrival[1] - << std::endl; - */ - TurnInfo tp = tp_model; tp.method = method_collinear; - // If P arrives within Q, there is a turn dependant on P + // If P arrives within Q, there is a turn dependent on P if (dir_info.arrival[0] == 1 && set_tp<0>(pi, pj, pk, tp, intersection_info)) { - AssignPolicy::apply(tp, pi, qi); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); *out++ = tp; } - // If Q arrives within P, there is a turn dependant on Q + // If Q arrives within P, there is a turn dependent on Q if (dir_info.arrival[1] == 1 && set_tp<1>(qi, qj, qk, tp, intersection_info)) { - AssignPolicy::apply(tp, pi, qi); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); *out++ = tp; } + + if (AssignPolicy::include_opposite) + { + // Handle cases not yet handled above + if ((dir_info.arrival[1] == -1 && dir_info.arrival[0] == 0) + || (dir_info.arrival[0] == -1 && dir_info.arrival[1] == 0)) + { + for (int i = 0; i < 2; i++) + { + tp.operations[i].operation = operation_opposite; + } + for (unsigned int i = 0; i < intersection_info.count; i++) + { + geometry::convert(intersection_info.intersections[i], tp.point); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + *out++ = tp; + } + } + } + } }; @@ -722,9 +843,17 @@ struct assign_null_policy { static bool const include_no_turn = false; static bool const include_degenerate = false; + static bool const include_opposite = false; - template - static inline void apply(Info& , Point1 const& , Point2 const& ) + template + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply(Info& , Point1 const& , Point2 const&, IntersectionInfo const&, DirInfo const&) {} }; @@ -763,8 +892,9 @@ struct get_turn_info typedef typename si::segment_intersection_strategy_type strategy; - - + // Intersect pi-pj with qi-qj + // The points pk and qk are only used do determine more information + // about the turn. template static inline OutputIterator apply( Point1 const& pi, Point1 const& pj, Point1 const& pk, @@ -796,7 +926,7 @@ struct get_turn_info { only_convert::apply(tp, result.template get<0>()); - AssignPolicy::apply(tp, pi, qi); + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } break; @@ -824,7 +954,7 @@ struct get_turn_info policy::template apply<1>(qi, qj, qk, pi, pj, pk, tp, result.template get<0>(), result.template get<1>()); } - AssignPolicy::apply(tp, pi, qi); + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } break; @@ -838,7 +968,7 @@ struct get_turn_info policy::apply(pi, pj, pk, qi, qj, qk, tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi); + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } break; @@ -853,7 +983,7 @@ struct get_turn_info policy::apply(pi, pj, pk, qi, qj, qk, tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi); + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } break; @@ -871,10 +1001,18 @@ struct get_turn_info policy::apply(pi, pj, pk, qi, qj, qk, tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi); + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } - // If they ARE opposite, don't do anything. + else + { + equal_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, qi, + tp, out, result.template get<0>(), result.template get<1>()); + } } break; case 'c' : @@ -906,7 +1044,7 @@ struct get_turn_info tp, result.template get<0>(), result.template get<1>()); } - AssignPolicy::apply(tp, pi, qi); + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } else @@ -927,7 +1065,7 @@ struct get_turn_info if (AssignPolicy::include_degenerate) { only_convert::apply(tp, result.template get<0>()); - AssignPolicy::apply(tp, pi, qi); + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } } diff --git a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp index aa6b428f1..89a60b21a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -28,7 +28,8 @@ enum operation_type operation_union, operation_intersection, operation_blocked, - operation_continue + operation_continue, + operation_opposite }; @@ -102,6 +103,12 @@ struct turn_info { return has12(type, type); } + + inline bool has(operation_type type) const + { + return this->operations[0].operation == type + || this->operations[1].operation == type; + } inline bool combination(operation_type type1, operation_type type2) const { @@ -114,10 +121,13 @@ struct turn_info { return both(operation_blocked); } + inline bool opposite() const + { + return both(operation_opposite); + } inline bool any_blocked() const { - return this->operations[0].operation == operation_blocked - || this->operations[1].operation == operation_blocked; + return has(operation_blocked); } diff --git a/include/boost/geometry/algorithms/detail/partition.hpp b/include/boost/geometry/algorithms/detail/partition.hpp index 7a7de2cdd..be98fb4e9 100644 --- a/include/boost/geometry/algorithms/detail/partition.hpp +++ b/include/boost/geometry/algorithms/detail/partition.hpp @@ -153,7 +153,7 @@ class partition_one_collection static inline void next_level(Box const& box, InputCollection const& collection, index_vector_type const& input, - int level, int min_elements, + int level, std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) { if (boost::size(input) > 0) @@ -176,7 +176,7 @@ public : InputCollection const& collection, index_vector_type const& input, int level, - int min_elements, + std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) { box_policy.apply(box, level); @@ -230,7 +230,7 @@ class partition_two_collections index_vector_type const& input1, InputCollection const& collection2, index_vector_type const& input2, - int level, int min_elements, + int level, std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) { if (boost::size(input1) > 0 && boost::size(input2) > 0) @@ -257,7 +257,7 @@ public : InputCollection const& collection1, index_vector_type const& input1, InputCollection const& collection2, index_vector_type const& input2, int level, - int min_elements, + std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) { box_policy.apply(box, level); @@ -335,7 +335,7 @@ public : template static inline void apply(InputCollection const& collection, VisitPolicy& visitor, - int min_elements = 16, + std::size_t min_elements = 16, VisitBoxPolicy box_visitor = visit_no_policy() ) { @@ -377,7 +377,7 @@ public : static inline void apply(InputCollection const& collection1, InputCollection const& collection2, VisitPolicy& visitor, - int min_elements = 16, + std::size_t min_elements = 16, VisitBoxPolicy box_visitor = visit_no_policy() ) { diff --git a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp index 36bcbdd6e..a6e6837fe 100644 --- a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp +++ b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -254,7 +254,7 @@ struct sectionalize_part Range const& range, ring_identifier ring_id) { - if (boost::size(range) <= index) + if (int(boost::size(range)) <= index) { return; } diff --git a/include/boost/geometry/algorithms/disjoint.hpp b/include/boost/geometry/algorithms/disjoint.hpp index f36b8dbdd..f986cc24a 100644 --- a/include/boost/geometry/algorithms/disjoint.hpp +++ b/include/boost/geometry/algorithms/disjoint.hpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -44,15 +45,57 @@ namespace boost { namespace geometry namespace detail { namespace disjoint { +template +struct check_each_ring_for_within +{ + bool has_within; + Geometry const& m_geometry; + + inline check_each_ring_for_within(Geometry const& g) + : has_within(false) + , m_geometry(g) + {} + + template + inline void apply(Range const& range) + { + typename geometry::point_type::type p; + geometry::point_on_border(p, range); + if (geometry::within(p, m_geometry)) + { + has_within = true; + } + } +}; + +template +inline bool rings_containing(FirstGeometry const& geometry1, + SecondGeometry const& geometry2) +{ + check_each_ring_for_within checker(geometry1); + geometry::detail::for_each_range(geometry2, checker); + return checker.has_within; +} + + struct assign_disjoint_policy { // We want to include all points: static bool const include_no_turn = true; static bool const include_degenerate = true; + static bool const include_opposite = true; // We don't assign extra info: - template - static inline void apply(Info& , Point1 const& , Point2 const& ) + template + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply(Info& , Point1 const& , Point2 const&, + IntersectionInfo const&, DirInfo const&) {} }; @@ -107,8 +150,6 @@ struct disjoint_segment } }; - - template struct general_areal { @@ -119,20 +160,10 @@ struct general_areal return false; } - typedef typename geometry::point_type::type point_type; - // If there is no intersection of segments, they might located // inside each other - point_type p1; - geometry::point_on_border(p1, geometry1); - if (geometry::within(p1, geometry2)) - { - return false; - } - - typename geometry::point_type::type p2; - geometry::point_on_border(p2, geometry2); - if (geometry::within(p2, geometry1)) + if (rings_containing(geometry1, geometry2) + || rings_containing(geometry2, geometry1)) { return false; } diff --git a/include/boost/geometry/algorithms/distance.hpp b/include/boost/geometry/algorithms/distance.hpp index eca3b03c7..11c2bc929 100644 --- a/include/boost/geometry/algorithms/distance.hpp +++ b/include/boost/geometry/algorithms/distance.hpp @@ -130,7 +130,7 @@ struct point_to_range // check if other segments are closer for (++prev, ++it; it != boost::end(view); ++prev, ++it) { - return_type const ds = ps_strategy.apply(point, *prev, *it); + return_type const ds = eps_strategy.apply(point, *prev, *it); if (geometry::math::equals(ds, zero)) { return ds; diff --git a/include/boost/geometry/algorithms/touches.hpp b/include/boost/geometry/algorithms/touches.hpp new file mode 100644 index 000000000..7d424af42 --- /dev/null +++ b/include/boost/geometry/algorithms/touches.hpp @@ -0,0 +1,181 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// 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) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_TOUCHES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_TOUCHES_HPP + + +#include + +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace detail { namespace touches +{ + +template +inline bool ok_for_touch(Turn const& turn) +{ + return turn.both(detail::overlay::operation_union) + || turn.both(detail::overlay::operation_blocked) + || turn.combination(detail::overlay::operation_union, detail::overlay::operation_blocked) + ; +} + +template +inline bool has_only_turns(Turns const& turns) +{ + bool has_touch = false; + typedef typename boost::range_iterator::type iterator_type; + for (iterator_type it = boost::begin(turns); it != boost::end(turns); ++it) + { + if (it->has(detail::overlay::operation_intersection)) + { + return false; + } + + switch(it->method) + { + case detail::overlay::method_crosses: + return false; + case detail::overlay::method_equal: + // Segment spatially equal means: at the right side + // the polygon internally overlaps. So return false. + return false; + case detail::overlay::method_touch: + case detail::overlay::method_touch_interior: + case detail::overlay::method_collinear: + if (ok_for_touch(*it)) + { + has_touch = true; + } + else + { + return false; + } + break; + case detail::overlay::method_none : + case detail::overlay::method_disjoint : + case detail::overlay::method_error : + break; + } + } + return has_touch; +} + +}} + +/*! +\brief \brief_check{has at least one touching point (self-tangency)} +\note This function can be called for one geometry (self-tangency) and + also for two geometries (touch) +\ingroup touches +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_check{is self-touching} + +\qbk{distinguish,one geometry} +\qbk{[def __one_parameter__]} +\qbk{[include reference/algorithms/touches.qbk]} +*/ +template +inline bool touches(Geometry const& geometry) +{ + concept::check(); + + typedef detail::overlay::turn_info + < + typename geometry::point_type::type + > turn_info; + + typedef detail::overlay::get_turn_info + < + typename point_type::type, + typename point_type::type, + turn_info, + detail::overlay::assign_null_policy + > policy_type; + + std::deque turns; + detail::self_get_turn_points::no_interrupt_policy policy; + detail::self_get_turn_points::get_turns + < + Geometry, + std::deque, + policy_type, + detail::self_get_turn_points::no_interrupt_policy + >::apply(geometry, turns, policy); + + return detail::touches::has_only_turns(turns); +} + + +/*! +\brief \brief_check2{have at least one touching point (tangent - non overlapping)} +\ingroup touches +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{touch each other} + +\qbk{distinguish,two geometries} +\qbk{[include reference/algorithms/touches.qbk]} + */ +template +inline bool touches(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check(); + concept::check(); + + + typedef detail::overlay::turn_info + < + typename geometry::point_type::type + > turn_info; + + typedef detail::overlay::get_turn_info + < + typename point_type::type, + typename point_type::type, + turn_info, + detail::overlay::assign_null_policy + > policy_type; + + std::deque turns; + detail::get_turns::no_interrupt_policy policy; + boost::geometry::get_turns + < + false, false, + detail::overlay::assign_null_policy + >(geometry1, geometry2, turns, policy); + + return detail::touches::has_only_turns(turns) + && ! geometry::detail::disjoint::rings_containing(geometry1, geometry2) + && ! geometry::detail::disjoint::rings_containing(geometry2, geometry1) + ; +} + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_TOUCHES_HPP diff --git a/include/boost/geometry/algorithms/within.hpp b/include/boost/geometry/algorithms/within.hpp index 0e0cb68d5..f1f0993d7 100644 --- a/include/boost/geometry/algorithms/within.hpp +++ b/include/boost/geometry/algorithms/within.hpp @@ -65,7 +65,7 @@ struct point_in_ring static inline int apply(Point const& point, Ring const& ring, Strategy const& strategy) { - if (boost::size(ring) + if (int(boost::size(ring)) < core_detail::closure::minimum_ring_size::value) { return -1; diff --git a/include/boost/geometry/iterators/ever_circling_iterator.hpp b/include/boost/geometry/iterators/ever_circling_iterator.hpp index 03921096e..566669e26 100644 --- a/include/boost/geometry/iterators/ever_circling_iterator.hpp +++ b/include/boost/geometry/iterators/ever_circling_iterator.hpp @@ -95,67 +95,115 @@ private: bool m_skip_first; }; - - template -class ever_circling_range_iterator - : public boost::iterator_adaptor - < - ever_circling_range_iterator, - typename boost::range_iterator::type - > +struct ever_circling_range_iterator + : public boost::iterator_facade + < + ever_circling_range_iterator, + typename boost::range_value::type const, + boost::random_access_traversal_tag + > { -public : - typedef typename boost::range_iterator::type iterator_type; + /// Constructor including the range it is based on + explicit inline ever_circling_range_iterator(Range& range) + : m_range(&range) + , m_iterator(boost::begin(range)) + , m_size(boost::size(range)) + , m_index(0) + {} - explicit inline ever_circling_range_iterator(Range& range, - bool skip_first = false) - : m_range(range) - , m_skip_first(skip_first) + /// Default constructor + explicit inline ever_circling_range_iterator() + : m_range(NULL) + , m_size(0) + , m_index(0) + {} + + inline ever_circling_range_iterator& operator=(ever_circling_range_iterator const& source) { - this->base_reference() = boost::begin(m_range); + m_range = source.m_range; + m_iterator = source.m_iterator; + m_size = source.m_size; + m_index = source.m_index; + return *this; } - explicit inline ever_circling_range_iterator(Range& range, iterator_type start, - bool skip_first = false) - : m_range(range) - , m_skip_first(skip_first) - { - this->base_reference() = start; - } - - /// Navigate to a certain position, should be in [start .. end], if at end - /// it will circle again. - inline void moveto(iterator_type it) - { - this->base_reference() = it; - check_end(); - } + typedef std::ptrdiff_t difference_type; private: - friend class boost::iterator_core_access; - inline void increment(bool possibly_skip = true) + inline typename boost::range_value::type const& dereference() const { - (this->base_reference())++; - check_end(possibly_skip); + return *m_iterator; } - inline void check_end(bool possibly_skip = true) + inline difference_type distance_to(ever_circling_range_iterator const& other) const { - if (this->base_reference() == boost::end(m_range)) + return other.m_index - this->m_index; + } + + inline bool equal(ever_circling_range_iterator const& other) const + { + return this->m_range == other.m_range + && this->m_index == other.m_index; + } + + inline void increment() + { + ++m_index; + if (m_index >= 0 && m_index < m_size) { - this->base_reference() = boost::begin(m_range); - if (m_skip_first && possibly_skip) - { - increment(false); - } + ++m_iterator; + } + else + { + update_iterator(); } } - Range& m_range; - bool m_skip_first; + inline void decrement() + { + --m_index; + if (m_index >= 0 && m_index < m_size) + { + --m_iterator; + } + else + { + update_iterator(); + } + } + + inline void advance(difference_type n) + { + if (m_index >= 0 && m_index < m_size + && m_index + n >= 0 && m_index + n < m_size) + { + m_index += n; + m_iterator += n; + } + else + { + m_index += n; + update_iterator(); + } + } + + inline void update_iterator() + { + while (m_index < 0) + { + m_index += m_size; + } + m_index = m_index % m_size; + this->m_iterator = boost::begin(*m_range) + m_index; + } + + Range* m_range; + typename boost::range_iterator::type m_iterator; + difference_type m_size; + difference_type m_index; }; diff --git a/include/boost/geometry/multi/algorithms/detail/overlay/copy_segment_point.hpp b/include/boost/geometry/multi/algorithms/detail/overlay/copy_segment_point.hpp index 1093a8456..72be5ddbf 100644 --- a/include/boost/geometry/multi/algorithms/detail/overlay/copy_segment_point.hpp +++ b/include/boost/geometry/multi/algorithms/detail/overlay/copy_segment_point.hpp @@ -42,7 +42,7 @@ struct copy_segment_point_multi BOOST_ASSERT ( seg_id.multi_index >= 0 - && seg_id.multi_index < boost::size(multi) + && seg_id.multi_index < int(boost::size(multi)) ); // Call the single-version diff --git a/include/boost/geometry/multi/algorithms/detail/overlay/copy_segments.hpp b/include/boost/geometry/multi/algorithms/detail/overlay/copy_segments.hpp index a234e240d..f474b12fd 100644 --- a/include/boost/geometry/multi/algorithms/detail/overlay/copy_segments.hpp +++ b/include/boost/geometry/multi/algorithms/detail/overlay/copy_segments.hpp @@ -44,7 +44,7 @@ struct copy_segments_multi BOOST_ASSERT ( seg_id.multi_index >= 0 - && seg_id.multi_index < boost::size(multi_geometry) + && seg_id.multi_index < int(boost::size(multi_geometry)) ); // Call the single-version diff --git a/include/boost/geometry/multi/algorithms/detail/overlay/get_ring.hpp b/include/boost/geometry/multi/algorithms/detail/overlay/get_ring.hpp index 8cdd7ed97..e23acf99b 100644 --- a/include/boost/geometry/multi/algorithms/detail/overlay/get_ring.hpp +++ b/include/boost/geometry/multi/algorithms/detail/overlay/get_ring.hpp @@ -35,7 +35,7 @@ struct get_ring BOOST_ASSERT ( id.multi_index >= 0 - && id.multi_index < boost::size(multi_polygon) + && id.multi_index < int(boost::size(multi_polygon)) ); return get_ring::apply(id, multi_polygon[id.multi_index]); diff --git a/include/boost/geometry/multi/algorithms/detail/point_on_border.hpp b/include/boost/geometry/multi/algorithms/detail/point_on_border.hpp index 04d76422c..ac462ed4c 100644 --- a/include/boost/geometry/multi/algorithms/detail/point_on_border.hpp +++ b/include/boost/geometry/multi/algorithms/detail/point_on_border.hpp @@ -31,13 +31,13 @@ namespace detail { namespace point_on_border template < - typename MultiGeometry, typename Point, + typename MultiGeometry, typename Policy > struct point_on_multi { - static inline bool apply(MultiGeometry const& multi, Point& point) + static inline bool apply(Point& point, MultiGeometry const& multi, bool midpoint) { // Take a point on the first multi-geometry // (i.e. the first that is not empty) @@ -48,7 +48,7 @@ struct point_on_multi it != boost::end(multi); ++it) { - if (Policy::apply(*it, point)) + if (Policy::apply(point, *it, midpoint)) { return true; } @@ -69,16 +69,16 @@ namespace dispatch { -template -struct point_on_border +template +struct point_on_border : detail::point_on_border::point_on_multi < - Multi, Point, + Multi, detail::point_on_border::point_on_polygon < - typename boost::range_value::type, - Point + Point, + typename boost::range_value::type > > {}; diff --git a/include/boost/geometry/multi/algorithms/detail/sections/range_by_section.hpp b/include/boost/geometry/multi/algorithms/detail/sections/range_by_section.hpp index cf4bf5d83..28a4805ed 100644 --- a/include/boost/geometry/multi/algorithms/detail/sections/range_by_section.hpp +++ b/include/boost/geometry/multi/algorithms/detail/sections/range_by_section.hpp @@ -46,7 +46,7 @@ struct full_section_multi BOOST_ASSERT ( section.ring_id.multi_index >= 0 - && section.ring_id.multi_index < boost::size(multi) + && section.ring_id.multi_index < int(boost::size(multi)) ); return Policy::apply(multi[section.ring_id.multi_index], section); diff --git a/include/boost/geometry/multi/io/wkt/read.hpp b/include/boost/geometry/multi/io/wkt/read.hpp index 3c75fd804..218ddf999 100644 --- a/include/boost/geometry/multi/io/wkt/read.hpp +++ b/include/boost/geometry/multi/io/wkt/read.hpp @@ -58,6 +58,69 @@ struct multi_parser handle_close_parenthesis(it, tokens.end(), wkt); } + + check_end(it, tokens.end(), wkt); + } +}; + +template +struct noparenthesis_point_parser +{ + static inline void apply(tokenizer::iterator& it, tokenizer::iterator end, + std::string const& wkt, P& point) + { + parsing_assigner::value>::apply(it, end, point, wkt); + } +}; + +template +struct multi_point_parser +{ + static inline void apply(std::string const& wkt, MultiGeometry& geometry) + { + traits::clear::apply(geometry); + + tokenizer tokens(wkt, boost::char_separator(" ", ",()")); + tokenizer::iterator it; + + if (initialize(tokens, PrefixPolicy::apply(), wkt, it)) + { + handle_open_parenthesis(it, tokens.end(), wkt); + + // If first point definition starts with "(" then parse points as (x y) + // otherwise as "x y" + bool using_brackets = (it != tokens.end() && *it == "("); + + while(it != tokens.end() && *it != ")") + { + traits::resize::apply(geometry, boost::size(geometry) + 1); + + if (using_brackets) + { + point_parser + < + typename boost::range_value::type + >::apply(it, tokens.end(), wkt, geometry.back()); + } + else + { + noparenthesis_point_parser + < + typename boost::range_value::type + >::apply(it, tokens.end(), wkt, geometry.back()); + } + + if (it != tokens.end() && *it == ",") + { + // Skip "," after point is parsed + ++it; + } + } + + handle_close_parenthesis(it, tokens.end(), wkt); + } + + check_end(it, tokens.end(), wkt); } }; @@ -69,10 +132,9 @@ namespace dispatch template struct read_wkt - : detail::wkt::multi_parser + : detail::wkt::multi_point_parser < MultiGeometry, - detail::wkt::point_parser, detail::wkt::prefix_multipoint > {}; diff --git a/include/boost/geometry/policies/relate/direction.hpp b/include/boost/geometry/policies/relate/direction.hpp index 9090d8b51..cfbaf7dd1 100644 --- a/include/boost/geometry/policies/relate/direction.hpp +++ b/include/boost/geometry/policies/relate/direction.hpp @@ -126,7 +126,9 @@ struct segments_direction typedef typename select_most_precise::type rtype; + template static inline return_type segments_intersect(side_info const& sides, + R const&, coordinate_type const& dx1, coordinate_type const& dy1, coordinate_type const& dx2, coordinate_type const& dy2, S1 const& s1, S2 const& s2) diff --git a/include/boost/geometry/policies/relate/intersection_points.hpp b/include/boost/geometry/policies/relate/intersection_points.hpp index afd1dde50..d7d519905 100644 --- a/include/boost/geometry/policies/relate/intersection_points.hpp +++ b/include/boost/geometry/policies/relate/intersection_points.hpp @@ -36,56 +36,33 @@ struct segments_intersection_points typedef ReturnType return_type; typedef S1 segment_type1; typedef S2 segment_type2; + typedef typename select_calculation_type < S1, S2, CalculationType >::type coordinate_type; + template static inline return_type segments_intersect(side_info const&, + R const& r, coordinate_type const& dx1, coordinate_type const& dy1, coordinate_type const& dx2, coordinate_type const& dy2, S1 const& s1, S2 const& s2) { - return_type result; typedef typename geometry::coordinate_type < typename return_type::point_type - >::type coordinate_type; + >::type return_coordinate_type; - // Get the same type, but at least a double (also used for divisions) - typedef typename select_most_precise - < - coordinate_type, double - >::type promoted_type; - - promoted_type const s1x = get<0, 0>(s1); - promoted_type const s1y = get<0, 1>(s1); - - // Calculate other determinants - Cramers rule - promoted_type const wx = get<0, 0>(s1) - get<0, 0>(s2); - promoted_type const wy = get<0, 1>(s1) - get<0, 1>(s2); - promoted_type const d = detail::determinant(dx1, dy1, dx2, dy2); - promoted_type const da = detail::determinant(dx2, dy2, wx, wy); - - // r: ratio 0-1 where intersection divides A/B - promoted_type r = da / d; - promoted_type const zero = promoted_type(); - promoted_type const one = 1; - // Handle robustness issues - if (r < zero) - { - r = zero; - } - else if (r > one) - { - r = one; - } + coordinate_type const s1x = get<0, 0>(s1); + coordinate_type const s1y = get<0, 1>(s1); + return_type result; result.count = 1; set<0>(result.intersections[0], - boost::numeric_cast(s1x + r * promoted_type(dx1))); + boost::numeric_cast(R(s1x) + r * R(dx1))); set<1>(result.intersections[0], - boost::numeric_cast(s1y + r * promoted_type(dy1))); + boost::numeric_cast(R(s1y) + r * R(dy1))); return result; } diff --git a/include/boost/geometry/policies/relate/tupled.hpp b/include/boost/geometry/policies/relate/tupled.hpp index 853a8a26a..f6713c44c 100644 --- a/include/boost/geometry/policies/relate/tupled.hpp +++ b/include/boost/geometry/policies/relate/tupled.hpp @@ -49,16 +49,18 @@ struct segments_tupled // Get the same type, but at least a double typedef typename select_most_precise::type rtype; + template static inline return_type segments_intersect(side_info const& sides, + R const& r, coordinate_type const& dx1, coordinate_type const& dy1, coordinate_type const& dx2, coordinate_type const& dy2, segment_type1 const& s1, segment_type2 const& s2) { return boost::make_tuple ( - Policy1::segments_intersect(sides, + Policy1::segments_intersect(sides, r, dx1, dy1, dx2, dy2, s1, s2), - Policy2::segments_intersect(sides, + Policy2::segments_intersect(sides, r, dx1, dy1, dx2, dy2, s1, s2) ); } diff --git a/include/boost/geometry/strategies/cartesian/cart_intersect.hpp b/include/boost/geometry/strategies/cartesian/cart_intersect.hpp index 2bf7b7767..ed0f50087 100644 --- a/include/boost/geometry/strategies/cartesian/cart_intersect.hpp +++ b/include/boost/geometry/strategies/cartesian/cart_intersect.hpp @@ -41,21 +41,17 @@ namespace strategy { namespace intersection namespace detail { -template -struct segment_arrange +template +static inline void segment_arrange(Segment const& s, T& s_1, T& s_2, bool& swapped) { - template - static inline void apply(Segment const& s, T& s_1, T& s_2, bool& swapped) + s_1 = get<0, Dimension>(s); + s_2 = get<1, Dimension>(s); + if (s_1 > s_2) { - s_1 = get<0, Dimension>(s); - s_2 = get<1, Dimension>(s); - if (s_1 > s_2) - { - std::swap(s_1, s_2); - swapped = true; - } + std::swap(s_1, s_2); + swapped = true; } -}; +} template inline typename geometry::point_type::type get_from_index( @@ -121,34 +117,20 @@ struct relate_cartesian_segments coordinate_type const& dx_a, coordinate_type const& dy_a, coordinate_type const& dx_b, coordinate_type const& dy_b) { - // 1) Handle "disjoint", common case. - // per dimension, 2 cases: a_1----------a_2 b_1-------b_2 or B left of A - coordinate_type ax_1, ax_2, bx_1, bx_2; - bool ax_swapped = false, bx_swapped = false; - detail::segment_arrange::apply(a, ax_1, ax_2, ax_swapped); - detail::segment_arrange::apply(b, bx_1, bx_2, bx_swapped); - if (ax_2 < bx_1 || ax_1 > bx_2) - { - return Policy::disjoint(); - } - - // 1b) In Y-dimension - coordinate_type ay_1, ay_2, by_1, by_2; - bool ay_swapped = false, by_swapped = false; - detail::segment_arrange::apply(a, ay_1, ay_2, ay_swapped); - detail::segment_arrange::apply(b, by_1, by_2, by_swapped); - if (ay_2 < by_1 || ay_1 > by_2) - { - return Policy::disjoint(); - } - typedef side::side_by_triangle side; side_info sides; - // 2) Calculate sides - // Note: Do NOT yet calculate the determinant here, but use the SIDE strategy. - // Determinant calculation is not robust; side (orient) can be made robust - // (and is much robuster even without measures) + bool collinear_use_first = math::abs(dx_a) + math::abs(dx_b) >= math::abs(dy_a) + math::abs(dy_b); + + sides.set<0> + ( + side::apply(detail::get_from_index<0>(b) + , detail::get_from_index<1>(b) + , detail::get_from_index<0>(a)), + side::apply(detail::get_from_index<0>(b) + , detail::get_from_index<1>(b) + , detail::get_from_index<1>(a)) + ); sides.set<1> ( side::apply(detail::get_from_index<0>(a) @@ -159,26 +141,18 @@ struct relate_cartesian_segments , detail::get_from_index<1>(b)) ); - if (sides.same<1>()) + bool collinear = sides.collinear(); + + robustness_verify_collinear(a, b, sides, collinear); + robustness_verify_meeting(a, b, sides, collinear, collinear_use_first); + + if (sides.same<0>() || sides.same<1>()) { // Both points are at same side of other segment, we can leave - return Policy::disjoint(); - } - - // 2b) For other segment - sides.set<0> - ( - side::apply(detail::get_from_index<0>(b) - , detail::get_from_index<1>(b) - , detail::get_from_index<0>(a)), - side::apply(detail::get_from_index<0>(b) - , detail::get_from_index<1>(b) - , detail::get_from_index<1>(a)) - ); - - if (sides.same<0>()) - { - return Policy::disjoint(); + if (robustness_verify_same_side(a, b, sides)) + { + return Policy::disjoint(); + } } // Degenerate cases: segments of single point, lying on other segment, non disjoint @@ -192,82 +166,343 @@ struct relate_cartesian_segments return Policy::degenerate(b, false); } - bool collinear = sides.collinear(); - - // Get the same type, but at least a double (also used for divisions) typedef typename select_most_precise < coordinate_type, double >::type promoted_type; - // Calculate the determinant/2D cross product - // (Note, because we only check on zero, - // the order a/b does not care) - promoted_type const d = geometry::detail::determinant - < - promoted_type - >(dx_a, dy_a, dx_b, dy_b); + // r: ratio 0-1 where intersection divides A/B + // (only calculated for non-collinear segments) + promoted_type r; + if (! collinear) + { + // Calculate determinants - Cramers rule + coordinate_type const wx = get<0, 0>(a) - get<0, 0>(b); + coordinate_type const wy = get<0, 1>(a) - get<0, 1>(b); + coordinate_type const d = geometry::detail::determinant(dx_a, dy_a, dx_b, dy_b); + coordinate_type const da = geometry::detail::determinant(dx_b, dy_b, wx, wy); - // Determinant d should be nonzero. - // If it is zero, we have an robustness issue here, - // (and besides that we cannot divide by it) - promoted_type const pt_zero = promoted_type(); - if(math::equals(d, pt_zero) && ! collinear) - { -#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION - std::cout << "Determinant zero? Type : " - << typeid(coordinate_type).name() - << std::endl; + coordinate_type const zero = coordinate_type(); + if (math::equals(d, zero)) + { + // This is still a collinear case (because of FP imprecision this can occur here) + // sides.debug(); + sides.set<0>(0,0); + sides.set<1>(0,0); + collinear = true; + } + else + { + r = da / d; - std::cout << " dx_a : " << dx_a << std::endl; - std::cout << " dy_a : " << dy_a << std::endl; - std::cout << " dx_b : " << dx_b << std::endl; - std::cout << " dy_b : " << dy_b << std::endl; + if (! robustness_verify_r(a, b, r)) + { + return Policy::disjoint(); + } - std::cout << " side a <-> b.first : " << sides.get<0,0>() << std::endl; - std::cout << " side a <-> b.second: " << sides.get<0,1>() << std::endl; - std::cout << " side b <-> a.first : " << sides.get<1,0>() << std::endl; - std::cout << " side b <-> a.second: " << sides.get<1,1>() << std::endl; -#endif + robustness_handle_meeting(a, b, sides, dx_a, dy_a, wx, wy, d, r); - if (sides.as_collinear()) - { - sides.set<0>(0,0); - sides.set<1>(0,0); - collinear = true; - } - else - { - return Policy::error("Determinant zero!"); - } - } + if (robustness_verify_disjoint_at_one_collinear(a, b, sides)) + { + return Policy::disjoint(); + } + + } + } if(collinear) { - // Segments are collinear. We'll find out how. - if (math::equals(dx_b, zero)) + if (collinear_use_first) { - // Vertical -> Check y-direction - return relate_collinear(a, b, - ay_1, ay_2, by_1, by_2, - ay_swapped, by_swapped); + return relate_collinear<0>(a, b); } else { - // Check x-direction - return relate_collinear(a, b, - ax_1, ax_2, bx_1, bx_2, - ax_swapped, bx_swapped); + // Y direction contains larger segments (maybe dx is zero) + return relate_collinear<1>(a, b); } } - return Policy::segments_intersect(sides, + return Policy::segments_intersect(sides, r, dx_a, dy_a, dx_b, dy_b, a, b); } private : + + // Ratio should lie between 0 and 1 + // Also these three conditions might be of FP imprecision, the segments were actually (nearly) collinear + template + static inline bool robustness_verify_r( + segment_type1 const& a, segment_type2 const& b, + T& r) + { + T const zero = 0; + T const one = 1; + if (r < zero || r > one) + { + if (verify_disjoint<0>(a, b) || verify_disjoint<1>(a, b)) + { + // Can still be disjoint (even if not one is left or right from another) + // This is e.g. in case #snake4 of buffer test. + return false; + } + + //std::cout << "ROBUSTNESS: correction of r " << r << std::endl; + // sides.debug(); + + // ROBUSTNESS: the r value can in epsilon-cases much larger than 1, while (with perfect arithmetic) + // it should be one. It can be 1.14 or even 1.98049 or 2 (while still intersecting) + + // If segments are crossing (we can see that with the sides) + // and one is inside the other, there must be an intersection point. + // We correct for that. + // This is (only) in case #ggl_list_20110820_christophe in unit tests + + // If segments are touching (two sides zero), of course they should intersect + // This is (only) in case #buffer_rt_i in the unit tests) + + // If one touches in the middle, they also should intersect (#buffer_rt_j) + + // Note that even for ttmath r is occasionally > 1, e.g. 1.0000000000000000000000036191231203575 + + if (r > one) + { + r = one; + } + else if (r < zero) + { + r = zero; + } + } + return true; + } + + static inline void robustness_verify_collinear( + segment_type1 const& a, segment_type2 const& b, + side_info& sides, + bool& collinear) + { + if ((sides.zero<0>() && ! sides.zero<1>()) || (sides.zero<1>() && ! sides.zero<0>())) + { + // If one of the segments is collinear, the other must be as well. + // So handle it as collinear. + // (In float/double epsilon margins it can easily occur that one or two of them are -1/1) + // sides.debug(); + sides.set<0>(0,0); + sides.set<1>(0,0); + collinear = true; + } + } + + static inline void robustness_verify_meeting( + segment_type1 const& a, segment_type2 const& b, + side_info& sides, + bool& collinear, bool& collinear_use_first) + { + if (sides.meeting()) + { + // If two segments meet each other at their segment-points, two sides are zero, + // the other two are not (unless collinear but we don't mean those here). + // However, in near-epsilon ranges it can happen that two sides are zero + // but they do not meet at their segment-points. + // In that case they are nearly collinear and handled as such. + if (! point_equals + ( + select(sides.zero_index<0>(), a), + select(sides.zero_index<1>(), b) + ) + ) + { + sides.set<0>(0,0); + sides.set<1>(0,0); + collinear = true; + + if (collinear_use_first && analyse_equal<0>(a, b)) + { + collinear_use_first = false; + } + else if (! collinear_use_first && analyse_equal<1>(a, b)) + { + collinear_use_first = true; + } + + } + } + } + + // Verifies and if necessary correct missed touch because of robustness + // This is the case at multi_polygon_buffer unittest #rt_m + static inline bool robustness_verify_same_side( + segment_type1 const& a, segment_type2 const& b, + side_info& sides) + { + int corrected = 0; + if (sides.one_touching<0>()) + { + if (point_equals( + select(sides.zero_index<0>(), a), + select(0, b) + )) + { + sides.correct_to_zero<1, 0>(); + corrected = 1; + } + if (point_equals + ( + select(sides.zero_index<0>(), a), + select(1, b) + )) + { + sides.correct_to_zero<1, 1>(); + corrected = 2; + } + } + else if (sides.one_touching<1>()) + { + if (point_equals( + select(sides.zero_index<1>(), b), + select(0, a) + )) + { + sides.correct_to_zero<0, 0>(); + corrected = 3; + } + if (point_equals + ( + select(sides.zero_index<1>(), b), + select(1, a) + )) + { + sides.correct_to_zero<0, 1>(); + corrected = 4; + } + } + + return corrected == 0; + } + + static inline bool robustness_verify_disjoint_at_one_collinear( + segment_type1 const& a, segment_type2 const& b, + side_info const& sides) + { + if (sides.one_of_all_zero()) + { + if (verify_disjoint<0>(a, b) || verify_disjoint<1>(a, b)) + { + return true; + } + } + return false; + } + + + // If r is one, or zero, segments should meet and their endpoints. + // Robustness issue: check if this is really the case. + // It turns out to be no problem, see buffer test #rt_s1 (and there are many cases generated) + // It generates an "ends in the middle" situation which is correct. + template + static inline void robustness_handle_meeting(segment_type1 const& a, segment_type2 const& b, + side_info& sides, + T const& dx_a, T const& dy_a, T const& wx, T const& wy, + T const& d, R const& r) + { + return; + + T const db = geometry::detail::determinant(dx_a, dy_a, wx, wy); + + R const zero = 0; + R const one = 1; + if (math::equals(r, zero) || math::equals(r, one)) + { + R rb = db / d; + if (rb <= 0 || rb >= 1 || math::equals(rb, 0) || math::equals(rb, 1)) + { + if (sides.one_zero<0>() && ! sides.one_zero<1>()) // or vice versa + { +#if defined(BOOST_GEOMETRY_COUNT_INTERSECTION_EQUAL) + extern int g_count_intersection_equal; + g_count_intersection_equal++; +#endif + sides.debug(); + std::cout << "E r=" << r << " r.b=" << rb << " "; + } + } + } + } + + template + static inline bool verify_disjoint(segment_type1 const& a, + segment_type2 const& b) + { + coordinate_type a_1, a_2, b_1, b_2; + bool a_swapped = false, b_swapped = false; + detail::segment_arrange(a, a_1, a_2, a_swapped); + detail::segment_arrange(b, b_1, b_2, b_swapped); + return math::smaller(a_2, b_1) || math::larger(a_1, b_2); + } + + template + static inline typename point_type::type select(int index, Segment const& segment) + { + return index == 0 + ? detail::get_from_index<0>(segment) + : detail::get_from_index<1>(segment) + ; + } + + // We cannot use geometry::equals here. Besides that this will be changed + // to compare segment-coordinate-values directly (not necessary to retrieve point first) + template + static inline bool point_equals(Point1 const& point1, Point2 const& point2) + { + return math::equals(get<0>(point1), get<0>(point2)) + && math::equals(get<1>(point1), get<1>(point2)) + ; + } + + // We cannot use geometry::equals here. Besides that this will be changed + // to compare segment-coordinate-values directly (not necessary to retrieve point first) + template + static inline bool point_equality(Point1 const& point1, Point2 const& point2, + bool& equals_0, bool& equals_1) + { + equals_0 = math::equals(get<0>(point1), get<0>(point2)); + equals_1 = math::equals(get<1>(point1), get<1>(point2)); + return equals_0 && equals_1; + } + + template + static inline bool analyse_equal(segment_type1 const& a, segment_type2 const& b) + { + coordinate_type const a_1 = geometry::get<0, Dimension>(a); + coordinate_type const a_2 = geometry::get<1, Dimension>(a); + coordinate_type const b_1 = geometry::get<0, Dimension>(b); + coordinate_type const b_2 = geometry::get<1, Dimension>(b); + return math::equals(a_1, b_1) + || math::equals(a_2, b_1) + || math::equals(a_1, b_2) + || math::equals(a_2, b_2) + ; + } + + template + static inline return_type relate_collinear(segment_type1 const& a, + segment_type2 const& b) + { + coordinate_type a_1, a_2, b_1, b_2; + bool a_swapped = false, b_swapped = false; + detail::segment_arrange(a, a_1, a_2, a_swapped); + detail::segment_arrange(b, b_1, b_2, b_swapped); + if (math::smaller(a_2, b_1) || math::larger(a_1, b_2)) + //if (a_2 < b_1 || a_1 > b_2) + { + return Policy::disjoint(); + } + return relate_collinear(a, b, a_1, a_2, b_1, b_2, a_swapped, b_swapped); + } + /// Relate segments known collinear static inline return_type relate_collinear(segment_type1 const& a , segment_type2 const& b @@ -296,25 +531,28 @@ private : // Handle "equal", in polygon neighbourhood comparisons a common case - // Check if segments are equal... - bool const a1_eq_b1 = math::equals(get<0, 0>(a), get<0, 0>(b)) - && math::equals(get<0, 1>(a), get<0, 1>(b)); - bool const a2_eq_b2 = math::equals(get<1, 0>(a), get<1, 0>(b)) - && math::equals(get<1, 1>(a), get<1, 1>(b)); - if (a1_eq_b1 && a2_eq_b2) + bool const opposite = a_swapped ^ b_swapped; + bool const both_swapped = a_swapped && b_swapped; + + // Check if segments are equal or opposite equal... + bool const swapped_a1_eq_b1 = math::equals(a_1, b_1); + bool const swapped_a2_eq_b2 = math::equals(a_2, b_2); + + if (swapped_a1_eq_b1 && swapped_a2_eq_b2) { - return Policy::segment_equal(a, false); + return Policy::segment_equal(a, opposite); } - // ... or opposite equal - bool const a1_eq_b2 = math::equals(get<0, 0>(a), get<1, 0>(b)) - && math::equals(get<0, 1>(a), get<1, 1>(b)); - bool const a2_eq_b1 = math::equals(get<1, 0>(a), get<0, 0>(b)) - && math::equals(get<1, 1>(a), get<0, 1>(b)); - if (a1_eq_b2 && a2_eq_b1) - { - return Policy::segment_equal(a, true); - } + bool const swapped_a2_eq_b1 = math::equals(a_2, b_1); + bool const swapped_a1_eq_b2 = math::equals(a_1, b_2); + + bool const a1_eq_b1 = both_swapped ? swapped_a2_eq_b2 : a_swapped ? swapped_a2_eq_b1 : b_swapped ? swapped_a1_eq_b2 : swapped_a1_eq_b1; + bool const a2_eq_b2 = both_swapped ? swapped_a1_eq_b1 : a_swapped ? swapped_a1_eq_b2 : b_swapped ? swapped_a2_eq_b1 : swapped_a2_eq_b2; + + bool const a1_eq_b2 = both_swapped ? swapped_a2_eq_b1 : a_swapped ? swapped_a2_eq_b2 : b_swapped ? swapped_a1_eq_b1 : swapped_a1_eq_b2; + bool const a2_eq_b1 = both_swapped ? swapped_a1_eq_b2 : a_swapped ? swapped_a1_eq_b1 : b_swapped ? swapped_a2_eq_b2 : swapped_a2_eq_b1; + + // The rest below will return one or two intersections. @@ -322,7 +560,7 @@ private : // For IM it is important to know which relates to which. So this information is given, // without performance penalties to intersection calculation - bool const has_common_points = a1_eq_b1 || a1_eq_b2 || a2_eq_b1 || a2_eq_b2; + bool const has_common_points = swapped_a1_eq_b1 || swapped_a1_eq_b2 || swapped_a2_eq_b1 || swapped_a2_eq_b2; // "Touch" -> one intersection point -> one but not two common points @@ -342,7 +580,7 @@ private : // #4: a2<----a1 b1--->b2 (no arrival at all) // Where the arranged forms have two forms: // a_1-----a_2/b_1-------b_2 or reverse (B left of A) - if (has_common_points && (math::equals(a_2, b_1) || math::equals(b_2, a_1))) + if ((swapped_a2_eq_b1 || swapped_a1_eq_b2) && ! swapped_a1_eq_b1 && ! swapped_a2_eq_b2) { if (a2_eq_b1) return Policy::collinear_touch(get<1, 0>(a), get<1, 1>(a), 0, -1); if (a1_eq_b2) return Policy::collinear_touch(get<0, 0>(a), get<0, 1>(a), -1, 0); @@ -402,7 +640,6 @@ private : if (a1_eq_b1) return Policy::collinear_interior_boundary_intersect(a_in_b ? a : b, a_in_b, arrival_a, -arrival_a, false); } - bool const opposite = a_swapped ^ b_swapped; // "Inside", a completely within b or b completely within a @@ -464,7 +701,6 @@ private : the picture might seem wrong but it (supposed to be) is right. */ - bool const both_swapped = a_swapped && b_swapped; if (b_1 < a_2 && a_2 < b_2) { // Left column, from bottom to top @@ -486,7 +722,7 @@ private : ; } // Nothing should goes through. If any we have made an error - // Robustness: it can occur here... + // std::cout << "Robustness issue, non-logical behaviour" << std::endl; return Policy::error("Robustness issue, non-logical behaviour"); } }; diff --git a/include/boost/geometry/strategies/cartesian/distance_projected_point.hpp b/include/boost/geometry/strategies/cartesian/distance_projected_point.hpp index e704a3310..13d416844 100644 --- a/include/boost/geometry/strategies/cartesian/distance_projected_point.hpp +++ b/include/boost/geometry/strategies/cartesian/distance_projected_point.hpp @@ -75,23 +75,27 @@ template class projected_point { public : - typedef typename strategy::distance::services::return_type::type calculation_type; - -private : - // The three typedefs below are necessary to calculate distances // from segments defined in integer coordinates. // Integer coordinates can still result in FP distances. // There is a division, which must be represented in FP. // So promote. - typedef typename promote_floating_point::type fp_type; + typedef typename promote_floating_point + < + typename strategy::distance::services::return_type + < + Strategy + >::type + >::type calculation_type; + +private : // A projected point of points in Integer coordinates must be able to be // represented in FP. typedef model::point < - fp_type, + calculation_type, dimension::value, typename coordinate_system::type > fp_point_type; @@ -139,19 +143,19 @@ public : boost::ignore_unused_variable_warning(strategy); calculation_type const zero = calculation_type(); - fp_type const c1 = dot_product(w, v); + calculation_type const c1 = dot_product(w, v); if (c1 <= zero) { return strategy.apply(p, p1); } - fp_type const c2 = dot_product(v, v); + calculation_type const c2 = dot_product(v, v); if (c2 <= c1) { return strategy.apply(p, p2); } // See above, c1 > 0 AND c2 > c1 so: c2 != 0 - fp_type const b = c1 / c2; + calculation_type const b = c1 / c2; fp_strategy_type fp_strategy = strategy::distance::services::get_similar diff --git a/include/boost/geometry/strategies/side_info.hpp b/include/boost/geometry/strategies/side_info.hpp index 3fc43b8d9..f3a9da0df 100644 --- a/include/boost/geometry/strategies/side_info.hpp +++ b/include/boost/geometry/strategies/side_info.hpp @@ -44,6 +44,19 @@ public : sides[Which].second = second; } + template + inline void correct_to_zero() + { + if (Index == 0) + { + sides[Which].first = 0; + } + else + { + sides[Which].second = 0; + } + } + template inline int get() const { @@ -67,21 +80,81 @@ public : && sides[1].second == 0; } - // If one of the segments is collinear, the other must be as well. - // So handle it as collinear. - // (In floating point margins it can occur that one of them is 1!) - inline bool as_collinear() const + inline bool crossing() const { - return sides[0].first * sides[0].second == 0 - || sides[1].first * sides[1].second == 0; + return sides[0].first * sides[0].second == -1 + && sides[1].first * sides[1].second == -1; } + inline bool touching() const + { + return (sides[0].first * sides[1].first == -1 + && sides[0].second == 0 && sides[1].second == 0) + || (sides[1].first * sides[0].first == -1 + && sides[1].second == 0 && sides[0].second == 0); + } + + template + inline bool one_touching() const + { + // This is normally a situation which can't occur: + // If one is completely left or right, the other cannot touch + return one_zero() + && sides[1 - Which].first * sides[1 - Which].second == 1; + } + + inline bool meeting() const + { + // Two of them (in each segment) zero, two not + return one_zero<0>() && one_zero<1>(); + } + + template + inline bool zero() const + { + return sides[Which].first == 0 && sides[Which].second == 0; + } + + template + inline bool one_zero() const + { + return (sides[Which].first == 0 && sides[Which].second != 0) + || (sides[Which].first != 0 && sides[Which].second == 0); + } + + inline bool one_of_all_zero() const + { + int const sum = std::abs(sides[0].first) + + std::abs(sides[0].second) + + std::abs(sides[1].first) + + std::abs(sides[1].second); + return sum == 3; + } + + + template + inline int zero_index() const + { + return sides[Which].first == 0 ? 0 : 1; + } + + + inline void debug() const + { + std::cout << sides[0].first << " " + << sides[0].second << " " + << sides[1].first << " " + << sides[1].second + << std::endl; + } + + inline void reverse() { std::swap(sides[0], sides[1]); } -private : +//private : std::pair sides[2]; }; diff --git a/include/boost/geometry/strategies/strategy_transform.hpp b/include/boost/geometry/strategies/strategy_transform.hpp index 7a1f06016..61a408c61 100644 --- a/include/boost/geometry/strategies/strategy_transform.hpp +++ b/include/boost/geometry/strategies/strategy_transform.hpp @@ -23,7 +23,9 @@ #include #include #include +#include #include +#include #include #include @@ -251,6 +253,23 @@ namespace detail return false; } + template + inline bool cartesian_to_spherical_equatorial3(T x, T y, T z, P& p) + { + assert_dimension(); + + // http://en.wikipedia.org/wiki/List_of_canonical_coordinate_transformations#From_Cartesian_coordinates + T const r = sqrt(x * x + y * y + z * z); + set<2>(p, r); + set_from_radian<0>(p, atan2(y, x)); + if (r > 0.0) + { + set_from_radian<1>(p, asin(z / r)); + return true; + } + return false; + } + } // namespace detail #endif // DOXYGEN_NO_DETAIL @@ -361,6 +380,16 @@ struct from_cartesian_3_to_spherical_polar_3 } }; +template +struct from_cartesian_3_to_spherical_equatorial_3 +{ + inline bool apply(P1 const& p1, P2& p2) const + { + assert_dimension(); + return detail::cartesian_to_spherical_equatorial3(get<0>(p1), get<1>(p1), get<2>(p1), p2); + } +}; + #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS namespace services @@ -454,6 +483,11 @@ struct default_strategy type; }; +template +struct default_strategy +{ + typedef from_cartesian_3_to_spherical_equatorial_3 type; +}; } // namespace services diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp index edfa961b1..95cbdf2ce 100644 --- a/include/boost/geometry/util/math.hpp +++ b/include/boost/geometry/util/math.hpp @@ -44,11 +44,43 @@ struct equals template struct equals { + static inline Type get_max(Type const& a, Type const& b, Type const& c) + { + return (std::max)((std::max)(a, b), c); + } + static inline bool apply(Type const& a, Type const& b) { + if (a == b) + { + return true; + } + // See http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.17, // FUTURE: replace by some boost tool or boost::test::close_at_tolerance - return std::abs(a - b) <= std::numeric_limits::epsilon() * std::abs(a); + return std::abs(a - b) <= std::numeric_limits::epsilon() * get_max(std::abs(a), std::abs(b), 1.0); + } +}; + +template +struct smaller +{ + static inline bool apply(Type const& a, Type const& b) + { + return a < b; + } +}; + +template +struct smaller +{ + static inline bool apply(Type const& a, Type const& b) + { + if (equals::apply(a, b)) + { + return false; + } + return a < b; } }; @@ -116,6 +148,28 @@ inline bool equals_with_epsilon(T1 const& a, T2 const& b) >::apply(a, b); } +template +inline bool smaller(T1 const& a, T2 const& b) +{ + typedef typename select_most_precise::type select_type; + return detail::smaller + < + select_type, + boost::is_floating_point::type::value + >::apply(a, b); +} + +template +inline bool larger(T1 const& a, T2 const& b) +{ + typedef typename select_most_precise::type select_type; + return detail::smaller + < + select_type, + boost::is_floating_point::type::value + >::apply(b, a); +} + double const d2r = geometry::math::pi() / 180.0; diff --git a/test/algorithms/Jamfile.v2 b/test/algorithms/Jamfile.v2 index e412f0adf..13811d303 100644 --- a/test/algorithms/Jamfile.v2 +++ b/test/algorithms/Jamfile.v2 @@ -35,6 +35,7 @@ test-suite boost-geometry-algorithms [ run perimeter.cpp ] [ run reverse.cpp ] [ run simplify.cpp ] + [ run touches.cpp ] [ run transform.cpp ] [ run union.cpp ] [ run unique.cpp ] diff --git a/test/algorithms/algorithms_tests.sln b/test/algorithms/algorithms_tests.sln index 124ad8af0..96ba06492 100644 --- a/test/algorithms/algorithms_tests.sln +++ b/test/algorithms/algorithms_tests.sln @@ -58,6 +58,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "convert", "convert.vcproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "covered_by", "covered_by.vcproj", "{5ABF0B56-F9F1-4D93-B15A-E3972F45D97B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "touches", "touches.vcproj", "{8359726E-9F03-4300-8F63-1FEAC84251D0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -180,6 +182,10 @@ Global {5ABF0B56-F9F1-4D93-B15A-E3972F45D97B}.Debug|Win32.Build.0 = Debug|Win32 {5ABF0B56-F9F1-4D93-B15A-E3972F45D97B}.Release|Win32.ActiveCfg = Release|Win32 {5ABF0B56-F9F1-4D93-B15A-E3972F45D97B}.Release|Win32.Build.0 = Release|Win32 + {8359726E-9F03-4300-8F63-1FEAC84251D0}.Debug|Win32.ActiveCfg = Debug|Win32 + {8359726E-9F03-4300-8F63-1FEAC84251D0}.Debug|Win32.Build.0 = Debug|Win32 + {8359726E-9F03-4300-8F63-1FEAC84251D0}.Release|Win32.ActiveCfg = Release|Win32 + {8359726E-9F03-4300-8F63-1FEAC84251D0}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/algorithms/difference.cpp b/test/algorithms/difference.cpp index 642cd1e66..1d48fc7b8 100644 --- a/test/algorithms/difference.cpp +++ b/test/algorithms/difference.cpp @@ -7,6 +7,8 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#define TEST_ISOVIST + //#define BOOST_GEOMETRY_CHECK_WITH_POSTGIS //#define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER @@ -258,12 +260,17 @@ void test_all() ***/ #ifdef _MSC_VER - // Isovist (submitted by Brandon during Formal Review) +#ifdef TEST_ISOVIST test_one("isovist", isovist1[0], isovist1[1], - 4, 0, 0.279121891701124, - 4, 0, 224.889211358929, - 0.01); + if_typed_tt(4, 2), 0, 0.279121891701124, + if_typed_tt(4, 3), 0, if_typed_tt(224.889211358929, 223.777), + if_typed_tt(0.001, 0.2)); + + // SQL Server gives: 0.279121891701124 and 224.889211358929 + // PostGIS gives: 0.279121991127244 and 224.889205853156 + +#endif test_one("ggl_list_20110306_javier", ggl_list_20110306_javier[0], ggl_list_20110306_javier[1], @@ -277,11 +284,14 @@ void test_all() 1, 0, 3200.4, 0.01); - test_one("ggl_list_20110716_enrico", - ggl_list_20110716_enrico[0], ggl_list_20110716_enrico[1], - 3, 0, 35723.8506317139, - 1, 0, 58456.4964294434 - ); + if (! boost::is_same::value) + { + test_one("ggl_list_20110716_enrico", + ggl_list_20110716_enrico[0], ggl_list_20110716_enrico[1], + 3, 0, 35723.8506317139, + 1, 0, 58456.4964294434 + ); + } test_one("ggl_list_20110820_christophe", ggl_list_20110820_christophe[0], ggl_list_20110820_christophe[1], @@ -300,7 +310,7 @@ void test_all() // Because we cannot predict this, we only test for MSVC test_one("ggl_list_20110627_phillip", ggl_list_20110627_phillip[0], ggl_list_20110627_phillip[1], - if_typed(0, 1), 0, + if_typed_tt(1, 0), 0, if_typed_tt(0.0000000000001105367, 0.0), 1, 0, 3577.40960816756, 0.01 @@ -465,6 +475,7 @@ int test_main(int, char* []) test_all >(); #ifdef HAVE_TTMATH + std::cout << "Testing TTMATH" << std::endl; test_all >(); //test_difference_parcel_precision(); #endif diff --git a/test/algorithms/disjoint.cpp b/test/algorithms/disjoint.cpp index a5e602568..83738e24f 100644 --- a/test/algorithms/disjoint.cpp +++ b/test/algorithms/disjoint.cpp @@ -113,6 +113,12 @@ void test_all() test_disjoint("s/s 1", "linestring(0 0,1 1)", "linestring(1 0,0 1)", false); test_disjoint("s/s 2", "linestring(0 0,1 1)", "linestring(1 0,2 1)", true); + // Collinear opposite + test_disjoint("ls/ls co", "linestring(0 0,2 2)", "linestring(1 1,0 0)", false); + // Collinear opposite and equal + test_disjoint("ls/ls co-e", "linestring(0 0,1 1)", "linestring(1 1,0 0)", false); + + // Degenerate linestrings { // Submitted by Zachary on the Boost.Geometry Mailing List, on 2012-01-29 diff --git a/test/algorithms/distance.cpp b/test/algorithms/distance.cpp index 3803fe9ad..e7d072976 100644 --- a/test/algorithms/distance.cpp +++ b/test/algorithms/distance.cpp @@ -194,7 +194,11 @@ void test_all() test_geometry("POINT(0 3)", "POINT(4 0)", 5.0); test_geometry >("POINT(1 3)", "LINESTRING(1 1,4 4)", sqrt(2.0)); test_geometry >("POINT(3 1)", "LINESTRING(1 1,4 4)", sqrt(2.0)); + test_geometry >("POINT(50 50)", "LINESTRING(50 40, 40 50)", sqrt(50.0)); + test_geometry >("POINT(50 50)", "LINESTRING(50 40, 40 50, 0 90)", sqrt(50.0)); test_geometry, P>("LINESTRING(1 1,4 4)", "POINT(1 3)", sqrt(2.0)); + test_geometry, P>("LINESTRING(50 40, 40 50)", "POINT(50 50)", sqrt(50.0)); + test_geometry, P>("LINESTRING(50 40, 40 50, 0 90)", "POINT(50 50)", sqrt(50.0)); // Rings test_geometry >("POINT(1 3)", "POLYGON((1 1,4 4,5 0,1 1))", sqrt(2.0)); diff --git a/test/algorithms/intersection.cpp b/test/algorithms/intersection.cpp index 680b1f31a..05e0a46a1 100644 --- a/test/algorithms/intersection.cpp +++ b/test/algorithms/intersection.cpp @@ -15,6 +15,9 @@ #include #include +#define BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE +#define TEST_ISOVIST + //#define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER //#define BOOST_GEOMETRY_DEBUG_INTERSECTION //#define BOOST_GEOMETRY_DEBUG_TRAVERSE @@ -164,14 +167,17 @@ void test_areal() typedef typename bg::coordinate_type::type ct; +#ifdef TEST_ISOVIST #ifdef _MSC_VER - // Isovist (submitted by Brandon during Formal Review) test_one("isovist", isovist1[0], isovist1[1], - 1, - if_typed(19, if_typed(20, 20)), - 88.19203, - if_typed(0.5, if_typed(0.1, 0.01))); + 1, 20, 88.19203, + if_typed_tt(0.01, 0.1)); + + // SQL Server gives: 88.1920416352664 + // PostGIS gives: 88.19203677911 + +#endif #endif //std::cout << typeid(ct).name() << std::endl; @@ -191,13 +197,22 @@ void test_areal() 1, if_typed_tt(6, 5), 11151.6618); #ifdef _MSC_VER // gcc/linux behaves differently - test_one("ggl_list_20110716_enrico", - ggl_list_20110716_enrico[0], ggl_list_20110716_enrico[1], - 3, - if_typed(19, if_typed(22, 21)), - 35723.8506317139); + if (! boost::is_same::value) + { + test_one("ggl_list_20110716_enrico", + ggl_list_20110716_enrico[0], ggl_list_20110716_enrico[1], + 3, + if_typed(19, if_typed(22, 21)), + 35723.8506317139); + } #endif + test_one("buffer_rt_f", buffer_rt_f[0], buffer_rt_f[1], + 1, 4, 0.00029437899183903937, 0.01); + + test_one("buffer_rt_g", buffer_rt_g[0], buffer_rt_g[1], + 1, 0, 2.914213562373); + return; @@ -496,6 +511,7 @@ int test_main(int, char* []) test_all >(); #if defined(HAVE_TTMATH) + std::cout << "Testing TTMATH" << std::endl; test_all >(); #endif diff --git a/test/algorithms/overlay/get_turn_info.cpp b/test/algorithms/overlay/get_turn_info.cpp index 0eb12c605..476e719e4 100644 --- a/test/algorithms/overlay/get_turn_info.cpp +++ b/test/algorithms/overlay/get_turn_info.cpp @@ -807,14 +807,12 @@ void test_all() 4, 3, 2, 3, 0, 3, // q method_touch, 2, 3, "xx"); - /*** - TODO, work this out further / check this - // Case where - test_both("issue_buffer_mill", - 5.1983614873206241 , 6.7259025813913107 , 5.0499999999999998 , 6.4291796067500622 , 5.1983614873206241 , 6.7259025813913107, // p - 5.0499999999999998 , 6.4291796067500622 , 5.0499999999999998 , 6.4291796067500622 , 5.1983614873206241 , 6.7259025813913107, // q - method_collinear, 2, 0, "tt"); - ***/ + // BSG 2012-05-26 to be decided what's the problem here and what it tests... + // Anyway, test results are not filled out. + //test_both("issue_buffer_mill", + // 5.1983614873206241 , 6.7259025813913107 , 5.0499999999999998 , 6.4291796067500622 , 5.1983614873206241 , 6.7259025813913107, // p + // 5.0499999999999998 , 6.4291796067500622 , 5.0499999999999998 , 6.4291796067500622 , 5.1983614873206241 , 6.7259025813913107, // q + // method_collinear, 2, 0, "tt"); } diff --git a/test/algorithms/overlay/get_turns.cpp b/test/algorithms/overlay/get_turns.cpp index 12c4dd19c..07c401c3b 100644 --- a/test/algorithms/overlay/get_turns.cpp +++ b/test/algorithms/overlay/get_turns.cpp @@ -54,7 +54,7 @@ struct test_get_turns { template static void apply(std::string const& id, - int expected_count, + std::size_t expected_count, G1 const& g1, G2 const& g2, double precision) { typedef bg::detail::overlay::turn_info @@ -133,7 +133,7 @@ struct test_get_turns template struct test_get_turns { - inline static void apply(std::string const& id, int expected_count, + inline static void apply(std::string const& id, std::size_t expected_count, std::string const& wkt1, std::string const& wkt2, double precision = 0.001) { diff --git a/test/algorithms/overlay/overlay_cases.hpp b/test/algorithms/overlay/overlay_cases.hpp index dfe7c24bd..a4326b07b 100644 --- a/test/algorithms/overlay/overlay_cases.hpp +++ b/test/algorithms/overlay/overlay_cases.hpp @@ -480,6 +480,7 @@ static std::string snl_1[2] = { "POLYGON((184861.118 606901.158,184893.787 606898.483,184925.043 606913.4,184927.174 606951.759,184912.9 606987.146,184877.87 606989.232,184885.103 607023.774,184899.058 607022.743,184906.008 607044.948,184966.465 607025.02,184968.442 606961.3,185024.768 606947.402,185024.544 606941.355,185027.007 606937.323,185030.366 606934.187,185035.516 606933.963,185040.442 606935.531,185042.905 606939.115,185088.364 606931.385,185089.139 607015.509,185095.2 607011.3,185118.827 606995.545,185126.813 606991.995,185177.727 606973.799,185181.482 606966.676,185193.571 606977.795,185193.711 606960.3,185189.352 606779.02,185167.515 606783.844,185086.96 606801.241,185011.707 606817.809,185000 606819.304,184994.034 606819.794,184976.398 606819.572,184956.654 606817.131,184934.913 606813.137,184893.097 606804.927,184884.445 606831.555,184866.919 606883.481,184861.118 606901.158),(184907.556 607013.301,184905.782 607009.972,184906.004 607005.978,184908.444 606998.877,184912.215 606994.218,184919.314 606993.996,184922.42 606995.771,184925.747 606998.877,184926.413 607002.872,184925.747 607007.753,184922.42 607012.191,184917.096 607015.298,184911.771 607015.298,184907.556 607013.301))"}; +// Isovist (submitted by Brandon during Formal Review) static std::string isovist[2] = { "POLYGON((37.29449462890625 1.7902572154998779, 46.296027072709599 -2.4984308554828116, 45.389434814453125 -4.5143837928771973, 47.585065917176543 -6.1314922196594779, 46.523914387974358 -8.5152102535033496, 42.699958801269531 -4.4278755187988281, 42.577877044677734 -4.4900407791137695, 42.577911376953125 -4.4901103973388672, 40.758884429931641 -5.418975830078125, 40.6978759765625 -5.4500408172607422, 41.590042114257813 -7.2021245956420898, 57.297810222148939 -37.546793343968417, 50.974888957147442 -30.277285722290763, 37.140213012695313 1.3446992635726929, 37.000419616699219 1.664225697517395, 37.29449462890625 1.7902572154998779))", @@ -559,5 +560,99 @@ static std::string ticket_5103[2] = "POLYGON((-95269304 222758,-95260668 419862,-95234760 615696,-95192088 808228,-95132906 996442,-95057214 1178814,-94966028 1354074,-94860110 1520444,-94739968 1676908,-94606618 1822450,-94999048 2214880,-95165164 2033778,-95314770 1838706,-95446850 1631442,-95560388 1413510,-95654368 1186434,-95728282 951992,-95781368 711962,-95813626 468376,-95824294 222758,-95269304 222758))" }; +static std::string ggl_list_20120229_volker[3] = + { + "POLYGON((1716 1554,2076 2250,2436 2352,2796 1248,3156 2484,3516 2688,3516 2688,3156 2484,2796 1248,2436 2352,2076 2250, 1716 1554))", + "POLYGON((2500 1600,2500 2300,3200 2300,3200 1600,2500 1600))", + "POLYGON((1716 1554,2076 2250,2436 2352,2796 1248,3156 2484,3516 2688,3156 2483,2796 1247,2436 2351,2076 2249, 1716 1554))", + }; + +static std::string buffer_rt_a[2] = + { + "POLYGON((1 7,1 8,1.0012 8.04907,1.00482 8.09802,1.01082 8.14673,1.01921 8.19509,1.02997 8.24298,1.04306 8.29028,1.05846 8.33689,1.07612 8.38268,1.09601 8.42756,1.11808 8.4714,1.14227 8.5141,1.16853 8.55557,1.19679 8.5957,1.22699 8.63439,1.25905 8.67156,1.29289 8.70711,1.32844 8.74095,1.36561 8.77301,1.4043 8.80321,1.44443 8.83147,1.4859 8.85773,1.5286 8.88192,1.57244 8.90399,1.61732 8.92388,1.66311 8.94154,1.70972 8.95694,1.75702 8.97003,1.80491 8.98079,1.85327 8.98918,1.90198 8.99518,1.95093 8.9988,2 9,3 9,3.04907 8.9988,3.09802 8.99518,3.14673 8.98918,3.19509 8.98079,3.24298 8.97003,3.29028 8.95694,3.33689 8.94154,3.38268 8.92388,3.42756 8.90399,3.4714 8.88192,3.5141 8.85773,3.55557 8.83147,3.5957 8.80321,3.63439 8.77301,3.67156 8.74095,3.70711 8.70711,3.74095 8.67156,3.77301 8.63439,3.80321 8.5957,3.83147 8.55557,3.85773 8.5141,3.88192 8.4714,3.90399 8.42756,3.92388 8.38268,3.94154 8.33689,3.95694 8.29028,3.97003 8.24298,3.98079 8.19509,3.98918 8.14673,3.99518 8.09802,3.9988 8.04907,4 8,4 7,3.9988 6.95093,3.99518 6.90198,3.98918 6.85327,3.98079 6.80491,3.97003 6.75702,3.95694 6.70972,3.94154 6.66311,3.92388 6.61732,3.90399 6.57244,3.88192 6.5286,3.85773 6.4859,3.83147 6.44443,3.80321 6.4043,3.77301 6.36561,3.74095 6.32844,3.70711 6.29289,3.67156 6.25905,3.63439 6.22699,3.5957 6.19679,3.55557 6.16853,3.5141 6.14227,3.4714 6.11808,3.42756 6.09601,3.38268 6.07612,3.33689 6.05846,3.29028 6.04306,3.24298 6.02997,3.19509 6.01921,3.14673 6.01082,3.09802 6.00482,3.04907 6.0012,3 6,2 6,1.95093 6.0012,1.90198 6.00482,1.85327 6.01082,1.80491 6.01921,1.75702 6.02997,1.70972 6.04306,1.66311 6.05846,1.61732 6.07612,1.57244 6.09601,1.5286 6.11808,1.4859 6.14227,1.44443 6.16853,1.4043 6.19679,1.36561 6.22699,1.32844 6.25905,1.29289 6.29289,1.25905 6.32844,1.22699 6.36561,1.19679 6.4043,1.16853 6.44443,1.14227 6.4859,1.11808 6.5286,1.09601 6.57244,1.07612 6.61732,1.05846 6.66311,1.04306 6.70972,1.02997 6.75702,1.01921 6.80491,1.01082 6.85327,1.00482 6.90198,1.0012 6.95093,1 7))", + "POLYGON((3 6,4 6,4.04907 5.9988,4.09802 5.99518,4.14673 5.98918,4.19509 5.98079,4.24298 5.97003,4.29028 5.95694,4.33689 5.94154,4.38268 5.92388,4.42756 5.90399,4.4714 5.88192,4.5141 5.85773,4.55557 5.83147,4.5957 5.80321,4.63439 5.77301,4.67156 5.74095,4.70711 5.70711,4.74095 5.67156,4.77301 5.63439,4.80321 5.5957,4.83147 5.55557,4.85773 5.5141,4.88192 5.4714,4.90399 5.42756,4.92388 5.38268,4.94154 5.33689,4.95694 5.29028,4.97003 5.24298,4.98079 5.19509,4.98918 5.14673,4.99518 5.09802,4.9988 5.04907,5 5,5 4,4.9988 3.95093,4.99518 3.90198,4.98918 3.85327,4.98079 3.80491,4.97003 3.75702,4.95694 3.70972,4.94154 3.66311,4.92388 3.61732,4.90399 3.57244,4.88192 3.5286,4.85773 3.4859,4.83147 3.44443,4.80321 3.4043,4.77301 3.36561,4.74095 3.32844,4.70711 3.29289,4.67156 3.25905,4.63439 3.22699,4.5957 3.19679,4.55557 3.16853,4.5141 3.14227,4.4714 3.11808,4.42756 3.09601,4.38268 3.07612,4.33689 3.05846,4.29028 3.04306,4.24298 3.02997,4.19509 3.01921,4.14673 3.01082,4.09802 3.00482,4.04907 3.0012,4 3,3 3,3 3,2 3,1.95093 3.0012,1.90198 3.00482,1.85327 3.01082,1.80491 3.01921,1.75702 3.02997,1.70972 3.04306,1.66311 3.05846,1.61732 3.07612,1.57244 3.09601,1.5286 3.11808,1.4859 3.14227,1.44443 3.16853,1.4043 3.19679,1.36561 3.22699,1.32844 3.25905,1.29289 3.29289,1.25905 3.32844,1.22699 3.36561,1.19679 3.4043,1.16853 3.44443,1.14227 3.4859,1.11808 3.5286,1.09601 3.57244,1.07612 3.61732,1.05846 3.66311,1.04306 3.70972,1.02997 3.75702,1.01921 3.80491,1.01082 3.85327,1.00482 3.90198,1.0012 3.95093,1 4,1 5,1.0012 5.04907,1.00482 5.09802,1.01082 5.14673,1.01921 5.19509,1.02997 5.24298,1.04306 5.29028,1.05846 5.33689,1.07612 5.38268,1.09601 5.42756,1.11808 5.4714,1.14227 5.5141,1.16853 5.55557,1.19679 5.5957,1.22699 5.63439,1.25905 5.67156,1.29289 5.70711,1.32844 5.74095,1.36561 5.77301,1.4043 5.80321,1.44443 5.83147,1.4859 5.85773,1.5286 5.88192,1.57244 5.90399,1.61732 5.92388,1.66311 5.94154,1.70972 5.95694,1.75702 5.97003,1.80491 5.98079,1.85327 5.98918,1.90198 5.99518,1.95093 5.9988,2 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6,3 6))" + }; + +static std::string buffer_rt_f[2] = + { + "POLYGON((-0.29999999999999999 6.0000000000000000,-0.29999999999999999 7.0000000000000000,-0.30000000000000027 7.7242640687119302,0.21213203435596423 7.2121320343559638,1.2121320343559643 6.2121320343559638,1.7242640687119293 5.7000000000000002,1.0000000000000000 5.7000000000000002,0.00000000000000000 5.7000000000000002,-0.30000000000000027 5.7000000000000002,-0.29999999999999999 6.0000000000000000))", + "POLYGON((1.3000000000000000 9.0000000000000000,1.3000000000000000 8.0000000000000000,1.3000000000000007 7.7000000000000002,1.0000000000000000 7.7000000000000002,0.00000000000000000 7.7000000000000002,-0.29999999999999982 7.7000000000000002,-0.29999999999999999 8.0000000000000000,-0.29999999999999999 9.0000000000000000,-0.29999999999999982 9.3000000000000007,0.00000000000000000 9.3000000000000007,1.0000000000000000 9.3000000000000007,1.3000000000000007 9.3000000000000007,1.3000000000000000 9.0000000000000000))" + }; + +static std::string buffer_rt_g[2] = + { + "POLYGON((2.0 8.0,2.0 9.0,2.0 10.0,3.0 10.0,4.0 10.0,6.4142135623730958 10.0,4.7071067811865479 8.2928932188134521,3.7071067811865475 7.2928932188134521,2.0 5.5857864376269051,2.0 8.0))", + "POLYGON((0.0 6.0,0.0 7.0,0.0 8.0,1.0 8.0,2.0 8.0,4.4142135623730958 8.0,2.7071067811865475 6.2928932188134521,1.7071067811865475 5.2928932188134521,-0.0 3.5857864376269042,0.0 6.0))" + }; + +static std::string buffer_rt_g_boxes[5] = + { + "POLYGON((0 0,0 4,4 4,4 0,0 0))", + "POLYGON((2 2,2 4,6 4,6 2,2 2))", + "POLYGON((2 2,2 4,6 4,8 4,8 2,2 2))", + "POLYGON((2 2,2 4,6 4,6 6,8 6,8 2,2 2))", + + "POLYGON((0 0,0 4,4 4,4 5,5 5,5 1,4 1,4 0,0 0))" + }; + +// From buffer-robustness-test with segment-intersection "touching" with r ~ 1.16 +static std::string buffer_rt_i[2] = + { + "POLYGON((1.0 1.0,1.0 2.0,1.0 3.0,2.0 3.0,3.0 3.0,5.4142135623730949 3.0,3.7071067811865475 1.2928932188134525,2.7071067811865475 0.29289321881345254,1.0 -1.4142135623730949,1.0 1.0))", + "POLYGON((2.0 2.0,2.0 3.0,2.0 4.0,3.0 4.0,4.0 4.0,6.4142135623730958 4.0,4.7071067811865479 2.2928932188134525,3.7071067811865475 1.2928932188134525,2.0 -0.41421356237309387,2.0 2.0))" + }; + +// Idem, but with a touch-in-the-middle +static std::string buffer_rt_j[2] = + { + "POLYGON((1.0 4.0,1.0 5.0,1.0 6.0,2.0 6.0,3.0 6.0,5.4142135623730967 6.0,3.7071067811865475 4.2928932188134521,2.7071067811865475 3.2928932188134525,1.0 1.5857864376269055,1.0 4.0))", + "POLYGON((-1.0 2.0,-1.0 3.0,-1.0 4.0,0.00 4.0,1.0 4.0,3.4142135623730949 4.0,1.7071067811865475 2.2928932188134525,0.70710678118654746 1.2928932188134525,-1.0 -0.41421356237309492,-1.0 2.0))" + }; + +// Nearly-collinear (two segments as-if meeting but not really) +static std::string buffer_rt_l[2] = + { + "POLYGON((1.0 5.0,1.0 6.0,1.0 8.4142135623730958,2.7071067811865475 6.7071067811865479,3.7071067811865475 5.7071067811865479,5.4142135623730958 4.0,3.0 4.0,2.0 4.0,1.0 4.0,1.0 5.0))", + "POLYGON((5.0 1.0,5.0 2.0,5.0 4.4142135623730958,6.7071067811865479 2.7071067811865475,7.7071067811865479 1.7071067811865475,9.4142135623730958 0.00,7.0 0.0,6.0 0.0,5.0 0.0,5.0 1.0))" + }; + +static std::string buffer_rt_m1[2] = + { + "POLYGON((3.0 2.0,3.0 3.0,3.0 5.4142135623730940,4.7071067811865479 3.7071067811865475,5.7071067811865479 2.7071067811865475,7.4142135623730940 1.0,5.0 1.0,4.0 1.0,3.0 1.0,3.0 2.0))", + "POLYGON((0.29289321881345254 2.7071067811865475,1.2928932188134525 3.7071067811865475,3.0 5.4142135623730949,3.0 3.0,3.0 2.0,3.0 1.0,2.0 1.0,1.0 1.0,-1.4142135623730949 1.0,0.29289321881345254 2.7071067811865475))" + }; + +static std::string buffer_rt_m2[2] = + { + "POLYGON((-0.70710678118654746 3.7071067811865475,0.29289321881345254 4.7071067811865479,2.0 6.4142135623730967,2.0 4.0,2.0 3.0,2.0 2.0,1.0 2.0,0.00 2.0,-2.4142135623730949 2.0,-0.70710678118654746 3.7071067811865475))", + "POLYGON((2.2928932188134525 6.7071067811865479,3.2928932188134525 7.7071067811865479,5.0 9.4142135623730958,5.0 7.0,5.0 6.0,5.0 6.0,5.0 5.0,5.0 4.0,4.0 4.0,3.0 4.0,2.0 4.0,2.0 5.0,2.0 6.0,2.0 6.4142135623730958,2.2928932188134525 6.7071067811865479))" + }; + + +static std::string buffer_rt_n[2] = + { + "POLYGON((-0.70710678118654746 0.70710678118654746,0.29289321881345254 1.7071067811865475,2.0 3.4142135623730949,2.0 1.0,2.0 0.0,2.0 -1.0,1.0 -1.0,0.00 -1.0,-2.4142135623730949 -1.0,-0.70710678118654746 0.70710678118654746))", + "POLYGON((2.2928932188134525 3.7071067811865475,3.2928932188134525 4.7071067811865479,5.0 6.4142135623730958,5.0 4.0,5.0 3.0,5.0 2.0,4.0 2.0,3.0 2.0,0.58578643762690574 2.0,2.2928932188134525 3.7071067811865475))" + }; + +static std::string buffer_rt_q[2] = + { + "POLYGON((6.0 6.0,7.0 6.0,9.4142135623730958 6.0,7.7071067811865479 4.2928932188134521,6.7071067811865479 3.2928932188134525,6.4142135623730949 3.0,6.0 3.0,5.0 3.0,4.0 3.0,4.0 4.0,4.0 5.0,4.0 6.0,5.0 6.0,6.0 6.0))", + "POLYGON((3.0 2.0,3.0 3.0,3.0 4.0,4.0 4.0,5.0 4.0,7.4142135623730958 4.0,5.7071067811865479 2.2928932188134525,4.7071067811865479 1.2928932188134525,3.0 -0.41421356237309581,3.0 2.0))" + }; + +static std::string buffer_rt_r[2] = + { + "POLYGON((3.7071067811865475 0.29289321881345254,2.7071067811865475 -0.70710678118654746,2.0 -1.4142135623730949,1.2928932188134525 -0.70710678118654746,0.29289321881345254 0.29289321881345254,-0.41421356237309492 1.0,0.29289321881345254 1.7071067811865475,1.2928932188134525 2.7071067811865475,2.0000000000000004 3.4142135623730954,2.7071067811865475 2.7071067811865475,3.7071067811865475 1.7071067811865475,4.4142135623730958 1.0000000000000004,3.7071067811865475 0.29289321881345254))", + "POLYGON((5.7071067811865479 2.2928932188134525,4.7071067811865479 1.2928932188134525,4.4142135623730958 1.0,4.0 1.0,3.0 1.0,1.3819660112501053 1.0,2.1055728090000843 2.4472135954999579,3.1055728090000843 4.4472135954999583,3.7260485282911020 5.6881650340819956,4.7071067811865479 4.7071067811865479,5.7071067811865479 3.7071067811865475,6.4142135623730958 3.0,5.7071067811865479 2.2928932188134525))" + }; + + +static std::string buffer_rt_t[2] = + { + "POLYGON((0.00000000000000000 3.0,0.00000000000000000 4.0,0.00000000000000000 6.4142135623730958,1.7071067811865475 4.7071067811865479,2.7071067811865475 3.7071067811865475,4.4142135623730940 2.0,2.0 2.0,1.0 2.0,-0.00000000000000000 2.0,0.00000000000000000 3.0))", + "POLYGON((1.7071067811865475 3.2928932188134525,0.70710678118654746 2.2928932188134525,-1.0 0.58578643762690508,-1.0 3.0,-1.0 4.0,-1.0 4.0,-1.0 5.0,-1.0 7.4142135623730958,0.70710678118654746 5.7071067811865479,1.7071067811865475 4.7071067811865479,2.4142135623730954 4.0,1.7071067811865475 3.2928932188134525))" + }; + #endif // BOOST_GEOMETRY_TEST_OVERLAY_CASES_HPP diff --git a/test/algorithms/overlay/robustness/test_overlay_p_q.hpp b/test/algorithms/overlay/robustness/test_overlay_p_q.hpp index 23f5c3a2c..85729b165 100644 --- a/test/algorithms/overlay/robustness/test_overlay_p_q.hpp +++ b/test/algorithms/overlay/robustness/test_overlay_p_q.hpp @@ -31,6 +31,8 @@ #include #include +#include +#include struct p_q_settings { @@ -103,6 +105,19 @@ static bool test_overlay_p_q(std::string const& caseid, } } + if (true) + { + if ((area_i > 0 && bg::touches(p, q)) + || (area_i <= 0 && bg::intersects(p, q) && ! bg::touches(p, q))) + { + std::cout << "Wrong 'touch'! " + << " Intersection area: " << area_i + << " Touch gives: " << std::boolalpha << bg::touches(p, q) + << std::endl; + wrong = true; + } + } + bool svg = settings.svg; if (wrong || settings.wkt) diff --git a/test/algorithms/overlay/traverse.cpp b/test/algorithms/overlay/traverse.cpp index 6444b8387..cebd8b000 100644 --- a/test/algorithms/overlay/traverse.cpp +++ b/test/algorithms/overlay/traverse.cpp @@ -7,6 +7,9 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#define BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE +// #define BOOST_GEOMETRY_OVERLAY_NO_THROW +// #define TEST_WITH_SVG #include #include @@ -83,7 +86,7 @@ struct test_traverse { static void apply(std::string const& id, - int expected_count, double expected_area, + std::size_t expected_count, double expected_area, G1 const& g1, G2 const& g2, double precision) { @@ -353,7 +356,7 @@ struct test_traverse G1, G2, Direction, Reverse1, Reverse2 > detail_test_traverse; - inline static void apply(std::string const& id, int expected_count, double expected_area, + inline static void apply(std::string const& id, std::size_t expected_count, double expected_area, std::string const& wkt1, std::string const& wkt2, double precision = 0.001) { @@ -823,22 +826,11 @@ void test_all(bool test_self_tangencies = true, bool test_mixed = false) } { - // Note: values are checked with SQL Server, - /* - select geometry::STGeomFromText('POLYGON((...))', 0) - .STIntersection(geometry::STGeomFromText('...))', 0)) - .STArea() - - and STUnion - */ - - // Boost.List during Formal Review, isovists Brandon - // For FP, they may deviate more. test_traverse::apply("isov", - 1, 88.1920416352664, isovist[0], isovist[1], + 1, 88.1920, isovist[0], isovist[1], float_might_deviate_more); test_traverse::apply("isov", - 1, 313.360374193241, isovist[0], isovist[1], + 1, 313.3604, isovist[0], isovist[1], float_might_deviate_more); } @@ -907,6 +899,51 @@ void test_all(bool test_self_tangencies = true, bool test_mixed = false) 1, 67.3550722317627, ggl_list_20110820_christophe[0], ggl_list_20110820_christophe[1]); } + + test_traverse::apply("buffer_rt_f", + 1, 4.60853, + buffer_rt_f[0], buffer_rt_f[1]); + test_traverse::apply("buffer_rt_f", + 1, 0.0002943725152286, + buffer_rt_f[0], buffer_rt_f[1]); + + test_traverse::apply("buffer_rt_g", + 1, 16.571, + buffer_rt_g[0], buffer_rt_g[1]); + + test_traverse::apply("buffer_rt_g_boxes1", + 1, 20, + buffer_rt_g_boxes[0], buffer_rt_g_boxes[1]); + test_traverse::apply("buffer_rt_g_boxes2", + 1, 24, + buffer_rt_g_boxes[0], buffer_rt_g_boxes[2]); + test_traverse::apply("buffer_rt_g_boxes3", + 1, 28, + buffer_rt_g_boxes[0], buffer_rt_g_boxes[3]); + + test_traverse::apply("buffer_rt_g_boxes43", + 1, 30, + buffer_rt_g_boxes[4], buffer_rt_g_boxes[3]); + +#ifdef BOOST_GEOMETRY_OVERLAY_NO_THROW + { + // NOTE: currently throws (normally) + std::string caseid = "ggl_list_20120229_volker"; + test_traverse::apply(caseid, + 1, 99, + ggl_list_20120229_volker[0], ggl_list_20120229_volker[1]); + test_traverse::apply(caseid, + 1, 99, + ggl_list_20120229_volker[0], ggl_list_20120229_volker[1]); + caseid = "ggl_list_20120229_volker_2"; + test_traverse::apply(caseid, + 1, 99, + ggl_list_20120229_volker[2], ggl_list_20120229_volker[1]); + test_traverse::apply(caseid, + 1, 99, + ggl_list_20120229_volker[2], ggl_list_20120229_volker[1]); + } +#endif } template diff --git a/test/algorithms/test_touches.hpp b/test/algorithms/test_touches.hpp new file mode 100644 index 000000000..aa88ea96d --- /dev/null +++ b/test/algorithms/test_touches.hpp @@ -0,0 +1,61 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2007-2012 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_TOUCHES_HPP +#define BOOST_GEOMETRY_TEST_TOUCHES_HPP + + +#include + +#include +#include +#include +#include +#include + +#include + + +template +void test_touches(std::string const& wkt1, + std::string const& wkt2, bool expected) +{ + Geometry1 geometry1; + Geometry2 geometry2; + + bg::read_wkt(wkt1, geometry1); + bg::read_wkt(wkt2, geometry2); + + bool detected = bg::touches(geometry1, geometry2); + + BOOST_CHECK_MESSAGE(detected == expected, + "touches: " << wkt1 + << " with " << wkt2 + << " -> Expected: " << expected + << " detected: " << detected); +} + + +template +void test_self_touches(std::string const& wkt, bool expected) +{ + Geometry geometry; + + bg::read_wkt(wkt, geometry); + + bool detected = bg::touches(geometry); + + BOOST_CHECK_MESSAGE(detected == expected, + "touches: " << wkt + << " -> Expected: " << expected + << " detected: " << detected); +} + + + +#endif diff --git a/test/algorithms/test_union.hpp b/test/algorithms/test_union.hpp index 0f4a786d8..ec4b0d10c 100644 --- a/test/algorithms/test_union.hpp +++ b/test/algorithms/test_union.hpp @@ -38,7 +38,7 @@ template void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, std::size_t expected_count, std::size_t expected_hole_count, - std::size_t expected_point_count, double expected_area, + int expected_point_count, double expected_area, double percentage) { typedef typename bg::coordinate_type::type coordinate_type; @@ -90,13 +90,15 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, << std::endl; ***/ - BOOST_CHECK_MESSAGE(std::size_t(n) == expected_point_count, - "union: " << caseid - << " #points expected: " << expected_point_count - << " detected: " << n - << " type: " << string_from_type::name() - ); - + if (expected_point_count >= 0) + { + BOOST_CHECK_MESSAGE(n == std::size_t(expected_point_count), + "union: " << caseid + << " #points expected: " << expected_point_count + << " detected: " << n + << " type: " << string_from_type::name() + ); + } BOOST_CHECK_EQUAL(clip.size(), expected_count); BOOST_CHECK_EQUAL(holes, expected_hole_count); @@ -149,7 +151,7 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, template void test_one(std::string const& caseid, std::string const& wkt1, std::string const& wkt2, std::size_t expected_count, std::size_t expected_hole_count, - std::size_t expected_point_count, double expected_area, + int expected_point_count, double expected_area, double percentage = 0.001) { G1 g1; diff --git a/test/algorithms/touches.cpp b/test/algorithms/touches.cpp new file mode 100644 index 000000000..84430d527 --- /dev/null +++ b/test/algorithms/touches.cpp @@ -0,0 +1,154 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// +// Copyright (c) 2012 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 + + + +template +void test_all() +{ + typedef bg::model::polygon

polygon; + + // Just a normal polygon + test_self_touches("POLYGON((0 0,0 4,1.5 2.5,2.5 1.5,4 0,0 0))", false); + + // Self intersecting + test_self_touches("POLYGON((1 2,1 1,2 1,2 2.25,3 2.25,3 0,0 0,0 3,3 3,2.75 2,1 2))", false); + + // Self touching at a point + test_self_touches("POLYGON((0 0,0 3,2 3,2 2,1 2,1 1,2 1,2 2,3 2,3 0,0 0))", true); + + // Self touching at a segment + test_self_touches("POLYGON((0 0,0 3,2 3,2 2,1 2,1 1,2 1,2 2.5,3 2.5,3 0,0 0))", true); + + // Touching at corner + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + "POLYGON((100 100,100 200,200 200, 200 100,100 100))", + true + ); + + // Intersecting at corner + test_touches + ( + "POLYGON((0 0,0 100,101 101,100 0,0 0))", + "POLYGON((100 100,100 200,200 200, 200 100,100 100))", + false + ); + + // Touching at side (interior of a segment) + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + "POLYGON((200 0,100 50,200 100,200 0))", + true + ); + + // Touching at side (partly collinear) + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + "POLYGON((200 20,100 20,100 80,200 80,200 20))", + true + ); + + // Completely equal + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + false + ); + + // Spatially equal + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + "POLYGON((0 0,0 100,100 100,100 80,100 20,100 0,0 0))", + false + ); + + // Spatially equal (without equal segments) + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + "POLYGON((0 0,0 50,0 100,50 100,100 100,100 50,100 0,50 0,0 0))", + false + ); + + + // Touching at side (opposite equal) + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + "POLYGON((200 0,100 0,100 100,200 100,200 0))", + true + ); + + // Touching at side (opposite equal - but with real "equal" turning point) + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 80,100 20,100 0,0 0))", + "POLYGON((200 0,100 0,100 20,100 80,100 100,200 100,200 0))", + true + ); + // First partly collinear to side, than overlapping + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + "POLYGON((200 20,100 20,100 50,50 50,50 80,100 80,200 80,200 20))", + false + ); + + // Touching interior (= no touch) + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0))", + "POLYGON((20 20,20 80,100 50,20 20))", + false + ); + + // Fitting in hole + test_touches + ( + "POLYGON((0 0,0 100,100 100,100 0,0 0),(40 40,60 40,60 60,40 60,40 40))", + "POLYGON((40 40,40 60,60 60,60 40,40 40))", + true + ); + +} + + + + +int test_main( int , char* [] ) +{ + test_all >(); + + +#if defined(HAVE_TTMATH) + test_all >(); +#endif + + return 0; +} + +/* +with viewy as +( +select geometry::STGeomFromText('POLYGON((0 0,0 100,100 100,100 0,0 0))',0) as p + , geometry::STGeomFromText('POLYGON((200 0,100 50,200 100,200 0))',0) as q +) +-- select p from viewy union all select q from viewy +select p.STTouches(q) from viewy +*/ \ No newline at end of file diff --git a/test/algorithms/touches.vcproj b/test/algorithms/touches.vcproj new file mode 100644 index 000000000..79cc17048 --- /dev/null +++ b/test/algorithms/touches.vcproj @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/algorithms/union.cpp b/test/algorithms/union.cpp index 349b256f5..7a2083f3b 100644 --- a/test/algorithms/union.cpp +++ b/test/algorithms/union.cpp @@ -15,6 +15,9 @@ #include #include +#define BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE +#define TEST_ISOVIST + //#define BOOST_GEOMETRY_DEBUG_ASSEMBLE //#define BOOST_GEOMETRY_DEBUG_IDENTIFIER @@ -229,24 +232,102 @@ void test_areal() if_typed(5, if_typed_tt(8, 7)), 14729.07145); -#ifdef TEST_ENRICO test_one("ggl_list_20110716_enrico", ggl_list_20110716_enrico[0], ggl_list_20110716_enrico[1], 1, 1, - if_typed(18, 17), + if_typed(18, if_typed(15, 17)), 129904.197692871); -#endif + test_one("ggl_list_20110820_christophe", + ggl_list_20110820_christophe[0], ggl_list_20110820_christophe[1], + if_typed(2, 1), + 0, + if_typed_tt(9, 8), + 67.3550722317627); + + +#ifdef TEST_ISOVIST #ifdef _MSC_VER - // Isovist (submitted by Brandon during Formal Review) test_one("isovist", isovist1[0], isovist1[1], 1, 0, - if_typed(71, - if_typed(70, 73)), - 313.36036462); + if_typed(71, if_typed(70, 73)), + 313.36036462, 0.1); + + // SQL Server gives: 313.360374193241 + // PostGIS gives: 313.360364623393 + #endif +#endif + + // Ticket 5103 https://svn.boost.org/trac/boost/ticket/5103 + // This ticket was actually reported for Boost.Polygon + // We check it for Boost.Geometry as well. + // SQL Server gives: 2515271331437.69 + // PostGIS gives: 2515271327070.52 + // Boost.Geometry gives: 2515271327070.5237746891 (ttmath) + // 2515271327070.5156 (double) + // 2515271320603.0000 (int) + // Note the int-test was tested outside of this unit test. It is in two points 0.37 off (logical for an int). + // Because of the width of the polygon (400000 meter) this causes a substantial difference. + + test_one("ticket_5103", ticket_5103[0], ticket_5103[1], + 1, 0, 25, 2515271327070.5); + + test_one("buffer_rt_a", buffer_rt_a[0], buffer_rt_a[1], + 1, 0, 265, 19.280667); + + // Robustness issues, followed out buffer-robustness-tests, test them also reverse + test_one("buffer_rt_f", buffer_rt_f[0], buffer_rt_f[1], + 1, 0, if_typed(22, 23), 4.60853); + test_one("buffer_rt_f_rev", buffer_rt_f[1], buffer_rt_f[0], + 1, 0, if_typed(22, 23), 4.60853); + + test_one("buffer_rt_g", buffer_rt_g[0], buffer_rt_g[1], + 1, 0, 17, 16.571); + test_one("buffer_rt_g_rev", buffer_rt_g[1], buffer_rt_g[0], + 1, 0, 17, 16.571); + + test_one("buffer_rt_i", buffer_rt_i[0], buffer_rt_i[1], + 1, 0, 13, 13.6569); + test_one("buffer_rt_i_rev", buffer_rt_i[1], buffer_rt_i[0], + 1, 0, 13, 13.6569); + + test_one("buffer_rt_j", buffer_rt_j[0], buffer_rt_j[1], + 1, 0, -1, 16.5711); + test_one("buffer_rt_j_rev", buffer_rt_j[1], buffer_rt_j[0], + 1, 0, -1, 16.5711); + + test_one("buffer_rt_l", buffer_rt_l[0], buffer_rt_l[1], + 1, 0, -1, 19.3995); + test_one("buffer_rt_l_rev", buffer_rt_l[1], buffer_rt_l[0], + 1, 0, -1, 19.3995); + + test_one("buffer_rt_m1", buffer_rt_m1[0], buffer_rt_m1[1], + 1, 0, if_typed_tt(14, 13), 19.4852); + test_one("buffer_rt_m1_rev", buffer_rt_m1[1], buffer_rt_m1[0], + 1, 0, if_typed_tt(14, 13), 19.4852); + + test_one("buffer_rt_m2", buffer_rt_m2[0], buffer_rt_m2[1], + 1, 0, if_typed_tt(20, 19), 21.4853); + test_one("buffer_rt_m2_rev", buffer_rt_m2[1], buffer_rt_m2[0], + 1, 0, if_typed_tt(20, 19), 21.4853); + + test_one("buffer_rt_q", buffer_rt_q[0], buffer_rt_q[1], + 1, 0, if_typed(18, 17), 18.5710); + test_one("buffer_rt_q_rev", buffer_rt_q[1], buffer_rt_q[0], + 1, 0, if_typed(18, 17), 18.5710); + + test_one("buffer_rt_r", buffer_rt_r[0], buffer_rt_r[1], + 1, 0, if_typed(19, 20), 21.07612); + test_one("buffer_rt_r_rev", buffer_rt_r[1], buffer_rt_r[0], + 1, 0, if_typed_tt(20, 19), 21.07612); + + test_one("buffer_rt_t", buffer_rt_t[0], buffer_rt_t[1], + 1, 0, if_typed_tt(16, 14), 15.6569); + test_one("buffer_rt_t_ref", buffer_rt_t[1], buffer_rt_t[0], + 1, 0, if_typed_tt(16, 14), 15.6569); } template @@ -258,6 +339,7 @@ void test_all() test_areal(); +#if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE) // Open test_areal, bg::model::polygon >(); @@ -266,6 +348,7 @@ void test_all() // Counter clockwise and open test_areal, bg::model::polygon >(); +#endif test_one("box_ring", example_box, example_ring, 1, 1, 15, 6.38875); @@ -302,32 +385,23 @@ void test_all() test_one("box_poly8", "box(0 0, 3 3)", "POLYGON((2 2, 1 4, 2 4, 3 3, 2 2))", 1, 0, 8, 10.25); - - // Ticket 5103 https://svn.boost.org/trac/boost/ticket/5103 - // This ticket was actually reported for Boost.Polygon - // but it is apparently a difficult case so we check it for Boost.Geometry as well. - // SQL Server gives: 2515271331437.69 - // PostGIS gives: 2515271327070.52 - // Boost.Geometry gives: 2515271327070.5237746891 (ttmath) - // 2515271327070.5156 (double) - // 2515271320603.0000 (int) - // Note the int-test was tested externally - it is in two points 0.37 off (makes sense). - // Because of the width of the polygon (400000 meter) this might indeed cause a substantial difference. - - test_one("ticket_5103", ticket_5103[0], ticket_5103[1], - 1, 0, 25, 2515271327070.5); } int test_main(int, char* []) { test_all >(); +#if defined(HAVE_TTMATH) + std::cout << "Testing TTMATH" << std::endl; + test_all >(); +#endif #if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE) test_all >(); //test_all >(); #if defined(HAVE_TTMATH) + std::cout << "Testing TTMATH" << std::endl; test_all >(); #endif #endif diff --git a/test/core/ring.cpp b/test/core/ring.cpp index f95f6ee41..f2c3ac4cb 100644 --- a/test/core/ring.cpp +++ b/test/core/ring.cpp @@ -34,9 +34,9 @@ template void test_ring(std::string const& wkt, - int expected_main_count, - int expected_interior_ring_count, - int expected_first_interior_count) + std::size_t expected_main_count, + std::size_t expected_interior_ring_count, + std::size_t expected_first_interior_count) { typedef bg::model::polygon

the_polygon; typedef typename bg::ring_type::type the_ring; @@ -48,7 +48,7 @@ void test_ring(std::string const& wkt, the_ring ext = bg::exterior_ring(poly); the_interior rings = bg::interior_rings(poly); - BOOST_CHECK_EQUAL(bg::num_interior_rings(poly), std::size_t(expected_interior_ring_count)); + BOOST_CHECK_EQUAL(bg::num_interior_rings(poly), expected_interior_ring_count); BOOST_CHECK_EQUAL(boost::size(rings), expected_interior_ring_count); BOOST_CHECK_EQUAL(boost::size(ext), expected_main_count); if (boost::size(rings)) diff --git a/test/geometries/adapted.cpp b/test/geometries/adapted.cpp index f1e7ed3a9..3ba47ef72 100644 --- a/test/geometries/adapted.cpp +++ b/test/geometries/adapted.cpp @@ -58,7 +58,7 @@ BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian) // ---------------------------------------------------------------------------- template -void test_geometry(G const& geometry, int expected_size = 0) +void test_geometry(G const& geometry, std::size_t expected_size = 0) { #if defined(BOOST_GEOMETRY_TEST_RING) BOOST_CONCEPT_ASSERT( (bg::concept::ConstRing) ); diff --git a/test/geometries/custom_linestring.cpp b/test/geometries/custom_linestring.cpp index 303e5f083..ef2bad43a 100644 --- a/test/geometries/custom_linestring.cpp +++ b/test/geometries/custom_linestring.cpp @@ -87,15 +87,15 @@ void test_linestring() typedef typename bg::point_type::type P; bg::clear(geometry); - BOOST_CHECK_EQUAL(boost::size(geometry), 0); + BOOST_CHECK_EQUAL(boost::size(geometry), 0u); bg::append(geometry, bg::make_zero

()); - BOOST_CHECK_EQUAL(boost::size(geometry), 1); + BOOST_CHECK_EQUAL(boost::size(geometry), 1u); //std::cout << geometry << std::endl; bg::clear(geometry); - BOOST_CHECK_EQUAL(boost::size(geometry), 0); + BOOST_CHECK_EQUAL(boost::size(geometry), 0u); //P p = boost::range::front(geometry); diff --git a/test/multi/algorithms/Jamfile.v2 b/test/multi/algorithms/Jamfile.v2 index b0ea72667..813d6aafa 100644 --- a/test/multi/algorithms/Jamfile.v2 +++ b/test/multi/algorithms/Jamfile.v2 @@ -17,6 +17,7 @@ test-suite boost-geometry-multi-algorithms [ run multi_correct.cpp ] [ run multi_covered_by.cpp ] [ run multi_difference.cpp ] + [ run multi_disjoint.cpp ] [ run multi_distance.cpp ] [ run multi_envelope.cpp ] [ run multi_equals.cpp ] @@ -29,6 +30,7 @@ test-suite boost-geometry-multi-algorithms [ run multi_perimeter.cpp ] [ run multi_reverse.cpp ] [ run multi_simplify.cpp ] + [ run multi_touches.cpp ] [ run multi_transform.cpp ] [ run multi_union.cpp ] [ run multi_unique.cpp ] diff --git a/test/multi/algorithms/multi_difference.cpp b/test/multi/algorithms/multi_difference.cpp index 7bf3701e9..e5e47bc5c 100644 --- a/test/multi/algorithms/multi_difference.cpp +++ b/test/multi/algorithms/multi_difference.cpp @@ -13,6 +13,11 @@ // #define BOOST_GEOMETRY_DEBUG_ASSEMBLE //#define BOOST_GEOMETRY_CHECK_WITH_SQLSERVER +//#define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER +//#define BOOST_GEOMETRY_DEBUG_FOLLOW +//#define BOOST_GEOMETRY_DEBUG_TRAVERSE + + #include #include #include @@ -163,6 +168,13 @@ void test_areal_linear() test_one_lp("case_mp_ls_3", "LINESTRING(6 6,6 7,7 7,7 6,8 6,8 7,9 7,9 6)", "MULTIPOLYGON(((5 7,5 8,6 8,6 7,5 7)),((6 6,6 7,7 7,7 6,6 6)),((8 8,9 8,9 7,8 7,7 7,7 8,8 8)))", 2, 5, 3.0); + + return; + + // TODO: this case contains collinearities and should still be solved + test_one_lp("case_mp_ls_4", + "LINESTRING(0 5,0 6,1 6,1 5,2 5,2 6,3 6,3 5,3 4,3 3,2 3,2 4,1 4,1 3,0 3,0 4)", + "MULTIPOLYGON(((0 2,0 3,1 2,0 2)),((2 5,3 6,3 5,2 5)),((1 5,1 6,2 6,2 5,1 5)),((2 3,2 4,3 4,2 3)),((0 3,1 4,1 3,0 3)),((4 3,3 3,3 5,4 5,4 4,4 3)))", 5, 11, 6.0); } @@ -183,6 +195,7 @@ int test_main(int, char* []) test_all >(); #ifdef HAVE_TTMATH + std::cout << "Testing TTMATH" << std::endl; test_all >(); #endif diff --git a/test/multi/algorithms/multi_disjoint.cpp b/test/multi/algorithms/multi_disjoint.cpp new file mode 100644 index 000000000..a1b88ce21 --- /dev/null +++ b/test/multi/algorithms/multi_disjoint.cpp @@ -0,0 +1,123 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2012 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include + +#include + + +template +void test_disjoint(std::string const& id, + std::string const& wkt1, + std::string const& wkt2, bool expected) +{ + G1 g1; + bg::read_wkt(wkt1, g1); + + G2 g2; + bg::read_wkt(wkt2, g2); + + bool detected = bg::disjoint(g1, g2); + BOOST_CHECK_MESSAGE(detected == expected, + "disjoint: " << id + << " -> Expected: " << expected + << " detected: " << detected); +} + + + +template +void test_all() +{ + typedef bg::model::polygon

polygon; + typedef bg::model::multi_polygon mp; + + test_disjoint("", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + false); + + // True disjoint + test_disjoint("", + "MULTIPOLYGON(((0 0,0 4,4 4,4 0,0 0)))", + "MULTIPOLYGON(((6 6,6 10,10 10,10 6,6 6)))", + true); + + // Touch -> not disjoint + test_disjoint("", + "MULTIPOLYGON(((0 0,0 5,5 5,5 0,0 0)))", + "MULTIPOLYGON(((5 5,5 10,10 10,10 5,5 5)))", + false); + + // Not disjoint but no IP's + test_disjoint("no_ips", + "MULTIPOLYGON(((2 2,2 8,8 8,8 2,2 2)),((20 0,20 10,30 10,30 0,20 0)))", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((22 2,28 2,28 8,22 8,22 2)))", + false); + + // Not disjoint and not inside each other (in first ring) but wrapped (in second rings) + test_disjoint("no_ips2", + "MULTIPOLYGON(((2 2,2 8,8 8,8 2,2 2)),((20 0,20 10,30 10,30 0,20 0)))", + "MULTIPOLYGON(((2 12,2 18,8 18,8 12,2 12)),((22 2,28 2,28 8,22 8,22 2)))", + false); + + test_disjoint("no_ips2_rev", + "MULTIPOLYGON(((2 12,2 18,8 18,8 12,2 12)),((22 2,28 2,28 8,22 8,22 2)))", + "MULTIPOLYGON(((2 2,2 8,8 8,8 2,2 2)),((20 0,20 10,30 10,30 0,20 0)))", + false); +} + +int test_main(int, char* []) +{ + //test_all >(); + test_all >(); + +#ifdef HAVE_TTMATH + test_all >(); +#endif + + return 0; +} + + +/* +with viewy as +( +select geometry::STGeomFromText('MULTIPOLYGON(((2 2,2 8,8 8,8 2,2 2)),((20 0,20 10,30 10,30 0,20 0)))',0) as p + , geometry::STGeomFromText('MULTIPOLYGON(((2 12,2 18,8 18,8 12,2 12)),((22 2,28 2,28 8,22 8,22 2)))',0) as q +) + select p from viewy union all select q from viewy +-- select p.STDisjoint(q) from viewy +*/ \ No newline at end of file diff --git a/test/multi/algorithms/multi_disjoint.vcproj b/test/multi/algorithms/multi_disjoint.vcproj new file mode 100644 index 000000000..fb55ab325 --- /dev/null +++ b/test/multi/algorithms/multi_disjoint.vcproj @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/multi/algorithms/multi_intersection.cpp b/test/multi/algorithms/multi_intersection.cpp index c6a0efced..d479f5d11 100644 --- a/test/multi/algorithms/multi_intersection.cpp +++ b/test/multi/algorithms/multi_intersection.cpp @@ -212,6 +212,7 @@ int test_main(int, char* []) test_all >(); #ifdef HAVE_TTMATH + std::cout << "Testing TTMATH" << std::endl; test_all >(); #endif diff --git a/test/multi/algorithms/multi_touches.cpp b/test/multi/algorithms/multi_touches.cpp new file mode 100644 index 000000000..0186c62e3 --- /dev/null +++ b/test/multi/algorithms/multi_touches.cpp @@ -0,0 +1,80 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// +// Copyright (c) 2010-2012 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + + +template +void test_all() +{ + typedef bg::model::polygon

polygon; + typedef bg::model::multi_polygon mp; + + test_self_touches("MULTIPOLYGON(((0 0,0 100,100 100,100 0,0 0)))", + false); + + // Exactly equal + test_touches("MULTIPOLYGON(((0 0,0 100,100 100,100 0,0 0)))", + "MULTIPOLYGON(((0 0,0 100,100 100,100 0,0 0)))", + false); + + // Spatially equal + test_touches("MULTIPOLYGON(((0 0,0 100,100 100,100 0,0 0)))", + "MULTIPOLYGON(((0 0,0 100,100 100,100 80,100 20,100 0,0 0)))", + false); + + // One exactly equal, another pair touching + test_touches("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((20 0,20 10,30 10,30 0,20 0)))", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((30 10,30 20,40 20,40 10,30 10)))", + false); + + // One spatially equal (without equal segments), another pair touching + test_touches("MULTIPOLYGON(((0 0,0 5,0 10,5 10,10 10,10 5,10 0,5 0,0 0)),((20 0,20 10,30 10,30 0,20 0)))", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((30 10,30 20,40 20,40 10,30 10)))", + false); + + // Alternate touches + test_touches("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((20 0,20 10,30 10,30 0,20 0)))", + "MULTIPOLYGON(((10 10,10 20,20 20,20 10,10 10)),((30 10,30 20,40 20,40 10,30 10)))", + true); + + // Touch plus inside + // TODO fix this + test_touches("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((20 0,20 10,30 10,30 0,20 0)))", + "MULTIPOLYGON(((10 10,10 20,20 20,20 10,10 10)),((22 2,28 2,28 8,22 8,22 2)))", + false); +} + +int test_main( int , char* [] ) +{ + test_all >(); + +#ifdef HAVE_TTMATH + test_all >(); +#endif + + return 0; +} diff --git a/test/multi/algorithms/multi_touches.vcproj b/test/multi/algorithms/multi_touches.vcproj new file mode 100644 index 000000000..05468f6cd --- /dev/null +++ b/test/multi/algorithms/multi_touches.vcproj @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/multi/algorithms/multi_union.cpp b/test/multi/algorithms/multi_union.cpp index e06306254..ffa694291 100644 --- a/test/multi/algorithms/multi_union.cpp +++ b/test/multi/algorithms/multi_union.cpp @@ -105,7 +105,7 @@ void test_areal() 1, 0, 14, 100.0); // Area from SQL Server test_one("case_recursive_boxes_3", case_recursive_boxes_3[0], case_recursive_boxes_3[1], - 18, 0, 160, 56.5); // Area from SQL Server + 17, 0, 159, 56.5); // Area from SQL Server } template @@ -134,6 +134,7 @@ int test_main(int, char* []) test_all >(); #ifdef HAVE_TTMATH + std::cout << "Testing TTMATH" << std::endl; test_all >(); #endif diff --git a/test/multi/algorithms/overlay/multi_overlay_cases.hpp b/test/multi/algorithms/overlay/multi_overlay_cases.hpp index 6c5817104..536aa0953 100644 --- a/test/multi/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/multi/algorithms/overlay/multi_overlay_cases.hpp @@ -293,7 +293,7 @@ static std::string case_100_multi[2] = { // for intersection "MULTIPOLYGON(((0 0,0 1,1 0,0 0)),((2 2,2 1,0 1,0 2,1 2,2 3,2 2)))", - "MULTIPOLYGON(((1 1,1 2,2 2,2 1,1 1)),((1 2,0 1,0 3,1 4,1 2))))" + "MULTIPOLYGON(((1 1,1 2,2 2,2 1,1 1)),((1 2,0 1,0 3,1 4,1 2)))" }; static std::string case_101_multi[2] = diff --git a/test/multi/io/wkt/wkt.cpp b/test/multi/io/wkt/wkt.cpp index 55476d705..a149946cf 100644 --- a/test/multi/io/wkt/wkt.cpp +++ b/test/multi/io/wkt/wkt.cpp @@ -55,9 +55,30 @@ void test_all() test_wkt > >("multilinestring((1 1,2 2,3 3),(4 4,5 5,6 6))", 6, 4 * sqrt(2.0)); test_wkt > >("multipolygon(((0 0,0 2,2 2,2 0,0 0),(1 1,1 2,2 2,2 1,1 1)),((0 0,0 4,4 4,4 0,0 0)))", 15, 0, 21, 28); + // Support for the official alternative syntax for multipoint + // (provided by Aleksey Tulinov): + test_relaxed_wkt >("multipoint(1 2,3 4)", "multipoint((1 2),(3 4))"); + test_wrong_wkt > >( "MULTIPOLYGON(((0 0,0 2,2 2,2 0,0 0),(1 1,1 2,2 2,2 1,1 1)),(0 0,0 4,4 4,4 0,0 0)))", "expected '('"); + + test_wrong_wkt > >( + "MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10)), (0 0, 1 1)", + "too much tokens at ','"); + + test_wrong_wkt >( + "MULTIPOINT((8 9), 10 11)", + "expected '(' at '10'"); + test_wrong_wkt >( + "MULTIPOINT(12 13, (14 15))", + "bad lexical cast: source type value could not be interpreted as target at '(' in 'multipoint(12 13, (14 15))'"); + test_wrong_wkt >( + "MULTIPOINT((16 17), (18 19)", + "expected ')' in 'multipoint((16 17), (18 19)'"); + test_wrong_wkt >( + "MULTIPOINT(16 17), (18 19)", + "too much tokens at ',' in 'multipoint(16 17), (18 19)'"); } /* diff --git a/test/multi/io/wkt/wkt.vcproj b/test/multi/io/wkt/wkt.vcproj index 19cc3e426..c8150e853 100644 --- a/test/multi/io/wkt/wkt.vcproj +++ b/test/multi/io/wkt/wkt.vcproj @@ -20,7 +20,7 @@ OutputDirectory="$(SolutionDir)$(ConfigurationName)" IntermediateDirectory="$(ConfigurationName)\wkt" ConfigurationType="1" - InheritedPropertySheets="..\..\..\..\boost.vsprops" + InheritedPropertySheets="..\..\..\boost.vsprops" CharacterSet="1" > @@ -114,7 +114,7 @@ /> + +template +inline void make_square_polygon(Polygon& polygon, Generator& generator, Settings const& settings) +{ + using namespace boost::geometry; + + typedef typename point_type::type point_type; + typedef typename coordinate_type::type coordinate_type; + + coordinate_type x, y; + x = generator(); + y = generator(); + + typename ring_type::type& ring = exterior_ring(polygon); + + point_type p; + set<0>(p, x); set<1>(p, y); append(ring, p); + set<0>(p, x); set<1>(p, y + 1); append(ring, p); + set<0>(p, x + 1); set<1>(p, y + 1); append(ring, p); + set<0>(p, x + 1); set<1>(p, y); append(ring, p); + set<0>(p, x); set<1>(p, y); append(ring, p); + + if (settings.triangular) + { + // Remove a point, generator says which + int c = generator() % 4; + if (c >= 1 && c <= 3) + { + ring.erase(ring.begin() + c); + } + } +} + +#endif // BOOST_GEOMETRY_TEST_ROBUSTNESS_MAKE_SQUARE_POLYGON_HPP diff --git a/test/robustness/convex_hull/Jamfile.v2 b/test/robustness/convex_hull/Jamfile.v2 new file mode 100644 index 000000000..20521d548 --- /dev/null +++ b/test/robustness/convex_hull/Jamfile.v2 @@ -0,0 +1,18 @@ +# Boost.Geometry (aka GGL, Generic Geometry Library) +# Robustness Test - convex_hull +# +# Copyright (c) 2012 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) + + +project random_multi_points + : requirements + . + ../../../../program_options/build//boost_program_options + static + ; + +exe random_multi_points : random_multi_points.cpp ; diff --git a/test/robustness/convex_hull/random_multi_points.cpp b/test/robustness/convex_hull/random_multi_points.cpp index 9ce4e406c..90ea7e2cd 100644 --- a/test/robustness/convex_hull/random_multi_points.cpp +++ b/test/robustness/convex_hull/random_multi_points.cpp @@ -1,5 +1,5 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Robustness Test +// Robustness Test - convex_hull // Copyright (c) 2012 Barend Gehrels, Amsterdam, the Netherlands. diff --git a/test/robustness/overlay/buffer/Jamfile.v2 b/test/robustness/overlay/buffer/Jamfile.v2 new file mode 100644 index 000000000..f724fcf8c --- /dev/null +++ b/test/robustness/overlay/buffer/Jamfile.v2 @@ -0,0 +1,19 @@ +# Boost.Geometry (aka GGL, Generic Geometry Library) +# Robustness Test - overlay - buffer +# +# Copyright (c) 2012 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) + + +project recursive_polygons_buffer + : requirements + . + ../.. + ../../../../../program_options/build//boost_program_options + static + ; + +exe recursive_polygons_buffer : recursive_polygons_buffer.cpp ; diff --git a/test/robustness/overlay/buffer/recursive_polygons_buffer.cpp b/test/robustness/overlay/buffer/recursive_polygons_buffer.cpp new file mode 100644 index 000000000..e54260f3b --- /dev/null +++ b/test/robustness/overlay/buffer/recursive_polygons_buffer.cpp @@ -0,0 +1,352 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Robustness Test + +// Copyright (c) 2012 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) + +#if defined(_MSC_VER) +# pragma warning( disable : 4244 ) +# pragma warning( disable : 4267 ) +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include // TODO: more specific +#include + +#include + + +#include +#include + + +struct buffer_settings : public common_settings +{ + int join_code; + double distance; +}; + +namespace bg = boost::geometry; + +template +void create_svg(std::string const& filename + , Geometry1 const& mp + , Geometry2 const& buffer + ) +{ + typedef typename boost::geometry::point_type::type point_type; + + + std::ofstream svg(filename.c_str()); + boost::geometry::svg_mapper mapper(svg, 800, 800); + + boost::geometry::model::box box; + bg::envelope(mp, box); + bg::buffer(box, box, 1.0); + mapper.add(box); + + if (bg::num_points(buffer) > 0) + { + bg::envelope(buffer, box); + bg::buffer(box, box, 1.0); + mapper.add(box); + } + + mapper.map(mp, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:3"); + mapper.map(buffer, "stroke-opacity:0.9;stroke:rgb(0,0,0);fill:none;stroke-width:1"); + + //mapper.map(intersection,"opacity:0.6;stroke:rgb(0,128,0);stroke-width:5"); +} + + + + +template +bool verify(std::string const& caseid, MultiPolygon const& mp, MultiPolygon const& buffer, Settings const& settings) +{ + bool result = true; + + // Area of buffer must be larger than of original polygon + BOOST_AUTO(area_mp, bg::area(mp)); + BOOST_AUTO(area_buf, bg::area(buffer)); + + if (area_buf < area_mp) + { + result = false; + } + + if (result) + { + typedef boost::range_value::type polygon_type; + BOOST_FOREACH(polygon_type const& polygon, mp) + { + typename bg::point_type::type point; + bg::point_on_border(point, polygon); + if (! bg::within(point, buffer)) + { + result = false; + } + } + } + + bool svg = settings.svg; + bool wkt = settings.wkt; + if (! result) + { + std::cout << "ERROR " << caseid << std::endl; + //std::cout << bg::wkt(mp) << std::endl; + //std::cout << bg::wkt(buffer) << std::endl; + svg = true; + wkt = true; + } + + if (svg || wkt) + { + //std::cout << caseid << std::endl; + } + + if (svg) + { + std::ostringstream filename; + filename << caseid << "_" + << typeid(typename bg::coordinate_type::type).name() + << ".svg"; + create_svg(filename.str(), mp, buffer); + } + + if (wkt) + { + std::ostringstream filename; + filename << caseid << "_" + << typeid(typename bg::coordinate_type::type).name() + << ".wkt"; + std::ofstream stream(filename.str().c_str()); + stream << bg::wkt(mp) << std::endl; + stream << bg::wkt(buffer) << std::endl; + } + + return result; +} + +template +bool test_buffer(MultiPolygon& result, int& index, + Generator& generator, + int level, Settings const& settings) +{ + MultiPolygon p, q; + + // Generate two boxes + if (level == 0) + { + p.resize(1); + q.resize(1); + make_square_polygon(p.front(), generator, settings); + make_square_polygon(q.front(), generator, settings); + bg::correct(p); + bg::correct(q); + } + else + { + bg::correct(p); + bg::correct(q); + if (! test_buffer(p, index, generator, level - 1, settings) + || ! test_buffer(q, index, generator, level - 1, settings)) + { + return false; + } + } + + typedef typename boost::range_value::type polygon; + + MultiPolygon mp; + bg::detail::union_::union_insert + < + polygon + >(p, q, std::back_inserter(mp)); + + bg::unique(mp); + bg::unique(mp); + bg::correct(mp); + result = mp; + + + typedef typename bg::coordinate_type::type coordinate_type; + typedef typename bg::point_type::type point_type; + typedef bg::strategy::buffer::distance_assymetric distance_strategy_type; + distance_strategy_type distance_strategy(settings.distance, settings.distance); + + typedef bg::strategy::buffer::join_round join_strategy_type; + join_strategy_type join_strategy; + + typedef typename boost::range_value::type polygon_type; + MultiPolygon buffered; + + std::ostringstream out; + out << "recursive_polygons_buffer_" << index++ << "_" << level; + + try + { + switch(settings.join_code) + { + case 1 : + bg::buffer_inserter(mp, std::back_inserter(buffered), + distance_strategy, + bg::strategy::buffer::join_round()); + break; + case 2 : + bg::buffer_inserter(mp, std::back_inserter(buffered), + distance_strategy, + bg::strategy::buffer::join_miter()); + break; + default : + return false; + } + } + catch(std::exception const& e) + { + MultiPolygon empty; + std::cout << out.str() << std::endl; + std::cout << "Exception " << e.what() << std::endl; + verify(out.str(), mp, empty, settings); + return false; + } + + + return verify(out.str(), mp, buffered, settings); +} + + +template +void test_all(int seed, int count, int level, Settings const& settings) +{ + boost::timer t; + + typedef boost::minstd_rand base_generator_type; + + base_generator_type generator(seed); + + boost::uniform_int<> random_coordinate(0, settings.field_size - 1); + boost::variate_generator > + coordinate_generator(generator, random_coordinate); + + typedef bg::model::polygon + < + bg::model::d2::point_xy, Clockwise, Closed + > polygon; + typedef bg::model::multi_polygon mp; + + + int index = 0; + for(int i = 0; i < count; i++) + { + mp p; + test_buffer(p, index, coordinate_generator, level, settings); + } + std::cout + << "geometries: " << index + << " type: " << typeid(T).name() + << " time: " << t.elapsed() << std::endl; +} + +int main(int argc, char** argv) +{ + try + { + namespace po = boost::program_options; + po::options_description description("=== recursive_polygons_linear_areal ===\nAllowed options"); + + int count = 1; + int seed = static_cast(std::time(0)); + int level = 3; + bool ccw = false; + bool open = false; + buffer_settings settings; + std::string form = "box"; + std::string join = "round"; + + description.add_options() + ("help", "Help message") + ("seed", po::value(&seed), "Initialization seed for random generator") + ("count", po::value(&count)->default_value(1), "Number of tests") + ("level", po::value(&level)->default_value(3), "Level to reach (higher->slower)") + ("distance", po::value(&settings.distance)->default_value(1.0), "Distance (1.0)") + ("form", po::value(&form)->default_value("box"), "Form of the polygons (box, triangle)") + ("join", po::value(&join)->default_value("round"), "Form of the joins (round, miter)") + ("ccw", po::value(&ccw)->default_value(false), "Counter clockwise polygons") + ("open", po::value(&open)->default_value(false), "Open polygons") + ("size", po::value(&settings.field_size)->default_value(10), "Size of the field") + ("wkt", po::value(&settings.wkt)->default_value(false), "Create a WKT of the inputs, for all tests") + ("svg", po::value(&settings.svg)->default_value(false), "Create a SVG for all tests") + ; + + po::variables_map varmap; + po::store(po::parse_command_line(argc, argv, description), varmap); + po::notify(varmap); + + if (varmap.count("help") + || (form != "box" && form != "triangle") + || (join != "round" && join != "miter") + ) + { + std::cout << description << std::endl; + return 1; + } + + settings.triangular = form != "box"; + settings.join_code = join == "round" ? 1 : 2; + + if (ccw && open) + { + test_all(seed, count, level, settings); + } + else if (ccw) + { + test_all(seed, count, level, settings); + } + else if (open) + { + test_all(seed, count, level, settings); + } + else + { + test_all(seed, count, level, settings); + } + +#if defined(HAVE_TTMATH) + // test_all(seed, count, max, svg, level); +#endif + } + catch(std::exception const& e) + { + std::cout << "Exception " << e.what() << std::endl; + } + catch(...) + { + std::cout << "Other exception" << std::endl; + } + + return 0; +} diff --git a/test/robustness/overlay/buffer/recursive_polygons_buffer.sln b/test/robustness/overlay/buffer/recursive_polygons_buffer.sln new file mode 100644 index 000000000..6e8c2574f --- /dev/null +++ b/test/robustness/overlay/buffer/recursive_polygons_buffer.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "recursive_polygons_buffer", "recursive_polygons_buffer.vcxproj", "{02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}.Debug|Win32.ActiveCfg = Debug|Win32 + {02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}.Debug|Win32.Build.0 = Debug|Win32 + {02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}.Release|Win32.ActiveCfg = Release|Win32 + {02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/robustness/overlay/buffer/recursive_polygons_buffer.vcproj b/test/robustness/overlay/buffer/recursive_polygons_buffer.vcproj new file mode 100644 index 000000000..3cc4a384f --- /dev/null +++ b/test/robustness/overlay/buffer/recursive_polygons_buffer.vcproj @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/robustness/overlay/linear_areal/Jamfile.v2 b/test/robustness/overlay/linear_areal/Jamfile.v2 new file mode 100644 index 000000000..a31dadf15 --- /dev/null +++ b/test/robustness/overlay/linear_areal/Jamfile.v2 @@ -0,0 +1,19 @@ +# Boost.Geometry (aka GGL, Generic Geometry Library) +# Robustness Test - overlay - linear/areal +# +# Copyright (c) 2012 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) + + +project recursive_polygons_linear_areal + : requirements + . + ../.. + ../../../../../program_options/build//boost_program_options + static + ; + +exe recursive_polygons_linear_areal : recursive_polygons_linear_areal.cpp ; diff --git a/test/robustness/overlay/linear_areal/recursive_polygons_linear_areal.cpp b/test/robustness/overlay/linear_areal/recursive_polygons_linear_areal.cpp new file mode 100644 index 000000000..3e5e88bd3 --- /dev/null +++ b/test/robustness/overlay/linear_areal/recursive_polygons_linear_areal.cpp @@ -0,0 +1,501 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Robustness Test + +// Copyright (c) 2012 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) + +#if defined(_MSC_VER) +# pragma warning( disable : 4244 ) +# pragma warning( disable : 4267 ) +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + + +namespace bg = boost::geometry; + +template +void create_svg(std::string const& filename + , Geometry1 const& mp + , Geometry2 const& ls + , Geometry3 const& difference + , Geometry3 const& intersection + ) +{ + typedef typename boost::geometry::point_type::type point_type; + + + std::ofstream svg(filename.c_str()); + boost::geometry::svg_mapper mapper(svg, 800, 800); + + boost::geometry::model::box box; + bg::envelope(mp, box); + bg::buffer(box, box, 1.0); + mapper.add(box); + bg::envelope(ls, box); + bg::buffer(box, box, 1.0); + mapper.add(box); + + mapper.map(mp, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:3"); + mapper.map(ls, "stroke-opacity:0.9;stroke:rgb(0,0,0);stroke-width:1"); + + mapper.map(intersection,"opacity:0.6;stroke:rgb(0,128,0);stroke-width:5"); + mapper.map(difference, "opacity:0.6;stroke:rgb(255,0,255);stroke-width:5"); //;stroke-dasharray:1,7;stroke-linecap:round +} + + +template +inline void make_random_linestring(Linestring& line, Generator& generator, Settings const& settings) +{ + using namespace boost::geometry; + + typedef typename point_type::type point_type; + typedef typename coordinate_type::type coordinate_type; + + coordinate_type x, y; + x = generator(); + y = generator(); + + int count = 3 + generator() % 6; + int d = 0; // direction + + for (int i = 0; i <= count && x <= settings.field_size; i++, x++, d = 1 - d) + { + append(line, make(x, y + d)); + append(line, make(x, y + 1 - d)); + } + + if (d == 0 && generator() % 4 < 3 && y >= 2) + { + d = 1 - d; + x--; + y -= 2; + count = 3 + generator() % 6; + for (int i = 0; i <= count && x >= 0; i++, x--, d = 1 - d) + { + append(line, make(x, y + d)); + append(line, make(x, y + 1 - d)); + } + } + + //if (settings.triangular) + //{ + // // Remove a point, generator says which + // int c = generator() % 4; + // if (c >= 1 && c <= 3) + // { + // ring.erase(ring.begin() + c); + // } + //} +} + +template +class inside_check +{ + Geometry const& m_geo; + bool& m_result; +public : + + inside_check(Geometry const& geo, bool& result) + : m_geo(geo) + , m_result(result) + {} + + inline inside_check operator=(inside_check const& input) + { + return inside_check(input.m_geo, input.m_result); + } + + template + inline void operator()(Point const& p) + { + if (! bg::covered_by(p, m_geo)) + { + if (m_result) + { + std::cout << "Does not fulfill inside check" << std::endl; + } + m_result = false; + } + } +}; + + +template +class outside_check +{ + Geometry const& m_geo; + bool& m_result; +public : + outside_check(Geometry const& geo, bool& result) + : m_geo(geo) + , m_result(result) + {} + + inline outside_check operator=(outside_check const& input) + { + return outside_check(input.m_geo, input.m_result); + } + + template + inline void operator()(Point const& p) + { + if (bg::within(p, m_geo)) + { + if (m_result) + { + std::cout << "Does not fulfill outside check" << std::endl; + } + m_result = false; + } + } +}; + +template +class border2_check +{ + Segment const& m_segment; + bool& m_result; + +public : + border2_check(Segment const& seg, bool& result) + : m_segment(seg) + , m_result(result) + {} + + inline border2_check operator=(border2_check const& input) + { + return border2_check(input.m_segment, input.m_result); + } + + template + inline void operator()(Segment2 const& segment) + { + // Create copies (TODO: find out why referring_segment does not compile) + typedef typename bg::point_type::type pt; + typedef bg::model::segment segment_type; + + typedef bg::strategy::intersection::relate_cartesian_segments + < + bg::policies::relate::segments_intersection_points + < + segment_type, + segment_type, + bg::segment_intersection_points + > + > policy; + + segment_type seg1, seg2; + bg::convert(m_segment, seg1); + bg::convert(segment, seg2); + bg::segment_intersection_points is = policy::apply(seg1, seg2); + + if (is.count == 2) + { + if (m_result) + { + std::cout << "Does not fulfill border2 check" << std::endl; + } + m_result = true; + } + } +}; + +template +class border_check +{ + Geometry const& m_geo; + bool& m_result; +public : + border_check(Geometry const& geo, bool& result) + : m_geo(geo) + , m_result(result) + {} + + inline border_check operator=(border_check const& input) + { + return border_check(input.m_geo, input.m_result); + } + + template + inline void operator()(Segment const& s) + { + bool on_border = false; + border2_check checker(s, on_border); + bg::for_each_segment(m_geo, checker); + + if (on_border) + { + if (m_result) + { + std::cout << "Does not fulfill border check" << std::endl; + } + m_result = false; + } + } +}; + + +template +bool verify(std::string const& caseid, MultiPolygon const& mp, Linestring const& ls, Settings const& settings) +{ + bg::model::multi_linestring difference, intersection; + bg::difference(ls, mp, difference); + bg::intersection(ls, mp, intersection); + + //typedef typename bg::length_result_type::type length_type; + + bool result = true; + + // 1) Check by length + typedef double length_type; + length_type len_input = bg::length(ls); + length_type len_difference = bg::length(difference); + length_type len_intersection = bg::length(intersection); + if (! bg::math::equals(len_input, len_difference + len_intersection)) + { + std::cout << "Input: " << len_input + << " difference: " << len_difference + << " intersection: " << len_intersection + << std::endl; + + std::cout << "Does not fulfill length check" << std::endl; + + result = false; + } + + // 2) Check by within and covered by + inside_check ic(mp, result); + bg::for_each_point(intersection, ic); + + outside_check oc(mp, result); + bg::for_each_point(difference, oc); + + border_check bc(mp, result); + bg::for_each_segment(difference, bc); + + // 3) check also the mid-points from the difference to remove false positives + BOOST_FOREACH(Linestring const& d, difference) + { + Linestring difference_midpoints; + bg::midpoints(d, false, std::back_inserter(difference_midpoints)); + outside_check ocm(mp, result); + bg::for_each_point(difference_midpoints, ocm); + } + + + bool svg = settings.svg; + bool wkt = settings.wkt; + if (! result) + { + std::cout << "ERROR " << caseid << std::endl; + std::cout << bg::wkt(mp) << std::endl; + std::cout << bg::wkt(ls) << std::endl; + svg = true; + wkt = true; + } + + if (svg) + { + std::ostringstream filename; + filename << caseid << "_" + << typeid(typename bg::coordinate_type::type).name() + << ".svg"; + create_svg(filename.str(), mp, ls, difference, intersection); + } + + if (wkt) + { + std::ostringstream filename; + filename << caseid << "_" + << typeid(typename bg::coordinate_type::type).name() + << ".wkt"; + std::ofstream stream(filename.str().c_str()); + stream << bg::wkt(mp) << std::endl; + stream << bg::wkt(ls) << std::endl; + } + + return result; +} + +template +bool test_linear_areal(MultiPolygon& result, int& index, + Generator& generator, + int level, common_settings const& settings) +{ + MultiPolygon p, q; + + // Generate two boxes + if (level == 0) + { + p.resize(1); + q.resize(1); + make_square_polygon(p.front(), generator, settings); + make_square_polygon(q.front(), generator, settings); + bg::correct(p); + bg::correct(q); + } + else + { + bg::correct(p); + bg::correct(q); + if (! test_linear_areal(p, index, generator, level - 1, settings) + || ! test_linear_areal(q, index, generator, level - 1, settings)) + { + return false; + } + } + + typedef typename boost::range_value::type polygon; + + MultiPolygon mp; + bg::detail::union_::union_insert + < + polygon + >(p, q, std::back_inserter(mp)); + + bg::unique(mp); + bg::simplify(mp, result, 0.01); + bg::correct(mp); + + // Generate a linestring + typedef typename bg::point_type::type point_type; + typedef bg::model::linestring linestring_type; + linestring_type ls; + make_random_linestring(ls, generator, settings); + + std::ostringstream out; + out << "recursive_la_" << index++ << "_" << level; + return verify(out.str(), mp, ls, settings); +} + + +template +void test_all(int seed, int count, int level, common_settings const& settings) +{ + boost::timer t; + + typedef boost::minstd_rand base_generator_type; + + base_generator_type generator(seed); + + boost::uniform_int<> random_coordinate(0, settings.field_size - 1); + boost::variate_generator > + coordinate_generator(generator, random_coordinate); + + typedef bg::model::polygon + < + bg::model::d2::point_xy, Clockwise, Closed + > polygon; + typedef bg::model::multi_polygon mp; + + + int index = 0; + for(int i = 0; i < count; i++) + { + mp p; + test_linear_areal(p, index, coordinate_generator, level, settings); + } + std::cout + << "geometries: " << index + << " type: " << typeid(T).name() + << " time: " << t.elapsed() << std::endl; +} + +int main(int argc, char** argv) +{ + try + { + namespace po = boost::program_options; + po::options_description description("=== recursive_polygons_linear_areal ===\nAllowed options"); + + int count = 1; + int seed = static_cast(std::time(0)); + int level = 3; + bool ccw = false; + bool open = false; + common_settings settings; + std::string form = "box"; + + description.add_options() + ("help", "Help message") + ("seed", po::value(&seed), "Initialization seed for random generator") + ("count", po::value(&count)->default_value(1), "Number of tests") + ("diff", po::value(&settings.also_difference)->default_value(false), "Include testing on difference") + ("level", po::value(&level)->default_value(3), "Level to reach (higher->slower)") + ("form", po::value(&form)->default_value("box"), "Form of the polygons (box, triangle)") + ("ccw", po::value(&ccw)->default_value(false), "Counter clockwise polygons") + ("open", po::value(&open)->default_value(false), "Open polygons") + ("size", po::value(&settings.field_size)->default_value(10), "Size of the field") + ("wkt", po::value(&settings.wkt)->default_value(false), "Create a WKT of the inputs, for all tests") + ("svg", po::value(&settings.svg)->default_value(false), "Create a SVG for all tests") + ; + + po::variables_map varmap; + po::store(po::parse_command_line(argc, argv, description), varmap); + po::notify(varmap); + + if (varmap.count("help") + || (form != "box" && form != "triangle")) + { + std::cout << description << std::endl; + return 1; + } + + settings.triangular = form != "box"; + + if (ccw && open) + { + test_all(seed, count, level, settings); + } + else if (ccw) + { + test_all(seed, count, level, settings); + } + else if (open) + { + test_all(seed, count, level, settings); + } + else + { + test_all(seed, count, level, settings); + } + +#if defined(HAVE_TTMATH) + // test_all(seed, count, max, svg, level); +#endif + } + catch(std::exception const& e) + { + std::cout << "Exception " << e.what() << std::endl; + } + catch(...) + { + std::cout << "Other exception" << std::endl; + } + + return 0; +} diff --git a/test/robustness/overlay/linear_areal/recursive_polygons_linear_areal.sln b/test/robustness/overlay/linear_areal/recursive_polygons_linear_areal.sln new file mode 100644 index 000000000..5865e1c36 --- /dev/null +++ b/test/robustness/overlay/linear_areal/recursive_polygons_linear_areal.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "recursive_polygons_linear_areal", "recursive_polygons_linear_areal.vcproj", "{1E269699-9450-4DD6-ACC3-C6A9AD2FC6E0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1E269699-9450-4DD6-ACC3-C6A9AD2FC6E0}.Debug|Win32.ActiveCfg = Debug|Win32 + {1E269699-9450-4DD6-ACC3-C6A9AD2FC6E0}.Debug|Win32.Build.0 = Debug|Win32 + {1E269699-9450-4DD6-ACC3-C6A9AD2FC6E0}.Release|Win32.ActiveCfg = Release|Win32 + {1E269699-9450-4DD6-ACC3-C6A9AD2FC6E0}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/robustness/overlay/linear_areal/recursive_polygons_linear_areal.vcproj b/test/robustness/overlay/linear_areal/recursive_polygons_linear_areal.vcproj new file mode 100644 index 000000000..14e8dd6fd --- /dev/null +++ b/test/robustness/overlay/linear_areal/recursive_polygons_linear_areal.vcproj @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/strategies/Jamfile.v2 b/test/strategies/Jamfile.v2 index 703ec3a8b..889ce66a9 100644 --- a/test/strategies/Jamfile.v2 +++ b/test/strategies/Jamfile.v2 @@ -15,6 +15,7 @@ test-suite boost-geometry-strategies [ run projected_point.cpp ] [ run pythagoras.cpp ] [ run spherical_side.cpp ] + [ run transform_cs.cpp ] [ run transformer.cpp ] [ run within.cpp ] ; diff --git a/test/strategies/projected_point.cpp b/test/strategies/projected_point.cpp index e9f65ca46..11ea749a1 100644 --- a/test/strategies/projected_point.cpp +++ b/test/strategies/projected_point.cpp @@ -87,33 +87,62 @@ void test_services() } -template -void test_all_2d() +template +void test_all_2d(std::string const& wkt_p, + std::string const& wkt_sp1, + std::string const& wkt_sp2, + T expected_distance) { P1 p; P2 sp1, sp2; - bg::read_wkt("POINT(1 1)", p); - bg::read_wkt("POINT(0 0)", sp1); - bg::read_wkt("POINT(2 3)", sp2); + bg::read_wkt(wkt_p, p); + bg::read_wkt(wkt_sp1, sp1); + bg::read_wkt(wkt_sp2, sp2); - typedef typename bg::strategy::distance::projected_point - < - P1, - P2 - > strategy_type; + { + typedef bg::strategy::distance::projected_point + < + P1, + P2 + > strategy_type; - BOOST_CONCEPT_ASSERT - ( - (bg::concept::PointSegmentDistanceStrategy) - ); + BOOST_CONCEPT_ASSERT + ( + (bg::concept::PointSegmentDistanceStrategy) + ); + strategy_type strategy; + typedef typename bg::strategy::distance::services::return_type::type return_type; + return_type d = strategy.apply(p, sp1, sp2); + BOOST_CHECK_CLOSE(d, expected_distance, 0.001); + } + + // Test combination with the comparable strategy + { + typedef bg::strategy::distance::projected_point + < + P1, + P2, + void, + bg::strategy::distance::comparable::pythagoras + > strategy_type; + strategy_type strategy; + typedef typename bg::strategy::distance::services::return_type::type return_type; + return_type d = strategy.apply(p, sp1, sp2); + T expected_squared_distance = expected_distance * expected_distance; + BOOST_CHECK_CLOSE(d, expected_squared_distance, 0.01); + } - strategy_type strategy; - typedef typename bg::strategy::distance::services::return_type::type return_type; - return_type d = strategy.apply(p, sp1, sp2); - BOOST_CHECK_CLOSE(d, return_type(0.27735203958327), 0.001); } +template +void test_all_2d() +{ + test_all_2d("POINT(1 1)", "POINT(0 0)", "POINT(2 3)", 0.27735203958327); + test_all_2d("POINT(2 2)", "POINT(1 4)", "POINT(4 1)", 0.5 * sqrt(2.0)); + test_all_2d("POINT(6 1)", "POINT(1 4)", "POINT(4 1)", 2.0); + test_all_2d("POINT(1 6)", "POINT(1 4)", "POINT(4 1)", 2.0); +} template void test_all_2d() diff --git a/test/strategies/strategies_tests.sln b/test/strategies/strategies_tests.sln index 226baff48..d29448213 100644 --- a/test/strategies/strategies_tests.sln +++ b/test/strategies/strategies_tests.sln @@ -18,6 +18,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spherical_side", "spherical EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "within", "within.vcproj", "{AB13D2AC-FD34-4DE4-BD8E-4D463050E5DD}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "transform_cs", "transform_cs.vcproj", "{2128A5D9-C67E-4C00-A917-A79058C78FCC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -60,6 +62,10 @@ Global {AB13D2AC-FD34-4DE4-BD8E-4D463050E5DD}.Debug|Win32.Build.0 = Debug|Win32 {AB13D2AC-FD34-4DE4-BD8E-4D463050E5DD}.Release|Win32.ActiveCfg = Release|Win32 {AB13D2AC-FD34-4DE4-BD8E-4D463050E5DD}.Release|Win32.Build.0 = Release|Win32 + {2128A5D9-C67E-4C00-A917-A79058C78FCC}.Debug|Win32.ActiveCfg = Debug|Win32 + {2128A5D9-C67E-4C00-A917-A79058C78FCC}.Debug|Win32.Build.0 = Debug|Win32 + {2128A5D9-C67E-4C00-A917-A79058C78FCC}.Release|Win32.ActiveCfg = Release|Win32 + {2128A5D9-C67E-4C00-A917-A79058C78FCC}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/strategies/transform_cs.cpp b/test/strategies/transform_cs.cpp new file mode 100644 index 000000000..36b327e3e --- /dev/null +++ b/test/strategies/transform_cs.cpp @@ -0,0 +1,149 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// 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 +#include + +template +inline T check_distance(P const& p) +{ + T x = bg::get<0>(p); + T y = bg::get<1>(p); + T z = bg::get<2>(p); + return sqrt(x * x + y * y + z * z); +} + +template +void test_transformations_spherical() +{ + T const input_long = 15.0; + T const input_lat = 5.0; + + T const expected_long = 0.26179938779914943653855361527329; + T const expected_lat = 0.08726646259971647884618453842443; + + // Can be checked using http://www.calc3d.com/ejavascriptcoordcalc.html + // (for phi use long, in radians, for theta use lat, in radians, they are listed there as "theta, phi") + T const expected_polar_x = 0.084186; + T const expected_polar_y = 0.0225576; + T const expected_polar_z = 0.996195; + + // Can be checked with same URL using 90-theta for lat. + // So for theta use 85 degrees, in radians: 0.08726646259971647884618453842443 + T const expected_equatorial_x = 0.962250; + T const expected_equatorial_y = 0.257834; + T const expected_equatorial_z = 0.0871557; + + // 1: Spherical-polar (lat=5, so it is near the pole - on a unit sphere) + bg::model::point > sp(input_long, input_lat); + + // 1a: to radian + bg::model::point > spr; + bg::transform(sp, spr); + BOOST_CHECK_CLOSE(bg::get<0>(spr), expected_long, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(spr), expected_lat, 0.001); + + // 1b: to cartesian-3d + bg::model::point pc3; + bg::transform(sp, pc3); + BOOST_CHECK_CLOSE(bg::get<0>(pc3), expected_polar_x, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(pc3), expected_polar_y, 0.001); + BOOST_CHECK_CLOSE(bg::get<2>(pc3), expected_polar_z, 0.001); + BOOST_CHECK_CLOSE(check_distance(pc3), 1.0, 0.001); + + // 1c: back + bg::transform(pc3, spr); + BOOST_CHECK_CLOSE(bg::get<0>(spr), expected_long, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(spr), expected_lat, 0.001); + + // 2: Spherical-equatorial (lat=5, so it is near the equator) + bg::model::point > se(input_long, input_lat); + + // 2a: to radian + bg::model::point > ser; + bg::transform(se, ser); + BOOST_CHECK_CLOSE(bg::get<0>(ser), expected_long, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(ser), expected_lat, 0.001); + + bg::transform(se, pc3); + BOOST_CHECK_CLOSE(bg::get<0>(pc3), expected_equatorial_x, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(pc3), expected_equatorial_y, 0.001); + BOOST_CHECK_CLOSE(bg::get<2>(pc3), expected_equatorial_z, 0.001); + BOOST_CHECK_CLOSE(check_distance(pc3), 1.0, 0.001); + + // 2c: back + bg::transform(pc3, ser); + BOOST_CHECK_CLOSE(bg::get<0>(spr), expected_long, 0.001); // expected_long + BOOST_CHECK_CLOSE(bg::get<1>(spr), expected_lat, 0.001); // expected_lat + + + // 3: Spherical-polar including radius + bg::model::point > sp3(input_long, input_lat, 0.5); + + // 3a: to radian + bg::model::point > spr3; + bg::transform(sp3, spr3); + BOOST_CHECK_CLOSE(bg::get<0>(spr3), expected_long, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(spr3), expected_lat, 0.001); + BOOST_CHECK_CLOSE(bg::get<2>(spr3), 0.5, 0.001); + + // 3b: to cartesian-3d + bg::transform(sp3, pc3); + BOOST_CHECK_CLOSE(bg::get<0>(pc3), expected_polar_x / 2.0, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(pc3), expected_polar_y / 2.0, 0.001); + BOOST_CHECK_CLOSE(bg::get<2>(pc3), expected_polar_z / 2.0, 0.001); + BOOST_CHECK_CLOSE(check_distance(pc3), 0.5, 0.001); + + // 3c: back + bg::transform(pc3, spr3); + BOOST_CHECK_CLOSE(bg::get<0>(spr3), expected_long, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(spr3), expected_lat, 0.001); + BOOST_CHECK_CLOSE(bg::get<2>(spr3), 0.5, 0.001); + + + // 4: Spherical-equatorial including radius + bg::model::point > se3(input_long, input_lat, 0.5); + + // 4a: to radian + bg::model::point > ser3; + bg::transform(se3, ser3); + BOOST_CHECK_CLOSE(bg::get<0>(ser3), expected_long, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(ser3), expected_lat, 0.001); + BOOST_CHECK_CLOSE(bg::get<2>(ser3), 0.5, 0.001); + + // 4b: to cartesian-3d + bg::transform(se3, pc3); + BOOST_CHECK_CLOSE(bg::get<0>(pc3), expected_equatorial_x / 2.0, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(pc3), expected_equatorial_y / 2.0, 0.001); + BOOST_CHECK_CLOSE(bg::get<2>(pc3), expected_equatorial_z / 2.0, 0.001); + BOOST_CHECK_CLOSE(check_distance(pc3), 0.5, 0.001); + + // 4c: back + bg::transform(pc3, ser3); + BOOST_CHECK_CLOSE(bg::get<0>(ser3), expected_long, 0.001); + BOOST_CHECK_CLOSE(bg::get<1>(ser3), expected_lat, 0.001); + BOOST_CHECK_CLOSE(bg::get<2>(ser3), 0.5, 0.001); +} + +int test_main(int, char* []) +{ + test_transformations_spherical(); + + return 0; +} diff --git a/test/strategies/transform_cs.vcproj b/test/strategies/transform_cs.vcproj new file mode 100644 index 000000000..ea115c56f --- /dev/null +++ b/test/strategies/transform_cs.vcproj @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/views/closeable_view.cpp b/test/views/closeable_view.cpp index 5afaa05c6..dde61a067 100644 --- a/test/views/closeable_view.cpp +++ b/test/views/closeable_view.cpp @@ -76,7 +76,7 @@ void test_non_geometry() it += 2; BOOST_CHECK_EQUAL(*it, 1); - BOOST_CHECK_EQUAL(boost::size(view), 4); + BOOST_CHECK_EQUAL(boost::size(view), 4u); }