Skip to content

Commit 705efe9

Browse files
Merge pull request #780 from barendgehrels/fix/sort-by-side-point-at-turn
Fix/sort by side point at turn
2 parents 421f4de + 59e0840 commit 705efe9

File tree

17 files changed

+283
-83
lines changed

17 files changed

+283
-83
lines changed

include/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
#include <boost/geometry/core/static_assert.hpp>
3030
#include <boost/geometry/core/tags.hpp>
3131
#include <boost/geometry/geometries/concepts/check.hpp>
32-
#include <boost/geometry/iterators/ever_circling_iterator.hpp>
3332
#include <boost/geometry/util/range.hpp>
3433
#include <boost/geometry/views/closeable_view.hpp>
3534
#include <boost/geometry/views/reversible_view.hpp>
@@ -43,12 +42,22 @@ namespace boost { namespace geometry
4342
namespace detail { namespace copy_segments
4443
{
4544

45+
inline signed_size_type circular_offset(signed_size_type segment_count, signed_size_type index,
46+
signed_size_type offset)
47+
{
48+
signed_size_type result = (index + offset) % segment_count;
49+
if (result < 0)
50+
{
51+
result += segment_count;
52+
}
53+
return result;
54+
}
4655

4756
template <typename Range, bool Reverse, typename SegmentIdentifier, typename PointOut>
4857
struct copy_segment_point_range
4958
{
5059
static inline bool apply(Range const& range,
51-
SegmentIdentifier const& seg_id, int offset,
60+
SegmentIdentifier const& seg_id, signed_size_type offset,
5261
PointOut& point)
5362
{
5463
typedef typename closeable_view
@@ -66,15 +75,13 @@ struct copy_segment_point_range
6675
cview_type cview(range);
6776
rview_type view(cview);
6877

69-
typedef typename boost::range_iterator<rview_type>::type iterator;
70-
geometry::ever_circling_iterator<iterator> it(boost::begin(view), boost::end(view),
71-
boost::begin(view) + seg_id.segment_index, true);
78+
std::size_t const segment_count = boost::size(view) - 1;
79+
signed_size_type const target = circular_offset(segment_count, seg_id.segment_index, offset);
7280

73-
for (signed_size_type i = 0; i < offset; ++i, ++it)
74-
{
75-
}
81+
BOOST_GEOMETRY_ASSERT(target >= 0);
82+
BOOST_GEOMETRY_ASSERT(target < boost::size(view));
83+
geometry::convert(range::at(view, target), point);
7684

77-
geometry::convert(*it, point);
7885
return true;
7986
}
8087
};
@@ -84,7 +91,7 @@ template <typename Polygon, bool Reverse, typename SegmentIdentifier, typename P
8491
struct copy_segment_point_polygon
8592
{
8693
static inline bool apply(Polygon const& polygon,
87-
SegmentIdentifier const& seg_id, int offset,
94+
SegmentIdentifier const& seg_id, signed_size_type offset,
8895
PointOut& point)
8996
{
9097
// Call ring-version with the right ring
@@ -110,18 +117,17 @@ template <typename Box, bool Reverse, typename SegmentIdentifier, typename Point
110117
struct copy_segment_point_box
111118
{
112119
static inline bool apply(Box const& box,
113-
SegmentIdentifier const& seg_id, int offset,
120+
SegmentIdentifier const& seg_id, signed_size_type offset,
114121
PointOut& point)
115122
{
116-
signed_size_type index = seg_id.segment_index;
117-
for (int i = 0; i < offset; i++)
118-
{
119-
index++;
120-
}
121-
122123
boost::array<typename point_type<Box>::type, 4> bp;
123124
assign_box_corners_oriented<Reverse>(box, bp);
124-
point = bp[index % 4];
125+
126+
signed_size_type const target = circular_offset(4, seg_id.segment_index, offset);
127+
BOOST_GEOMETRY_ASSERT(target >= 0);
128+
BOOST_GEOMETRY_ASSERT(target < bp.size());
129+
130+
point = bp[target];
125131
return true;
126132
}
127133
};
@@ -137,10 +143,9 @@ template
137143
struct copy_segment_point_multi
138144
{
139145
static inline bool apply(MultiGeometry const& multi,
140-
SegmentIdentifier const& seg_id, int offset,
146+
SegmentIdentifier const& seg_id, signed_size_type offset,
141147
PointOut& point)
142148
{
143-
144149
BOOST_GEOMETRY_ASSERT
145150
(
146151
seg_id.multi_index >= 0
@@ -278,16 +283,13 @@ struct copy_segment_point
278283
#endif // DOXYGEN_NO_DISPATCH
279284

280285

281-
282-
283-
284286
/*!
285287
\brief Helper function, copies a point from a segment
286288
\ingroup overlay
287289
*/
288290
template<bool Reverse, typename Geometry, typename SegmentIdentifier, typename PointOut>
289291
inline bool copy_segment_point(Geometry const& geometry,
290-
SegmentIdentifier const& seg_id, int offset,
292+
SegmentIdentifier const& seg_id, signed_size_type offset,
291293
PointOut& point_out)
292294
{
293295
concepts::check<Geometry const>();
@@ -316,7 +318,7 @@ template
316318
typename PointOut
317319
>
318320
inline bool copy_segment_point(Geometry1 const& geometry1, Geometry2 const& geometry2,
319-
SegmentIdentifier const& seg_id, int offset,
321+
SegmentIdentifier const& seg_id, signed_size_type offset,
320322
PointOut& point_out)
321323
{
322324
concepts::check<Geometry1 const>();
@@ -399,7 +401,6 @@ inline bool copy_segment_points(Geometry1 const& geometry1, Geometry2 const& geo
399401
}
400402

401403

402-
403404
}} // namespace boost::geometry
404405

405406
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP

include/boost/geometry/algorithms/detail/overlay/get_ring.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,12 @@ struct get_ring<multi_polygon_tag>
119119

120120

121121
template <typename Geometry>
122-
inline std::size_t segment_count_on_ring(Geometry const& geometry,
123-
segment_identifier const& seg_id)
122+
inline std::size_t segment_count_on_ring(Geometry const& geometry, segment_identifier const& seg_id)
124123
{
125124
typedef typename geometry::tag<Geometry>::type tag;
126125
ring_identifier const rid(0, seg_id.multi_index, seg_id.ring_index);
127126
// A closed polygon, a triangle of 4 points, including starting point,
128-
// contains 3 segments. So handle as if closed and subtract one.
127+
// contains 3 segments. So handle as if it is closed, and subtract one.
129128
return geometry::num_points(detail::overlay::get_ring<tag>::apply(rid, geometry), true) - 1;
130129
}
131130

include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ inline bool fill_sbs(Sbs& sbs, Point& turn_point,
831831
}
832832
for (int i = 0; i < 2; i++)
833833
{
834-
sbs.add(turn.operations[i], turn_index, i, geometry1, geometry2, first);
834+
sbs.add(turn, turn.operations[i], turn_index, i, geometry1, geometry2, first);
835835
first = false;
836836
}
837837
}

include/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ namespace boost { namespace geometry
2727
{
2828

2929

30-
3130
// Internal struct to uniquely identify a segment
3231
// on a linestring,ring
3332
// or polygon (needs ring_index)
3433
// or multi-geometry (needs multi_index)
34+
// It is always used for clockwise indication (even if the original is anticlockwise)
3535
struct segment_identifier
3636
{
3737
inline segment_identifier()

include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp

Lines changed: 102 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
2626

2727
#include <boost/geometry/util/condition.hpp>
28+
#include <boost/geometry/util/math.hpp>
29+
#include <boost/geometry/util/select_coordinate_type.hpp>
30+
#include <boost/geometry/util/select_most_precise.hpp>
2831

2932
namespace boost { namespace geometry
3033
{
@@ -66,6 +69,8 @@ struct ranked_point
6669
, seg_id(si)
6770
{}
6871

72+
using point_type = Point;
73+
6974
Point point;
7075
rank_type rank;
7176
signed_size_type zone; // index of closed zone, in uu turn there would be 2 zones
@@ -252,59 +257,119 @@ public :
252257
, m_strategy(strategy)
253258
{}
254259

260+
template <typename Operation>
255261
void add_segment_from(signed_size_type turn_index, int op_index,
256262
Point const& point_from,
257-
operation_type op, segment_identifier const& si,
263+
Operation const& op,
258264
bool is_origin)
259265
{
260-
m_ranked_points.push_back(rp(point_from, turn_index, op_index, dir_from, op, si));
266+
m_ranked_points.push_back(rp(point_from, turn_index, op_index,
267+
dir_from, op.operation, op.seg_id));
261268
if (is_origin)
262269
{
263270
m_origin = point_from;
264271
m_origin_count++;
265272
}
266273
}
267274

275+
template <typename Operation>
268276
void add_segment_to(signed_size_type turn_index, int op_index,
269277
Point const& point_to,
270-
operation_type op, segment_identifier const& si)
278+
Operation const& op)
271279
{
272-
m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op, si));
280+
m_ranked_points.push_back(rp(point_to, turn_index, op_index,
281+
dir_to, op.operation, op.seg_id));
273282
}
274283

284+
template <typename Operation>
275285
void add_segment(signed_size_type turn_index, int op_index,
276286
Point const& point_from, Point const& point_to,
277-
operation_type op, segment_identifier const& si,
278-
bool is_origin)
287+
Operation const& op, bool is_origin)
288+
{
289+
add_segment_from(turn_index, op_index, point_from, op, is_origin);
290+
add_segment_to(turn_index, op_index, point_to, op);
291+
}
292+
293+
// Returns true if two points are approximately equal, tuned by a giga-epsilon constant
294+
// (if constant is 1.0, for type double, the boundary is about 1.0e-7)
295+
template <typename Point1, typename Point2, typename T>
296+
static inline bool approximately_equals(Point1 const& a, Point2 const& b,
297+
T const& limit_giga_epsilon)
279298
{
280-
add_segment_from(turn_index, op_index, point_from, op, si, is_origin);
281-
add_segment_to(turn_index, op_index, point_to, op, si);
299+
// Including distance would introduce cyclic dependencies.
300+
using coor_t = typename select_coordinate_type<Point1, Point2>::type;
301+
using calc_t = typename geometry::select_most_precise <coor_t, T>::type;
302+
constexpr calc_t machine_giga_epsilon = 1.0e9 * std::numeric_limits<calc_t>::epsilon();
303+
304+
calc_t const& a0 = geometry::get<0>(a);
305+
calc_t const& b0 = geometry::get<0>(b);
306+
calc_t const& a1 = geometry::get<1>(a);
307+
calc_t const& b1 = geometry::get<1>(b);
308+
calc_t const one = 1.0;
309+
calc_t const c = math::detail::greatest(a0, b0, a1, b1, one);
310+
311+
// The maximum limit is avoid, for floating point, large limits like 400
312+
// (which are be calculated using eps)
313+
constexpr calc_t maxlimit = 1.0e-3;
314+
auto const limit = (std::min)(maxlimit, limit_giga_epsilon * machine_giga_epsilon * c);
315+
return std::abs(a0 - b0) <= limit && std::abs(a1 - b1) <= limit;
282316
}
283317

284318
template <typename Operation, typename Geometry1, typename Geometry2>
285-
Point add(Operation const& op, signed_size_type turn_index, int op_index,
319+
static Point walk_over_ring(Operation const& op, int offset,
320+
Geometry1 const& geometry1,
321+
Geometry2 const& geometry2)
322+
{
323+
Point point;
324+
geometry::copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, op.seg_id, offset, point);
325+
return point;
326+
}
327+
328+
template <typename Turn, typename Operation, typename Geometry1, typename Geometry2>
329+
Point add(Turn const& turn, Operation const& op, signed_size_type turn_index, int op_index,
286330
Geometry1 const& geometry1,
287331
Geometry2 const& geometry2,
288332
bool is_origin)
289333
{
290-
Point point1, point2, point3;
334+
Point point_from, point2, point3;
291335
geometry::copy_segment_points<Reverse1, Reverse2>(geometry1, geometry2,
292-
op.seg_id, point1, point2, point3);
293-
Point const& point_to = op.fraction.is_one() ? point3 : point2;
294-
add_segment(turn_index, op_index, point1, point_to, op.operation, op.seg_id, is_origin);
295-
return point1;
336+
op.seg_id, point_from, point2, point3);
337+
Point point_to = op.fraction.is_one() ? point3 : point2;
338+
339+
340+
// If the point is in the neighbourhood (the limit itself is not important),
341+
// then take a point (or more) further back.
342+
// The limit of offset avoids theoretical infinite loops. In practice it currently
343+
// walks max 1 point back in all cases.
344+
int offset = 0;
345+
while (approximately_equals(point_from, turn.point, 1.0) && offset > -10)
346+
{
347+
point_from = walk_over_ring(op, --offset, geometry1, geometry2);
348+
}
349+
350+
// Similarly for the point to, walk forward
351+
offset = 0;
352+
while (approximately_equals(point_to, turn.point, 1.0) && offset < 10)
353+
{
354+
point_to = walk_over_ring(op, ++offset, geometry1, geometry2);
355+
}
356+
357+
add_segment(turn_index, op_index, point_from, point_to, op, is_origin);
358+
359+
return point_from;
296360
}
297361

298-
template <typename Operation, typename Geometry1, typename Geometry2>
299-
void add(Operation const& op, signed_size_type turn_index, int op_index,
362+
template <typename Turn, typename Operation, typename Geometry1, typename Geometry2>
363+
void add(Turn const& turn,
364+
Operation const& op, signed_size_type turn_index, int op_index,
300365
segment_identifier const& departure_seg_id,
301366
Geometry1 const& geometry1,
302367
Geometry2 const& geometry2,
303-
bool check_origin)
368+
bool is_departure)
304369
{
305-
Point const point1 = add(op, turn_index, op_index, geometry1, geometry2, false);
370+
Point potential_origin = add(turn, op, turn_index, op_index, geometry1, geometry2, false);
306371

307-
if (check_origin)
372+
if (is_departure)
308373
{
309374
bool const is_origin
310375
= op.seg_id.source_index == departure_seg_id.source_index
@@ -317,33 +382,41 @@ public :
317382
if (m_origin_count == 0 ||
318383
segment_distance < m_origin_segment_distance)
319384
{
320-
m_origin = point1;
385+
m_origin = potential_origin;
321386
m_origin_segment_distance = segment_distance;
322387
}
323388
m_origin_count++;
324389
}
325390
}
326391
}
327392

393+
template <typename Operation, typename Geometry1, typename Geometry2>
394+
static signed_size_type segment_count_on_ring(Operation const& op,
395+
Geometry1 const& geometry1,
396+
Geometry2 const& geometry2)
397+
{
398+
// Take wrap into account
399+
// Suppose point_count=10 (10 points, 9 segments), dep.seg_id=7, op.seg_id=2,
400+
// then distance=9-7+2=4, being segments 7,8,0,1
401+
return op.seg_id.source_index == 0
402+
? detail::overlay::segment_count_on_ring(geometry1, op.seg_id)
403+
: detail::overlay::segment_count_on_ring(geometry2, op.seg_id);
404+
}
405+
328406
template <typename Operation, typename Geometry1, typename Geometry2>
329407
static signed_size_type calculate_segment_distance(Operation const& op,
330408
segment_identifier const& departure_seg_id,
331409
Geometry1 const& geometry1,
332410
Geometry2 const& geometry2)
333411
{
412+
BOOST_ASSERT(op.seg_id.source_index == departure_seg_id.source_index);
413+
signed_size_type result = op.seg_id.segment_index - departure_seg_id.segment_index;
334414
if (op.seg_id.segment_index >= departure_seg_id.segment_index)
335415
{
336416
// dep.seg_id=5, op.seg_id=7, distance=2, being segments 5,6
337-
return op.seg_id.segment_index - departure_seg_id.segment_index;
417+
return result;
338418
}
339-
// Take wrap into account
340-
// Suppose point_count=10 (10 points, 9 segments), dep.seg_id=7, op.seg_id=2,
341-
// then distance=9-7+2=4, being segments 7,8,0,1
342-
std::size_t const segment_count
343-
= op.seg_id.source_index == 0
344-
? segment_count_on_ring(geometry1, op.seg_id)
345-
: segment_count_on_ring(geometry2, op.seg_id);
346-
return segment_count - departure_seg_id.segment_index + op.seg_id.segment_index;
419+
return segment_count_on_ring(op, geometry1, geometry2) + result;
347420
}
348421

349422
void apply(Point const& turn_point)

0 commit comments

Comments
 (0)