From 08c1e93fa36a49f49325a07fe91ff92c964c2b6c Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Thu, 11 Dec 2014 18:55:56 +0900 Subject: Imported Upstream version 1.57.0 --- boost/geometry/algorithms/append.hpp | 175 +- boost/geometry/algorithms/area.hpp | 182 +- boost/geometry/algorithms/assign.hpp | 250 ++- boost/geometry/algorithms/buffer.hpp | 183 +- boost/geometry/algorithms/centroid.hpp | 434 ++-- boost/geometry/algorithms/clear.hpp | 59 +- boost/geometry/algorithms/comparable_distance.hpp | 69 +- boost/geometry/algorithms/convert.hpp | 201 +- boost/geometry/algorithms/convex_hull.hpp | 262 ++- boost/geometry/algorithms/correct.hpp | 100 +- boost/geometry/algorithms/covered_by.hpp | 414 +++- boost/geometry/algorithms/crosses.hpp | 194 ++ .../algorithms/detail/assign_box_corners.hpp | 22 +- .../algorithms/detail/assign_indexed_point.hpp | 2 +- boost/geometry/algorithms/detail/assign_values.hpp | 164 +- .../algorithms/detail/buffer/buffer_inserter.hpp | 940 +++++++++ .../algorithms/detail/buffer/buffer_policies.hpp | 171 ++ .../detail/buffer/buffered_piece_collection.hpp | 954 +++++++++ .../algorithms/detail/buffer/buffered_ring.hpp | 238 +++ .../algorithms/detail/buffer/get_piece_turns.hpp | 191 ++ .../detail/buffer/line_line_intersection.hpp | 88 + .../algorithms/detail/buffer/parallel_continue.hpp | 33 + .../algorithms/detail/buffer/turn_in_input.hpp | 98 + .../detail/buffer/turn_in_piece_visitor.hpp | 246 +++ .../geometry/algorithms/detail/calculate_null.hpp | 2 +- boost/geometry/algorithms/detail/calculate_sum.hpp | 18 +- .../detail/centroid/translating_transformer.hpp | 119 ++ .../algorithms/detail/check_iterator_range.hpp | 71 + .../detail/closest_feature/geometry_to_range.hpp | 147 ++ .../detail/closest_feature/point_to_range.hpp | 250 +++ .../detail/closest_feature/range_to_range.hpp | 196 ++ .../detail/comparable_distance/implementation.hpp | 24 + .../detail/comparable_distance/interface.hpp | 363 ++++ .../detail/convert_indexed_to_indexed.hpp | 20 +- boost/geometry/algorithms/detail/counting.hpp | 107 + boost/geometry/algorithms/detail/course.hpp | 56 + boost/geometry/algorithms/detail/disjoint.hpp | 225 -- .../algorithms/detail/disjoint/areal_areal.hpp | 134 ++ .../algorithms/detail/disjoint/box_box.hpp | 114 + .../algorithms/detail/disjoint/implementation.hpp | 36 + .../algorithms/detail/disjoint/interface.hpp | 187 ++ .../algorithms/detail/disjoint/linear_areal.hpp | 244 +++ .../algorithms/detail/disjoint/linear_linear.hpp | 174 ++ .../detail/disjoint/linear_segment_or_box.hpp | 195 ++ .../algorithms/detail/disjoint/point_box.hpp | 94 + .../algorithms/detail/disjoint/point_geometry.hpp | 111 + .../algorithms/detail/disjoint/point_point.hpp | 112 + .../algorithms/detail/disjoint/segment_box.hpp | 291 +++ .../detail/distance/backward_compatibility.hpp | 333 +++ .../algorithms/detail/distance/box_to_box.hpp | 60 + .../detail/distance/default_strategies.hpp | 137 ++ .../detail/distance/geometry_to_segment_or_box.hpp | 462 ++++ .../algorithms/detail/distance/implementation.hpp | 35 + .../algorithms/detail/distance/interface.hpp | 403 ++++ .../algorithms/detail/distance/is_comparable.hpp | 45 + .../detail/distance/iterator_selector.hpp | 70 + .../detail/distance/linear_or_areal_to_areal.hpp | 147 ++ .../detail/distance/linear_to_linear.hpp | 123 ++ .../detail/distance/multipoint_to_geometry.hpp | 240 +++ .../detail/distance/point_to_geometry.hpp | 518 +++++ .../detail/distance/range_to_geometry_rtree.hpp | 131 ++ .../algorithms/detail/distance/segment_to_box.hpp | 886 ++++++++ .../detail/distance/segment_to_segment.hpp | 150 ++ .../algorithms/detail/equals/collect_vectors.hpp | 43 +- .../algorithms/detail/equals/point_point.hpp | 52 + .../geometry/algorithms/detail/extreme_points.hpp | 520 +++++ .../geometry/algorithms/detail/for_each_range.hpp | 116 +- .../geometry/algorithms/detail/get_left_turns.hpp | 487 ++--- boost/geometry/algorithms/detail/get_max_size.hpp | 64 + .../algorithms/detail/has_self_intersections.hpp | 53 +- .../algorithms/detail/interior_iterator.hpp | 71 + .../algorithms/detail/intersection/box_box.hpp | 54 + .../detail/intersection/implementation.hpp | 22 + .../algorithms/detail/intersection/interface.hpp | 309 +++ .../algorithms/detail/intersection/multi.hpp | 423 ++++ .../algorithms/detail/is_simple/always_simple.hpp | 83 + .../geometry/algorithms/detail/is_simple/areal.hpp | 141 ++ .../is_simple/debug_print_boundary_points.hpp | 82 + .../algorithms/detail/is_simple/implementation.hpp | 18 + .../algorithms/detail/is_simple/interface.hpp | 80 + .../algorithms/detail/is_simple/linear.hpp | 248 +++ .../algorithms/detail/is_simple/multipoint.hpp | 84 + boost/geometry/algorithms/detail/is_valid/box.hpp | 86 + .../detail/is_valid/complement_graph.hpp | 239 +++ .../detail/is_valid/debug_complement_graph.hpp | 70 + .../detail/is_valid/debug_print_turns.hpp | 64 + .../detail/is_valid/debug_validity_phase.hpp | 68 + .../algorithms/detail/is_valid/has_duplicates.hpp | 70 + .../algorithms/detail/is_valid/has_spikes.hpp | 139 ++ .../detail/is_valid/has_valid_self_turns.hpp | 93 + .../algorithms/detail/is_valid/implementation.hpp | 21 + .../algorithms/detail/is_valid/interface.hpp | 78 + .../detail/is_valid/is_acceptable_turn.hpp | 144 ++ .../geometry/algorithms/detail/is_valid/linear.hpp | 125 ++ .../algorithms/detail/is_valid/multipolygon.hpp | 297 +++ .../algorithms/detail/is_valid/pointlike.hpp | 62 + .../algorithms/detail/is_valid/polygon.hpp | 376 ++++ boost/geometry/algorithms/detail/is_valid/ring.hpp | 173 ++ .../algorithms/detail/is_valid/segment.hpp | 61 + boost/geometry/algorithms/detail/multi_modify.hpp | 53 + .../detail/multi_modify_with_predicate.hpp | 52 + boost/geometry/algorithms/detail/multi_sum.hpp | 52 + .../detail/num_distinct_consecutive_points.hpp | 93 + .../geometry/algorithms/detail/occupation_info.hpp | 356 +--- .../algorithms/detail/overlay/add_rings.hpp | 35 +- .../detail/overlay/append_no_duplicates.hpp | 4 +- .../detail/overlay/append_no_dups_or_spikes.hpp | 160 ++ .../algorithms/detail/overlay/assign_parents.hpp | 58 +- .../detail/overlay/backtrack_check_si.hpp | 23 +- .../detail/overlay/calculate_distance_policy.hpp | 64 - .../algorithms/detail/overlay/check_enrich.hpp | 2 +- .../algorithms/detail/overlay/convert_ring.hpp | 21 +- .../detail/overlay/copy_segment_point.hpp | 94 +- .../algorithms/detail/overlay/copy_segments.hpp | 320 +-- .../algorithms/detail/overlay/do_reverse.hpp | 47 + .../detail/overlay/enrich_intersection_points.hpp | 165 +- .../algorithms/detail/overlay/enrichment_info.hpp | 20 +- .../geometry/algorithms/detail/overlay/follow.hpp | 292 ++- .../detail/overlay/follow_linear_linear.hpp | 536 +++++ .../detail/overlay/get_intersection_points.hpp | 71 +- .../detail/overlay/get_relative_order.hpp | 13 +- .../algorithms/detail/overlay/get_ring.hpp | 37 +- .../algorithms/detail/overlay/get_turn_info.hpp | 528 +++-- .../detail/overlay/get_turn_info_for_endpoint.hpp | 657 ++++++ .../detail/overlay/get_turn_info_helpers.hpp | 329 +++ .../algorithms/detail/overlay/get_turn_info_la.hpp | 805 +++++++ .../algorithms/detail/overlay/get_turn_info_ll.hpp | 720 +++++++ .../algorithms/detail/overlay/get_turns.hpp | 422 ++-- .../detail/overlay/handle_tangencies.hpp | 347 +-- .../detail/overlay/intersection_box_box.hpp | 84 + .../detail/overlay/intersection_insert.hpp | 571 +++-- .../algorithms/detail/overlay/linear_linear.hpp | 326 +++ .../geometry/algorithms/detail/overlay/overlay.hpp | 76 +- .../detail/overlay/pointlike_pointlike.hpp | 435 ++++ .../detail/overlay/segment_identifier.hpp | 31 +- .../algorithms/detail/overlay/select_rings.hpp | 63 +- .../algorithms/detail/overlay/self_turn_points.hpp | 117 +- .../algorithms/detail/overlay/stream_info.hpp | 3 +- .../algorithms/detail/overlay/traversal_info.hpp | 16 +- .../algorithms/detail/overlay/traverse.hpp | 82 +- .../algorithms/detail/overlay/turn_info.hpp | 12 +- .../algorithms/detail/overlay/visit_info.hpp | 54 - boost/geometry/algorithms/detail/partition.hpp | 101 +- .../algorithms/detail/point_is_spike_or_equal.hpp | 126 ++ .../geometry/algorithms/detail/point_on_border.hpp | 64 +- boost/geometry/algorithms/detail/recalculate.hpp | 231 ++ .../algorithms/detail/relate/areal_areal.hpp | 823 ++++++++ .../algorithms/detail/relate/boundary_checker.hpp | 134 ++ .../algorithms/detail/relate/follow_helpers.hpp | 401 ++++ boost/geometry/algorithms/detail/relate/less.hpp | 79 + .../algorithms/detail/relate/linear_areal.hpp | 1115 ++++++++++ .../algorithms/detail/relate/linear_linear.hpp | 768 +++++++ .../algorithms/detail/relate/point_geometry.hpp | 205 ++ .../algorithms/detail/relate/point_point.hpp | 242 +++ boost/geometry/algorithms/detail/relate/relate.hpp | 339 +++ boost/geometry/algorithms/detail/relate/result.hpp | 1390 ++++++++++++ .../algorithms/detail/relate/topology_check.hpp | 241 +++ boost/geometry/algorithms/detail/relate/turns.hpp | 253 +++ .../geometry/algorithms/detail/ring_identifier.hpp | 18 +- .../detail/sections/range_by_section.hpp | 65 +- .../algorithms/detail/sections/sectionalize.hpp | 450 ++-- .../algorithms/detail/signed_index_type.hpp | 29 + .../geometry/algorithms/detail/single_geometry.hpp | 95 + boost/geometry/algorithms/detail/sub_range.hpp | 113 + .../algorithms/detail/throw_on_empty_input.hpp | 8 +- .../algorithms/detail/turns/compare_turns.hpp | 113 + .../algorithms/detail/turns/debug_turn.hpp | 65 + .../detail/turns/filter_continue_turns.hpp | 78 + .../algorithms/detail/turns/print_turns.hpp | 96 + .../detail/turns/remove_duplicate_turns.hpp | 62 + .../algorithms/detail/within/point_in_geometry.hpp | 463 ++++ .../algorithms/detail/within/within_no_turns.hpp | 221 ++ boost/geometry/algorithms/difference.hpp | 45 +- boost/geometry/algorithms/disjoint.hpp | 298 +-- boost/geometry/algorithms/dispatch/disjoint.hpp | 70 + boost/geometry/algorithms/dispatch/distance.hpp | 82 + boost/geometry/algorithms/dispatch/is_simple.hpp | 38 + boost/geometry/algorithms/dispatch/is_valid.hpp | 46 + boost/geometry/algorithms/distance.hpp | 587 +----- boost/geometry/algorithms/envelope.hpp | 249 ++- boost/geometry/algorithms/equals.hpp | 361 +++- boost/geometry/algorithms/expand.hpp | 158 +- boost/geometry/algorithms/for_each.hpp | 339 +-- boost/geometry/algorithms/intersection.hpp | 229 +- boost/geometry/algorithms/intersects.hpp | 44 +- boost/geometry/algorithms/is_simple.hpp | 16 + boost/geometry/algorithms/is_valid.hpp | 16 + boost/geometry/algorithms/length.hpp | 161 +- boost/geometry/algorithms/not_implemented.hpp | 1 - boost/geometry/algorithms/num_geometries.hpp | 103 +- boost/geometry/algorithms/num_interior_rings.hpp | 88 +- boost/geometry/algorithms/num_points.hpp | 210 +- boost/geometry/algorithms/num_segments.hpp | 204 ++ boost/geometry/algorithms/overlaps.hpp | 64 +- boost/geometry/algorithms/perimeter.hpp | 192 +- boost/geometry/algorithms/point_on_surface.hpp | 327 +++ boost/geometry/algorithms/remove_spikes.hpp | 280 +++ boost/geometry/algorithms/reverse.hpp | 109 +- boost/geometry/algorithms/simplify.hpp | 410 ++-- boost/geometry/algorithms/sym_difference.hpp | 56 +- boost/geometry/algorithms/touches.hpp | 585 +++++- boost/geometry/algorithms/transform.hpp | 251 ++- boost/geometry/algorithms/union.hpp | 263 ++- boost/geometry/algorithms/unique.hpp | 85 +- boost/geometry/algorithms/within.hpp | 515 +++-- boost/geometry/arithmetic/arithmetic.hpp | 27 +- boost/geometry/arithmetic/determinant.hpp | 2 +- boost/geometry/arithmetic/dot_product.hpp | 16 +- boost/geometry/core/access.hpp | 29 +- boost/geometry/core/closure.hpp | 52 +- boost/geometry/core/coordinate_dimension.hpp | 20 +- boost/geometry/core/coordinate_system.hpp | 8 +- boost/geometry/core/coordinate_type.hpp | 14 +- boost/geometry/core/cs.hpp | 12 +- boost/geometry/core/exterior_ring.hpp | 2 +- boost/geometry/core/geometry_id.hpp | 31 +- boost/geometry/core/interior_rings.hpp | 13 +- boost/geometry/core/is_areal.hpp | 5 +- boost/geometry/core/point_order.hpp | 41 +- boost/geometry/core/point_type.hpp | 36 +- boost/geometry/core/ring_type.hpp | 53 +- boost/geometry/core/tag.hpp | 5 +- boost/geometry/core/tag_cast.hpp | 4 +- boost/geometry/core/tags.hpp | 45 +- boost/geometry/core/topological_dimension.hpp | 13 + .../geometries/adapted/boost_polygon/ring.hpp | 19 + .../adapted/boost_polygon/ring_proxy.hpp | 2 +- boost/geometry/geometries/concepts/check.hpp | 110 +- .../concepts/multi_linestring_concept.hpp | 91 + .../geometries/concepts/multi_point_concept.hpp | 90 + .../geometries/concepts/multi_polygon_concept.hpp | 91 + boost/geometry/geometries/geometries.hpp | 4 + boost/geometry/geometries/multi_linestring.hpp | 80 + boost/geometry/geometries/multi_point.hpp | 94 + boost/geometry/geometries/multi_polygon.hpp | 77 + boost/geometry/geometries/point.hpp | 12 +- boost/geometry/geometries/pointing_segment.hpp | 143 ++ boost/geometry/geometries/register/box.hpp | 8 +- boost/geometry/geometries/register/linestring.hpp | 4 +- .../geometries/register/multi_linestring.hpp | 59 + boost/geometry/geometries/register/multi_point.hpp | 59 + .../geometry/geometries/register/multi_polygon.hpp | 59 + boost/geometry/geometries/register/ring.hpp | 4 +- boost/geometry/geometries/variant.hpp | 34 + boost/geometry/geometry.hpp | 24 +- boost/geometry/index/adaptors/query.hpp | 88 + boost/geometry/index/detail/algorithms/bounds.hpp | 90 + .../algorithms/comparable_distance_centroid.hpp | 77 + .../detail/algorithms/comparable_distance_far.hpp | 66 + .../detail/algorithms/comparable_distance_near.hpp | 77 + boost/geometry/index/detail/algorithms/content.hpp | 87 + .../geometry/index/detail/algorithms/diff_abs.hpp | 39 + .../detail/algorithms/intersection_content.hpp | 37 + .../geometry/index/detail/algorithms/is_valid.hpp | 88 + boost/geometry/index/detail/algorithms/margin.hpp | 169 ++ .../index/detail/algorithms/minmaxdist.hpp | 119 ++ .../index/detail/algorithms/path_intersection.hpp | 119 ++ .../detail/algorithms/segment_intersection.hpp | 146 ++ .../detail/algorithms/smallest_for_indexable.hpp | 80 + .../index/detail/algorithms/sum_for_indexable.hpp | 76 + .../index/detail/algorithms/union_content.hpp | 33 + boost/geometry/index/detail/assert.hpp | 30 + boost/geometry/index/detail/bounded_view.hpp | 185 ++ boost/geometry/index/detail/config_begin.hpp | 21 + boost/geometry/index/detail/config_end.hpp | 12 + .../geometry/index/detail/distance_predicates.hpp | 161 ++ boost/geometry/index/detail/exception.hpp | 72 + boost/geometry/index/detail/meta.hpp | 42 + boost/geometry/index/detail/predicates.hpp | 813 ++++++++ boost/geometry/index/detail/rtree/adaptors.hpp | 54 + .../geometry/index/detail/rtree/kmeans/kmeans.hpp | 16 + boost/geometry/index/detail/rtree/kmeans/split.hpp | 87 + .../geometry/index/detail/rtree/linear/linear.hpp | 16 + .../detail/rtree/linear/redistribute_elements.hpp | 446 ++++ .../index/detail/rtree/node/auto_deallocator.hpp | 38 + boost/geometry/index/detail/rtree/node/concept.hpp | 85 + .../index/detail/rtree/node/dynamic_visitor.hpp | 67 + boost/geometry/index/detail/rtree/node/node.hpp | 178 ++ .../index/detail/rtree/node/node_auto_ptr.hpp | 81 + .../index/detail/rtree/node/node_elements.hpp | 95 + boost/geometry/index/detail/rtree/node/pairs.hpp | 71 + .../index/detail/rtree/node/variant_dynamic.hpp | 275 +++ .../index/detail/rtree/node/variant_static.hpp | 194 ++ .../index/detail/rtree/node/variant_visitor.hpp | 66 + .../index/detail/rtree/node/weak_dynamic.hpp | 294 +++ .../index/detail/rtree/node/weak_static.hpp | 174 ++ .../index/detail/rtree/node/weak_visitor.hpp | 67 + boost/geometry/index/detail/rtree/options.hpp | 155 ++ boost/geometry/index/detail/rtree/pack_create.hpp | 376 ++++ .../index/detail/rtree/quadratic/quadratic.hpp | 16 + .../rtree/quadratic/redistribute_elements.hpp | 298 +++ .../index/detail/rtree/query_iterators.hpp | 599 ++++++ .../index/detail/rtree/rstar/choose_next_node.hpp | 233 +++ boost/geometry/index/detail/rtree/rstar/insert.hpp | 577 +++++ .../detail/rtree/rstar/redistribute_elements.hpp | 468 +++++ boost/geometry/index/detail/rtree/rstar/rstar.hpp | 18 + .../index/detail/rtree/utilities/are_boxes_ok.hpp | 142 ++ .../index/detail/rtree/utilities/are_levels_ok.hpp | 109 + .../index/detail/rtree/utilities/gl_draw.hpp | 243 +++ .../index/detail/rtree/utilities/print.hpp | 219 ++ .../index/detail/rtree/utilities/statistics.hpp | 105 + .../geometry/index/detail/rtree/utilities/view.hpp | 61 + .../index/detail/rtree/visitors/children_box.hpp | 55 + .../geometry/index/detail/rtree/visitors/copy.hpp | 92 + .../geometry/index/detail/rtree/visitors/count.hpp | 107 + .../index/detail/rtree/visitors/destroy.hpp | 70 + .../index/detail/rtree/visitors/distance_query.hpp | 580 ++++++ .../index/detail/rtree/visitors/insert.hpp | 527 +++++ .../index/detail/rtree/visitors/is_leaf.hpp | 41 + .../index/detail/rtree/visitors/remove.hpp | 326 +++ .../index/detail/rtree/visitors/spatial_query.hpp | 206 ++ boost/geometry/index/detail/serialization.hpp | 585 ++++++ boost/geometry/index/detail/tags.hpp | 25 + boost/geometry/index/detail/translator.hpp | 60 + boost/geometry/index/detail/tuples.hpp | 204 ++ boost/geometry/index/detail/utilities.hpp | 46 + boost/geometry/index/detail/varray.hpp | 2203 ++++++++++++++++++++ boost/geometry/index/detail/varray_detail.hpp | 756 +++++++ boost/geometry/index/distance_predicates.hpp | 204 ++ boost/geometry/index/equal_to.hpp | 249 +++ boost/geometry/index/indexable.hpp | 217 ++ boost/geometry/index/inserter.hpp | 78 + boost/geometry/index/parameters.hpp | 253 +++ boost/geometry/index/predicates.hpp | 385 ++++ boost/geometry/index/rtree.hpp | 1919 +++++++++++++++++ boost/geometry/io/dsv/write.hpp | 64 +- boost/geometry/io/svg/svg_mapper.hpp | 390 ++++ boost/geometry/io/svg/write_svg.hpp | 279 +++ boost/geometry/io/svg/write_svg_multi.hpp | 78 + boost/geometry/io/wkt/detail/prefix.hpp | 21 + boost/geometry/io/wkt/detail/wkt_multi.hpp | 3 +- boost/geometry/io/wkt/read.hpp | 169 +- boost/geometry/io/wkt/stream.hpp | 6 +- boost/geometry/io/wkt/write.hpp | 215 +- boost/geometry/iterators/closing_iterator.hpp | 5 +- boost/geometry/iterators/concatenate_iterator.hpp | 184 ++ .../detail/point_iterator/inner_range_type.hpp | 66 + .../detail/point_iterator/iterator_type.hpp | 136 ++ .../iterators/detail/point_iterator/value_type.hpp | 47 + .../detail/segment_iterator/iterator_type.hpp | 153 ++ .../segment_iterator/range_segment_iterator.hpp | 223 ++ .../detail/segment_iterator/value_type.hpp | 43 + .../geometry/iterators/dispatch/point_iterator.hpp | 47 + .../iterators/dispatch/segment_iterator.hpp | 47 + .../geometry/iterators/ever_circling_iterator.hpp | 4 +- boost/geometry/iterators/flatten_iterator.hpp | 259 +++ boost/geometry/iterators/has_one_element.hpp | 29 + boost/geometry/iterators/point_iterator.hpp | 315 +++ .../geometry/iterators/point_reverse_iterator.hpp | 98 + boost/geometry/iterators/segment_iterator.hpp | 317 +++ boost/geometry/multi/algorithms/append.hpp | 45 +- boost/geometry/multi/algorithms/area.hpp | 36 - boost/geometry/multi/algorithms/centroid.hpp | 156 -- boost/geometry/multi/algorithms/clear.hpp | 22 - boost/geometry/multi/algorithms/convert.hpp | 107 - boost/geometry/multi/algorithms/correct.hpp | 45 - boost/geometry/multi/algorithms/covered_by.hpp | 55 +- .../multi/algorithms/detail/extreme_points.hpp | 19 + .../multi/algorithms/detail/for_each_range.hpp | 65 - boost/geometry/multi/algorithms/detail/modify.hpp | 34 +- .../algorithms/detail/modify_with_predicate.hpp | 33 +- .../geometry/multi/algorithms/detail/multi_sum.hpp | 53 +- .../detail/overlay/copy_segment_point.hpp | 88 +- .../algorithms/detail/overlay/copy_segments.hpp | 88 - .../multi/algorithms/detail/overlay/get_ring.hpp | 38 - .../multi/algorithms/detail/overlay/get_turns.hpp | 95 - .../algorithms/detail/overlay/select_rings.hpp | 46 - .../algorithms/detail/overlay/self_turn_points.hpp | 40 - .../multi/algorithms/detail/point_on_border.hpp | 77 +- .../detail/sections/range_by_section.hpp | 74 +- .../algorithms/detail/sections/sectionalize.hpp | 77 +- boost/geometry/multi/algorithms/disjoint.hpp | 21 + boost/geometry/multi/algorithms/distance.hpp | 150 +- boost/geometry/multi/algorithms/envelope.hpp | 96 - boost/geometry/multi/algorithms/equals.hpp | 48 - boost/geometry/multi/algorithms/for_each.hpp | 108 - boost/geometry/multi/algorithms/intersection.hpp | 426 +--- boost/geometry/multi/algorithms/length.hpp | 36 - boost/geometry/multi/algorithms/num_geometries.hpp | 29 - .../multi/algorithms/num_interior_rings.hpp | 45 +- boost/geometry/multi/algorithms/num_points.hpp | 70 +- boost/geometry/multi/algorithms/perimeter.hpp | 35 - boost/geometry/multi/algorithms/remove_spikes.hpp | 19 + boost/geometry/multi/algorithms/reverse.hpp | 48 - boost/geometry/multi/algorithms/simplify.hpp | 96 - boost/geometry/multi/algorithms/transform.hpp | 81 - boost/geometry/multi/algorithms/unique.hpp | 81 - boost/geometry/multi/algorithms/within.hpp | 91 +- boost/geometry/multi/core/closure.hpp | 39 - boost/geometry/multi/core/geometry_id.hpp | 36 - boost/geometry/multi/core/interior_rings.hpp | 33 - boost/geometry/multi/core/is_areal.hpp | 21 - boost/geometry/multi/core/point_order.hpp | 36 - boost/geometry/multi/core/point_type.hpp | 42 - boost/geometry/multi/core/ring_type.hpp | 47 +- boost/geometry/multi/core/tags.hpp | 51 +- .../geometry/multi/core/topological_dimension.hpp | 30 - boost/geometry/multi/geometries/concepts/check.hpp | 61 - .../concepts/multi_linestring_concept.hpp | 66 +- .../geometries/concepts/multi_point_concept.hpp | 65 +- .../geometries/concepts/multi_polygon_concept.hpp | 66 +- .../geometry/multi/geometries/multi_geometries.hpp | 6 +- .../geometry/multi/geometries/multi_linestring.hpp | 67 +- boost/geometry/multi/geometries/multi_point.hpp | 75 +- boost/geometry/multi/geometries/multi_polygon.hpp | 59 +- .../multi/geometries/register/multi_linestring.hpp | 41 +- .../multi/geometries/register/multi_point.hpp | 41 +- .../multi/geometries/register/multi_polygon.hpp | 41 +- boost/geometry/multi/io/dsv/write.hpp | 62 - boost/geometry/multi/io/wkt/detail/prefix.hpp | 32 +- boost/geometry/multi/io/wkt/read.hpp | 146 -- boost/geometry/multi/io/wkt/wkt.hpp | 4 +- boost/geometry/multi/io/wkt/write.hpp | 89 +- boost/geometry/multi/multi.hpp | 118 +- .../strategies/cartesian/centroid_average.hpp | 97 +- boost/geometry/multi/views/detail/range_type.hpp | 41 - .../policies/disjoint_interrupt_policy.hpp | 67 + .../policies/predicate_based_interrupt_policy.hpp | 101 + boost/geometry/policies/relate/de9im.hpp | 9 - boost/geometry/policies/relate/direction.hpp | 266 +-- .../policies/relate/intersection_points.hpp | 245 ++- .../policies/relate/intersection_ratios.hpp | 127 ++ boost/geometry/policies/relate/tupled.hpp | 116 +- .../policies/robustness/get_rescale_policy.hpp | 290 +++ .../policies/robustness/no_rescale_policy.hpp | 66 + .../policies/robustness/rescale_policy.hpp | 83 + .../policies/robustness/robust_point_type.hpp | 30 + boost/geometry/policies/robustness/robust_type.hpp | 67 + .../geometry/policies/robustness/segment_ratio.hpp | 239 +++ .../policies/robustness/segment_ratio_type.hpp | 28 + .../agnostic/buffer_distance_asymmetric.hpp | 110 + .../agnostic/buffer_distance_symmetric.hpp | 102 + .../strategies/agnostic/hull_graham_andrew.hpp | 57 +- .../strategies/agnostic/point_in_box_by_side.hpp | 22 +- .../strategies/agnostic/point_in_point.hpp | 136 ++ .../strategies/agnostic/point_in_poly_winding.hpp | 212 +- boost/geometry/strategies/agnostic/relate.hpp | 91 + .../agnostic/simplify_douglas_peucker.hpp | 305 ++- boost/geometry/strategies/buffer.hpp | 91 + boost/geometry/strategies/cartesian/box_in_box.hpp | 30 +- .../strategies/cartesian/buffer_end_flat.hpp | 112 + .../strategies/cartesian/buffer_end_round.hpp | 166 ++ .../strategies/cartesian/buffer_join_miter.hpp | 140 ++ .../strategies/cartesian/buffer_join_round.hpp | 177 ++ .../cartesian/buffer_join_round_by_divide.hpp | 155 ++ .../strategies/cartesian/buffer_point_circle.hpp | 108 + .../strategies/cartesian/buffer_point_square.hpp | 109 + .../strategies/cartesian/buffer_side_straight.hpp | 105 + .../strategies/cartesian/cart_intersect.hpp | 862 +++----- .../strategies/cartesian/centroid_average.hpp | 114 + .../cartesian/centroid_bashein_detmer.hpp | 25 +- .../cartesian/centroid_weighted_length.hpp | 3 +- .../cartesian/distance_projected_point.hpp | 205 +- .../cartesian/distance_projected_point_ax.hpp | 316 +++ .../strategies/cartesian/distance_pythagoras.hpp | 232 +-- .../cartesian/distance_pythagoras_box_box.hpp | 338 +++ .../cartesian/distance_pythagoras_point_box.hpp | 349 ++++ .../geometry/strategies/cartesian/point_in_box.hpp | 28 +- .../strategies/cartesian/side_by_triangle.hpp | 51 +- .../strategies/comparable_distance_result.hpp | 196 ++ .../strategies/concepts/convex_hull_concept.hpp | 15 +- .../strategies/concepts/distance_concept.hpp | 86 +- .../strategies/concepts/simplify_concept.hpp | 33 +- .../strategies/concepts/within_concept.hpp | 8 +- boost/geometry/strategies/covered_by.hpp | 2 +- .../default_comparable_distance_result.hpp | 43 + .../strategies/default_distance_result.hpp | 29 +- .../geometry/strategies/default_length_result.hpp | 65 +- boost/geometry/strategies/default_strategy.hpp | 34 + boost/geometry/strategies/distance.hpp | 59 +- boost/geometry/strategies/distance_result.hpp | 213 ++ boost/geometry/strategies/intersection.hpp | 37 +- boost/geometry/strategies/intersection_result.hpp | 53 +- boost/geometry/strategies/side_info.hpp | 21 +- .../geometry/strategies/spherical/area_huiller.hpp | 6 +- .../strategies/spherical/distance_cross_track.hpp | 219 +- .../spherical/distance_cross_track_point_box.hpp | 287 +++ .../strategies/spherical/distance_haversine.hpp | 205 +- .../strategies/spherical/side_by_cross_track.hpp | 32 +- boost/geometry/strategies/spherical/ssf.hpp | 8 +- boost/geometry/strategies/strategies.hpp | 24 + boost/geometry/strategies/strategy_transform.hpp | 58 +- boost/geometry/strategies/tags.hpp | 2 + .../strategies/transform/inverse_transformer.hpp | 19 +- .../strategies/transform/map_transformer.hpp | 28 +- .../strategies/transform/matrix_transformers.hpp | 199 +- boost/geometry/strategies/within.hpp | 2 +- boost/geometry/util/bare_type.hpp | 18 +- boost/geometry/util/calculation_type.hpp | 8 +- boost/geometry/util/combine_if.hpp | 78 + boost/geometry/util/compress_variant.hpp | 98 + boost/geometry/util/math.hpp | 193 +- boost/geometry/util/parameter_type_of.hpp | 4 +- boost/geometry/util/range.hpp | 325 +++ boost/geometry/util/rational.hpp | 48 +- boost/geometry/util/transform_variant.hpp | 79 + boost/geometry/views/box_view.hpp | 20 +- boost/geometry/views/closeable_view.hpp | 11 +- boost/geometry/views/detail/indexed_point_view.hpp | 112 + boost/geometry/views/detail/normalized_view.hpp | 116 ++ boost/geometry/views/detail/points_view.hpp | 19 +- boost/geometry/views/detail/range_type.hpp | 42 +- boost/geometry/views/identity_view.hpp | 8 + boost/geometry/views/segment_view.hpp | 14 +- 504 files changed, 65332 insertions(+), 12074 deletions(-) create mode 100644 boost/geometry/algorithms/crosses.hpp create mode 100644 boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp create mode 100644 boost/geometry/algorithms/detail/buffer/buffer_policies.hpp create mode 100644 boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp create mode 100644 boost/geometry/algorithms/detail/buffer/buffered_ring.hpp create mode 100644 boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp create mode 100644 boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp create mode 100644 boost/geometry/algorithms/detail/buffer/parallel_continue.hpp create mode 100644 boost/geometry/algorithms/detail/buffer/turn_in_input.hpp create mode 100644 boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp create mode 100644 boost/geometry/algorithms/detail/centroid/translating_transformer.hpp create mode 100644 boost/geometry/algorithms/detail/check_iterator_range.hpp create mode 100644 boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp create mode 100644 boost/geometry/algorithms/detail/closest_feature/point_to_range.hpp create mode 100644 boost/geometry/algorithms/detail/closest_feature/range_to_range.hpp create mode 100644 boost/geometry/algorithms/detail/comparable_distance/implementation.hpp create mode 100644 boost/geometry/algorithms/detail/comparable_distance/interface.hpp create mode 100644 boost/geometry/algorithms/detail/counting.hpp create mode 100644 boost/geometry/algorithms/detail/course.hpp delete mode 100644 boost/geometry/algorithms/detail/disjoint.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/areal_areal.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/box_box.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/implementation.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/interface.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/linear_areal.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/linear_linear.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/point_box.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/point_geometry.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/point_point.hpp create mode 100644 boost/geometry/algorithms/detail/disjoint/segment_box.hpp create mode 100644 boost/geometry/algorithms/detail/distance/backward_compatibility.hpp create mode 100644 boost/geometry/algorithms/detail/distance/box_to_box.hpp create mode 100644 boost/geometry/algorithms/detail/distance/default_strategies.hpp create mode 100644 boost/geometry/algorithms/detail/distance/geometry_to_segment_or_box.hpp create mode 100644 boost/geometry/algorithms/detail/distance/implementation.hpp create mode 100644 boost/geometry/algorithms/detail/distance/interface.hpp create mode 100644 boost/geometry/algorithms/detail/distance/is_comparable.hpp create mode 100644 boost/geometry/algorithms/detail/distance/iterator_selector.hpp create mode 100644 boost/geometry/algorithms/detail/distance/linear_or_areal_to_areal.hpp create mode 100644 boost/geometry/algorithms/detail/distance/linear_to_linear.hpp create mode 100644 boost/geometry/algorithms/detail/distance/multipoint_to_geometry.hpp create mode 100644 boost/geometry/algorithms/detail/distance/point_to_geometry.hpp create mode 100644 boost/geometry/algorithms/detail/distance/range_to_geometry_rtree.hpp create mode 100644 boost/geometry/algorithms/detail/distance/segment_to_box.hpp create mode 100644 boost/geometry/algorithms/detail/distance/segment_to_segment.hpp create mode 100644 boost/geometry/algorithms/detail/equals/point_point.hpp create mode 100644 boost/geometry/algorithms/detail/extreme_points.hpp create mode 100644 boost/geometry/algorithms/detail/get_max_size.hpp create mode 100644 boost/geometry/algorithms/detail/interior_iterator.hpp create mode 100644 boost/geometry/algorithms/detail/intersection/box_box.hpp create mode 100644 boost/geometry/algorithms/detail/intersection/implementation.hpp create mode 100644 boost/geometry/algorithms/detail/intersection/interface.hpp create mode 100644 boost/geometry/algorithms/detail/intersection/multi.hpp create mode 100644 boost/geometry/algorithms/detail/is_simple/always_simple.hpp create mode 100644 boost/geometry/algorithms/detail/is_simple/areal.hpp create mode 100644 boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp create mode 100644 boost/geometry/algorithms/detail/is_simple/implementation.hpp create mode 100644 boost/geometry/algorithms/detail/is_simple/interface.hpp create mode 100644 boost/geometry/algorithms/detail/is_simple/linear.hpp create mode 100644 boost/geometry/algorithms/detail/is_simple/multipoint.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/box.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/complement_graph.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/debug_complement_graph.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/has_spikes.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/implementation.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/interface.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/linear.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/multipolygon.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/pointlike.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/polygon.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/ring.hpp create mode 100644 boost/geometry/algorithms/detail/is_valid/segment.hpp create mode 100644 boost/geometry/algorithms/detail/multi_modify.hpp create mode 100644 boost/geometry/algorithms/detail/multi_modify_with_predicate.hpp create mode 100644 boost/geometry/algorithms/detail/multi_sum.hpp create mode 100644 boost/geometry/algorithms/detail/num_distinct_consecutive_points.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp delete mode 100644 boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/do_reverse.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/linear_linear.hpp create mode 100644 boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp create mode 100644 boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp create mode 100644 boost/geometry/algorithms/detail/recalculate.hpp create mode 100644 boost/geometry/algorithms/detail/relate/areal_areal.hpp create mode 100644 boost/geometry/algorithms/detail/relate/boundary_checker.hpp create mode 100644 boost/geometry/algorithms/detail/relate/follow_helpers.hpp create mode 100644 boost/geometry/algorithms/detail/relate/less.hpp create mode 100644 boost/geometry/algorithms/detail/relate/linear_areal.hpp create mode 100644 boost/geometry/algorithms/detail/relate/linear_linear.hpp create mode 100644 boost/geometry/algorithms/detail/relate/point_geometry.hpp create mode 100644 boost/geometry/algorithms/detail/relate/point_point.hpp create mode 100644 boost/geometry/algorithms/detail/relate/relate.hpp create mode 100644 boost/geometry/algorithms/detail/relate/result.hpp create mode 100644 boost/geometry/algorithms/detail/relate/topology_check.hpp create mode 100644 boost/geometry/algorithms/detail/relate/turns.hpp create mode 100644 boost/geometry/algorithms/detail/signed_index_type.hpp create mode 100644 boost/geometry/algorithms/detail/single_geometry.hpp create mode 100644 boost/geometry/algorithms/detail/sub_range.hpp create mode 100644 boost/geometry/algorithms/detail/turns/compare_turns.hpp create mode 100644 boost/geometry/algorithms/detail/turns/debug_turn.hpp create mode 100644 boost/geometry/algorithms/detail/turns/filter_continue_turns.hpp create mode 100644 boost/geometry/algorithms/detail/turns/print_turns.hpp create mode 100644 boost/geometry/algorithms/detail/turns/remove_duplicate_turns.hpp create mode 100644 boost/geometry/algorithms/detail/within/point_in_geometry.hpp create mode 100644 boost/geometry/algorithms/detail/within/within_no_turns.hpp create mode 100644 boost/geometry/algorithms/dispatch/disjoint.hpp create mode 100644 boost/geometry/algorithms/dispatch/distance.hpp create mode 100644 boost/geometry/algorithms/dispatch/is_simple.hpp create mode 100644 boost/geometry/algorithms/dispatch/is_valid.hpp create mode 100644 boost/geometry/algorithms/is_simple.hpp create mode 100644 boost/geometry/algorithms/is_valid.hpp create mode 100644 boost/geometry/algorithms/num_segments.hpp create mode 100644 boost/geometry/algorithms/point_on_surface.hpp create mode 100644 boost/geometry/algorithms/remove_spikes.hpp create mode 100644 boost/geometry/geometries/concepts/multi_linestring_concept.hpp create mode 100644 boost/geometry/geometries/concepts/multi_point_concept.hpp create mode 100644 boost/geometry/geometries/concepts/multi_polygon_concept.hpp create mode 100644 boost/geometry/geometries/multi_linestring.hpp create mode 100644 boost/geometry/geometries/multi_point.hpp create mode 100644 boost/geometry/geometries/multi_polygon.hpp create mode 100644 boost/geometry/geometries/pointing_segment.hpp create mode 100644 boost/geometry/geometries/register/multi_linestring.hpp create mode 100644 boost/geometry/geometries/register/multi_point.hpp create mode 100644 boost/geometry/geometries/register/multi_polygon.hpp create mode 100644 boost/geometry/geometries/variant.hpp create mode 100644 boost/geometry/index/adaptors/query.hpp create mode 100644 boost/geometry/index/detail/algorithms/bounds.hpp create mode 100644 boost/geometry/index/detail/algorithms/comparable_distance_centroid.hpp create mode 100644 boost/geometry/index/detail/algorithms/comparable_distance_far.hpp create mode 100644 boost/geometry/index/detail/algorithms/comparable_distance_near.hpp create mode 100644 boost/geometry/index/detail/algorithms/content.hpp create mode 100644 boost/geometry/index/detail/algorithms/diff_abs.hpp create mode 100644 boost/geometry/index/detail/algorithms/intersection_content.hpp create mode 100644 boost/geometry/index/detail/algorithms/is_valid.hpp create mode 100644 boost/geometry/index/detail/algorithms/margin.hpp create mode 100644 boost/geometry/index/detail/algorithms/minmaxdist.hpp create mode 100644 boost/geometry/index/detail/algorithms/path_intersection.hpp create mode 100644 boost/geometry/index/detail/algorithms/segment_intersection.hpp create mode 100644 boost/geometry/index/detail/algorithms/smallest_for_indexable.hpp create mode 100644 boost/geometry/index/detail/algorithms/sum_for_indexable.hpp create mode 100644 boost/geometry/index/detail/algorithms/union_content.hpp create mode 100644 boost/geometry/index/detail/assert.hpp create mode 100644 boost/geometry/index/detail/bounded_view.hpp create mode 100644 boost/geometry/index/detail/config_begin.hpp create mode 100644 boost/geometry/index/detail/config_end.hpp create mode 100644 boost/geometry/index/detail/distance_predicates.hpp create mode 100644 boost/geometry/index/detail/exception.hpp create mode 100644 boost/geometry/index/detail/meta.hpp create mode 100644 boost/geometry/index/detail/predicates.hpp create mode 100644 boost/geometry/index/detail/rtree/adaptors.hpp create mode 100644 boost/geometry/index/detail/rtree/kmeans/kmeans.hpp create mode 100644 boost/geometry/index/detail/rtree/kmeans/split.hpp create mode 100644 boost/geometry/index/detail/rtree/linear/linear.hpp create mode 100644 boost/geometry/index/detail/rtree/linear/redistribute_elements.hpp create mode 100644 boost/geometry/index/detail/rtree/node/auto_deallocator.hpp create mode 100644 boost/geometry/index/detail/rtree/node/concept.hpp create mode 100644 boost/geometry/index/detail/rtree/node/dynamic_visitor.hpp create mode 100644 boost/geometry/index/detail/rtree/node/node.hpp create mode 100644 boost/geometry/index/detail/rtree/node/node_auto_ptr.hpp create mode 100644 boost/geometry/index/detail/rtree/node/node_elements.hpp create mode 100644 boost/geometry/index/detail/rtree/node/pairs.hpp create mode 100644 boost/geometry/index/detail/rtree/node/variant_dynamic.hpp create mode 100644 boost/geometry/index/detail/rtree/node/variant_static.hpp create mode 100644 boost/geometry/index/detail/rtree/node/variant_visitor.hpp create mode 100644 boost/geometry/index/detail/rtree/node/weak_dynamic.hpp create mode 100644 boost/geometry/index/detail/rtree/node/weak_static.hpp create mode 100644 boost/geometry/index/detail/rtree/node/weak_visitor.hpp create mode 100644 boost/geometry/index/detail/rtree/options.hpp create mode 100644 boost/geometry/index/detail/rtree/pack_create.hpp create mode 100644 boost/geometry/index/detail/rtree/quadratic/quadratic.hpp create mode 100644 boost/geometry/index/detail/rtree/quadratic/redistribute_elements.hpp create mode 100644 boost/geometry/index/detail/rtree/query_iterators.hpp create mode 100644 boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp create mode 100644 boost/geometry/index/detail/rtree/rstar/insert.hpp create mode 100644 boost/geometry/index/detail/rtree/rstar/redistribute_elements.hpp create mode 100644 boost/geometry/index/detail/rtree/rstar/rstar.hpp create mode 100644 boost/geometry/index/detail/rtree/utilities/are_boxes_ok.hpp create mode 100644 boost/geometry/index/detail/rtree/utilities/are_levels_ok.hpp create mode 100644 boost/geometry/index/detail/rtree/utilities/gl_draw.hpp create mode 100644 boost/geometry/index/detail/rtree/utilities/print.hpp create mode 100644 boost/geometry/index/detail/rtree/utilities/statistics.hpp create mode 100644 boost/geometry/index/detail/rtree/utilities/view.hpp create mode 100644 boost/geometry/index/detail/rtree/visitors/children_box.hpp create mode 100644 boost/geometry/index/detail/rtree/visitors/copy.hpp create mode 100644 boost/geometry/index/detail/rtree/visitors/count.hpp create mode 100644 boost/geometry/index/detail/rtree/visitors/destroy.hpp create mode 100644 boost/geometry/index/detail/rtree/visitors/distance_query.hpp create mode 100644 boost/geometry/index/detail/rtree/visitors/insert.hpp create mode 100644 boost/geometry/index/detail/rtree/visitors/is_leaf.hpp create mode 100644 boost/geometry/index/detail/rtree/visitors/remove.hpp create mode 100644 boost/geometry/index/detail/rtree/visitors/spatial_query.hpp create mode 100644 boost/geometry/index/detail/serialization.hpp create mode 100644 boost/geometry/index/detail/tags.hpp create mode 100644 boost/geometry/index/detail/translator.hpp create mode 100644 boost/geometry/index/detail/tuples.hpp create mode 100644 boost/geometry/index/detail/utilities.hpp create mode 100644 boost/geometry/index/detail/varray.hpp create mode 100644 boost/geometry/index/detail/varray_detail.hpp create mode 100644 boost/geometry/index/distance_predicates.hpp create mode 100644 boost/geometry/index/equal_to.hpp create mode 100644 boost/geometry/index/indexable.hpp create mode 100644 boost/geometry/index/inserter.hpp create mode 100644 boost/geometry/index/parameters.hpp create mode 100644 boost/geometry/index/predicates.hpp create mode 100644 boost/geometry/index/rtree.hpp create mode 100644 boost/geometry/io/svg/svg_mapper.hpp create mode 100644 boost/geometry/io/svg/write_svg.hpp create mode 100644 boost/geometry/io/svg/write_svg_multi.hpp create mode 100644 boost/geometry/iterators/concatenate_iterator.hpp create mode 100644 boost/geometry/iterators/detail/point_iterator/inner_range_type.hpp create mode 100644 boost/geometry/iterators/detail/point_iterator/iterator_type.hpp create mode 100644 boost/geometry/iterators/detail/point_iterator/value_type.hpp create mode 100644 boost/geometry/iterators/detail/segment_iterator/iterator_type.hpp create mode 100644 boost/geometry/iterators/detail/segment_iterator/range_segment_iterator.hpp create mode 100644 boost/geometry/iterators/detail/segment_iterator/value_type.hpp create mode 100644 boost/geometry/iterators/dispatch/point_iterator.hpp create mode 100644 boost/geometry/iterators/dispatch/segment_iterator.hpp create mode 100644 boost/geometry/iterators/flatten_iterator.hpp create mode 100644 boost/geometry/iterators/has_one_element.hpp create mode 100644 boost/geometry/iterators/point_iterator.hpp create mode 100644 boost/geometry/iterators/point_reverse_iterator.hpp create mode 100644 boost/geometry/iterators/segment_iterator.hpp create mode 100644 boost/geometry/multi/algorithms/detail/extreme_points.hpp create mode 100644 boost/geometry/multi/algorithms/disjoint.hpp create mode 100644 boost/geometry/multi/algorithms/remove_spikes.hpp create mode 100644 boost/geometry/policies/disjoint_interrupt_policy.hpp create mode 100644 boost/geometry/policies/predicate_based_interrupt_policy.hpp create mode 100644 boost/geometry/policies/relate/intersection_ratios.hpp create mode 100644 boost/geometry/policies/robustness/get_rescale_policy.hpp create mode 100644 boost/geometry/policies/robustness/no_rescale_policy.hpp create mode 100644 boost/geometry/policies/robustness/rescale_policy.hpp create mode 100644 boost/geometry/policies/robustness/robust_point_type.hpp create mode 100644 boost/geometry/policies/robustness/robust_type.hpp create mode 100644 boost/geometry/policies/robustness/segment_ratio.hpp create mode 100644 boost/geometry/policies/robustness/segment_ratio_type.hpp create mode 100644 boost/geometry/strategies/agnostic/buffer_distance_asymmetric.hpp create mode 100644 boost/geometry/strategies/agnostic/buffer_distance_symmetric.hpp create mode 100644 boost/geometry/strategies/agnostic/point_in_point.hpp create mode 100644 boost/geometry/strategies/agnostic/relate.hpp create mode 100644 boost/geometry/strategies/buffer.hpp create mode 100644 boost/geometry/strategies/cartesian/buffer_end_flat.hpp create mode 100644 boost/geometry/strategies/cartesian/buffer_end_round.hpp create mode 100644 boost/geometry/strategies/cartesian/buffer_join_miter.hpp create mode 100644 boost/geometry/strategies/cartesian/buffer_join_round.hpp create mode 100644 boost/geometry/strategies/cartesian/buffer_join_round_by_divide.hpp create mode 100644 boost/geometry/strategies/cartesian/buffer_point_circle.hpp create mode 100644 boost/geometry/strategies/cartesian/buffer_point_square.hpp create mode 100644 boost/geometry/strategies/cartesian/buffer_side_straight.hpp create mode 100644 boost/geometry/strategies/cartesian/centroid_average.hpp create mode 100644 boost/geometry/strategies/cartesian/distance_projected_point_ax.hpp create mode 100644 boost/geometry/strategies/cartesian/distance_pythagoras_box_box.hpp create mode 100644 boost/geometry/strategies/cartesian/distance_pythagoras_point_box.hpp create mode 100644 boost/geometry/strategies/comparable_distance_result.hpp create mode 100644 boost/geometry/strategies/default_comparable_distance_result.hpp create mode 100644 boost/geometry/strategies/default_strategy.hpp create mode 100644 boost/geometry/strategies/distance_result.hpp create mode 100644 boost/geometry/strategies/spherical/distance_cross_track_point_box.hpp create mode 100644 boost/geometry/util/combine_if.hpp create mode 100644 boost/geometry/util/compress_variant.hpp create mode 100644 boost/geometry/util/range.hpp create mode 100644 boost/geometry/util/transform_variant.hpp create mode 100644 boost/geometry/views/detail/indexed_point_view.hpp create mode 100644 boost/geometry/views/detail/normalized_view.hpp (limited to 'boost/geometry') diff --git a/boost/geometry/algorithms/append.hpp b/boost/geometry/algorithms/append.hpp index 72b2bbadab..1a8828ba4b 100644 --- a/boost/geometry/algorithms/append.hpp +++ b/boost/geometry/algorithms/append.hpp @@ -1,8 +1,14 @@ // 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. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -14,17 +20,20 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_APPEND_HPP #define BOOST_GEOMETRY_ALGORITHMS_APPEND_HPP -#include +#include +#include +#include +#include +#include #include #include #include #include - -#include -#include #include +#include +#include namespace boost { namespace geometry @@ -92,7 +101,7 @@ struct point_to_polygon else if (ring_index < int(num_interior_rings(polygon))) { append_point::apply( - interior_rings(polygon)[ring_index], point); + range::at(interior_rings(polygon), ring_index), point); } } }; @@ -104,7 +113,7 @@ struct range_to_polygon typedef typename ring_type::type ring_type; static inline void apply(Polygon& polygon, Range const& range, - int ring_index, int ) + int ring_index, int = 0) { if (ring_index == -1) { @@ -114,7 +123,7 @@ struct range_to_polygon else if (ring_index < int(num_interior_rings(polygon))) { append_range::apply( - interior_rings(polygon)[ring_index], range); + range::at(interior_rings(polygon), ring_index), range); } } }; @@ -174,7 +183,7 @@ struct append_range : detail::append::range_to_polygon {}; -} +} // namespace splitted_dispatch // Default: append a range (or linestring or ring or whatever) to any geometry @@ -193,12 +202,143 @@ struct append : splitted_dispatch::append_point::type, Geometry, RangeOrPoint> {}; +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace append +{ + +template +struct append_to_multigeometry +{ + static inline void apply(MultiGeometry& multigeometry, + RangeOrPoint const& range_or_point, + int ring_index, int multi_index) + { + + dispatch::append + < + typename boost::range_value::type, + RangeOrPoint + >::apply(range::at(multigeometry, multi_index), range_or_point, ring_index); + } +}; + +}} // namespace detail::append +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +namespace splitted_dispatch +{ + +template +struct append_point + : detail::append::append_point +{}; + +template +struct append_range + : detail::append::append_range +{}; + +template +struct append_point + : detail::append::append_to_multigeometry +{}; + +template +struct append_range + : detail::append::append_to_multigeometry +{}; + +template +struct append_point + : detail::append::append_to_multigeometry +{}; + +template +struct append_range + : detail::append::append_to_multigeometry +{}; + +} // namespace splitted_dispatch } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH +namespace resolve_variant { + +template +struct append +{ + template + static inline void apply(Geometry& geometry, + RangeOrPoint const& range_or_point, + int ring_index, + int multi_index) + { + concept::check(); + dispatch::append::apply(geometry, + range_or_point, + ring_index, + multi_index); + } +}; + + +template +struct append > +{ + template + struct visitor: boost::static_visitor + { + RangeOrPoint const& m_range_or_point; + int m_ring_index; + int m_multi_index; + + visitor(RangeOrPoint const& range_or_point, + int ring_index, + int multi_index): + m_range_or_point(range_or_point), + m_ring_index(ring_index), + m_multi_index(multi_index) + {} + + template + void operator()(Geometry& geometry) const + { + append::apply(geometry, + m_range_or_point, + m_ring_index, + m_multi_index); + } + }; + + template + static inline void apply(boost::variant& variant_geometry, + RangeOrPoint const& range_or_point, + int ring_index, + int multi_index) + { + apply_visitor( + visitor( + range_or_point, + ring_index, + multi_index + ), + variant_geometry + ); + } +}; + +} // namespace resolve_variant; + + /*! \brief Appends one or more points to a linestring, ring, polygon, multi-geometry \ingroup append @@ -208,22 +348,17 @@ struct append \param range_or_point The point or range to add \param ring_index The index of the ring in case of a polygon: exterior ring (-1, the default) or interior ring index -\param multi_index Reserved for multi polygons or multi linestrings +\param multi_index The index of the geometry to which the points are appended \qbk{[include reference/algorithms/append.qbk]} } */ template inline void append(Geometry& geometry, RangeOrPoint const& range_or_point, - int ring_index = -1, int multi_index = 0) + int ring_index = -1, int multi_index = 0) { - concept::check(); - - dispatch::append - < - Geometry, - RangeOrPoint - >::apply(geometry, range_or_point, ring_index, multi_index); + resolve_variant::append + ::apply(geometry, range_or_point, ring_index, multi_index); } diff --git a/boost/geometry/algorithms/area.hpp b/boost/geometry/algorithms/area.hpp index 8193200ab9..7377798719 100644 --- a/boost/geometry/algorithms/area.hpp +++ b/boost/geometry/algorithms/area.hpp @@ -18,18 +18,24 @@ #include #include #include +#include +#include +#include #include #include #include #include +#include #include +#include #include #include #include // #include +#include #include #include @@ -49,41 +55,33 @@ namespace boost { namespace geometry namespace detail { namespace area { -template struct box_area { - typedef typename coordinate_type::type return_type; - - static inline return_type apply(Box const& box, Strategy const&) + template + static inline typename coordinate_type::type + apply(Box const& box, Strategy const&) { // Currently only works for 2D Cartesian boxes assert_dimension(); - return_type const dx = get(box) - - get(box); - return_type const dy = get(box) - - get(box); - - return dx * dy; + return (get(box) - get(box)) + * (get(box) - get(box)); } }; template < - typename Ring, iterate_direction Direction, - closure_selector Closure, - typename Strategy + closure_selector Closure > struct ring_area { - BOOST_CONCEPT_ASSERT( (geometry::concept::AreaStrategy) ); - - typedef typename Strategy::return_type type; - - static inline type apply(Ring const& ring, Strategy const& strategy) + template + static inline typename Strategy::return_type + apply(Ring const& ring, Strategy const& strategy) { + BOOST_CONCEPT_ASSERT( (geometry::concept::AreaStrategy) ); assert_dimension(); // Ignore warning (because using static method sometimes) on strategy @@ -92,10 +90,10 @@ 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 (int(boost::size(ring)) + if (boost::size(ring) < core_detail::closure::minimum_ring_size::value) { - return type(); + return typename Strategy::return_type(); } typedef typename reversible_view::type rview_type; @@ -136,77 +134,112 @@ namespace dispatch template < typename Geometry, - typename Strategy = typename strategy::area::services::default_strategy - < - typename cs_tag - < - typename point_type::type - >::type, - typename point_type::type - >::type, typename Tag = typename tag::type > -struct area - : detail::calculate_null - < - typename Strategy::return_type, - Geometry, - Strategy - > {}; +struct area : detail::calculate_null +{ + template + static inline typename Strategy::return_type apply(Geometry const& geometry, Strategy const& strategy) + { + return calculate_null::apply(geometry, strategy); + } +}; -template -< - typename Geometry, - typename Strategy -> -struct area - : detail::area::box_area +template +struct area : detail::area::box_area {}; -template -< - typename Ring, - typename Strategy -> -struct area +template +struct area : detail::area::ring_area < - Ring, order_as_direction::value>::value, - geometry::closure::value, - Strategy + geometry::closure::value > {}; -template -< - typename Polygon, - typename Strategy -> -struct area - : detail::calculate_polygon_sum - < +template +struct area : detail::calculate_polygon_sum +{ + template + static inline typename Strategy::return_type apply(Polygon const& polygon, Strategy const& strategy) + { + return calculate_polygon_sum::apply< typename Strategy::return_type, - Polygon, - Strategy, detail::area::ring_area < - typename ring_type::type, order_as_direction::value>::value, - geometry::closure::value, - Strategy + geometry::closure::value > - > -{}; + >(polygon, strategy); + } +}; + + +template +struct area : detail::multi_sum +{ + template + static inline typename Strategy::return_type + apply(MultiGeometry const& multi, Strategy const& strategy) + { + return multi_sum::apply + < + typename Strategy::return_type, + area::type> + >(multi, strategy); + } +}; } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH +namespace resolve_variant { + +template +struct area +{ + template + static inline typename Strategy::return_type apply(Geometry const& geometry, + Strategy const& strategy) + { + return dispatch::area::apply(geometry, strategy); + } +}; + +template +struct area > +{ + template + struct visitor: boost::static_visitor + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy): m_strategy(strategy) {} + + template + typename Strategy::return_type operator()(Geometry const& geometry) const + { + return area::apply(geometry, m_strategy); + } + }; + + template + static inline typename Strategy::return_type + apply(boost::variant const& geometry, + Strategy const& strategy) + { + return boost::apply_visitor(visitor(strategy), geometry); + } +}; + +} // namespace resolve_variant + /*! \brief \brief_calc{area} @@ -234,6 +267,8 @@ inline typename default_area_result::type area(Geometry const& geometr { concept::check(); + // TODO put this into a resolve_strategy stage + // (and take the return type from resolve_variant) typedef typename point_type::type point_type; typedef typename strategy::area::services::default_strategy < @@ -242,11 +277,8 @@ inline typename default_area_result::type area(Geometry const& geometr >::type strategy_type; // detail::throw_on_empty_input(geometry); - - return dispatch::area - < - Geometry - >::apply(geometry, strategy_type()); + + return resolve_variant::area::apply(geometry, strategy_type()); } /*! @@ -280,12 +312,8 @@ inline typename Strategy::return_type area( concept::check(); // detail::throw_on_empty_input(geometry); - - return dispatch::area - < - Geometry, - Strategy - >::apply(geometry, strategy); + + return resolve_variant::area::apply(geometry, strategy); } diff --git a/boost/geometry/algorithms/assign.hpp b/boost/geometry/algorithms/assign.hpp index 8c153c878d..32f095b9ac 100644 --- a/boost/geometry/algorithms/assign.hpp +++ b/boost/geometry/algorithms/assign.hpp @@ -3,6 +3,7 @@ // 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. +// Copyright (c) 2014 Samuel Debionne, Grenoble, France. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -40,6 +41,8 @@ #include +#include + namespace boost { namespace geometry { @@ -121,10 +124,233 @@ inline void assign_zero(Geometry& geometry) >::apply(geometry); } +/*! +\brief Assign two coordinates to a geometry (usually a 2D point) +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param geometry \param_geometry +\param c1 \param_x +\param c2 \param_y + +\qbk{distinguish, 2 coordinate values} +\qbk{ +[heading Example] +[assign_2d_point] [assign_2d_point_output] + +[heading See also] +\* [link geometry.reference.algorithms.make.make_2_2_coordinate_values make] +} + */ +template +inline void assign_values(Geometry& geometry, Type const& c1, Type const& c2) +{ + concept::check(); + + dispatch::assign + < + typename tag::type, + Geometry, + geometry::dimension::type::value + >::apply(geometry, c1, c2); +} + +/*! +\brief Assign three values to a geometry (usually a 3D point) +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param geometry \param_geometry +\param c1 \param_x +\param c2 \param_y +\param c3 \param_z + +\qbk{distinguish, 3 coordinate values} +\qbk{ +[heading Example] +[assign_3d_point] [assign_3d_point_output] + +[heading See also] +\* [link geometry.reference.algorithms.make.make_3_3_coordinate_values make] +} + */ +template +inline void assign_values(Geometry& geometry, + Type const& c1, Type const& c2, Type const& c3) +{ + concept::check(); + + dispatch::assign + < + typename tag::type, + Geometry, + geometry::dimension::type::value + >::apply(geometry, c1, c2, c3); +} + +/*! +\brief Assign four values to a geometry (usually a box or segment) +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param geometry \param_geometry +\param c1 First coordinate (usually x1) +\param c2 Second coordinate (usually y1) +\param c3 Third coordinate (usually x2) +\param c4 Fourth coordinate (usually y2) + +\qbk{distinguish, 4 coordinate values} + */ +template +inline void assign_values(Geometry& geometry, + Type const& c1, Type const& c2, Type const& c3, Type const& c4) +{ + concept::check(); + + dispatch::assign + < + typename tag::type, + Geometry, + geometry::dimension::type::value + >::apply(geometry, c1, c2, c3, c4); +} + + + +namespace resolve_variant +{ + +template +struct assign +{ + static inline void + apply( + Geometry1& geometry1, + const Geometry2& geometry2) + { + concept::check(); + concept::check(); + concept::check_concepts_and_equal_dimensions(); + + bool const same_point_order = + point_order::value == point_order::value; + bool const same_closure = + closure::value == closure::value; + + BOOST_MPL_ASSERT_MSG + ( + same_point_order, ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_POINT_ORDER + , (types) + ); + BOOST_MPL_ASSERT_MSG + ( + same_closure, ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_CLOSURE + , (types) + ); + + dispatch::convert::apply(geometry2, geometry1); + } +}; + + +template +struct assign, Geometry2> +{ + struct visitor: static_visitor + { + Geometry2 const& m_geometry2; + + visitor(Geometry2 const& geometry2) + : m_geometry2(geometry2) + {} + + template + result_type operator()(Geometry1& geometry1) const + { + return assign + < + Geometry1, + Geometry2 + >::apply + (geometry1, m_geometry2); + } + }; + + static inline void + apply(variant& geometry1, + Geometry2 const& geometry2) + { + return apply_visitor(visitor(geometry2), geometry1); + } +}; + + +template +struct assign > +{ + struct visitor: static_visitor + { + Geometry1& m_geometry1; + + visitor(Geometry1 const& geometry1) + : m_geometry1(geometry1) + {} + + template + result_type operator()(Geometry2 const& geometry2) const + { + return assign + < + Geometry1, + Geometry2 + >::apply + (m_geometry1, geometry2); + } + }; + + static inline void + apply(Geometry1& geometry1, + variant const& geometry2) + { + return apply_visitor(visitor(geometry1), geometry2); + } +}; + + +template +struct assign, variant > +{ + struct visitor: static_visitor + { + template + result_type operator()( + Geometry1& geometry1, + Geometry2 const& geometry2) const + { + return assign + < + Geometry1, + Geometry2 + >::apply + (geometry1, geometry2); + } + }; + + static inline void + apply(variant& geometry1, + variant const& geometry2) + { + return apply_visitor(visitor(), geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + /*! \brief Assigns one geometry to another geometry -\details The assign algorithm assigns one geometry, e.g. a BOX, to another geometry, e.g. a RING. This only -if it is possible and applicable. +\details The assign algorithm assigns one geometry, e.g. a BOX, to another +geometry, e.g. a RING. This only works if it is possible and applicable. \ingroup assign \tparam Geometry1 \tparam_geometry \tparam Geometry2 \tparam_geometry @@ -142,25 +368,7 @@ if it is possible and applicable. template inline void assign(Geometry1& geometry1, Geometry2 const& geometry2) { - concept::check_concepts_and_equal_dimensions(); - - bool const same_point_order = - point_order::value == point_order::value; - bool const same_closure = - closure::value == closure::value; - - BOOST_MPL_ASSERT_MSG - ( - same_point_order, ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_POINT_ORDER - , (types) - ); - BOOST_MPL_ASSERT_MSG - ( - same_closure, ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_CLOSURE - , (types) - ); - - dispatch::convert::apply(geometry2, geometry1); + resolve_variant::assign::apply(geometry1, geometry2); } diff --git a/boost/geometry/algorithms/buffer.hpp b/boost/geometry/algorithms/buffer.hpp index e22e36addc..b8b07ad4d9 100644 --- a/boost/geometry/algorithms/buffer.hpp +++ b/boost/geometry/algorithms/buffer.hpp @@ -17,15 +17,19 @@ #include #include - +#include +#include +#include #include -#include +#include +#include #include #include -#include +#include #include +#include namespace boost { namespace geometry { @@ -75,29 +79,87 @@ inline void buffer_box(BoxIn const& box_in, T const& distance, BoxOut& box_out) namespace dispatch { -template -struct buffer {}; +template +< + typename Input, + typename Output, + typename TagIn = typename tag::type, + typename TagOut = typename tag::type +> +struct buffer: not_implemented +{}; -template -struct buffer +template +struct buffer { - static inline void apply(BoxIn const& box_in, T const& distance, - T const& , BoxIn& box_out) + template + static inline void apply(BoxIn const& box_in, Distance const& distance, + Distance const& , BoxOut& box_out) { detail::buffer::buffer_box(box_in, distance, box_out); } }; -// Many things to do. Point is easy, other geometries require self intersections -// For point, note that it should output as a polygon (like the rest). Buffers -// of a set of geometries are often lateron combined using a "dissolve" operation. -// Two points close to each other get a combined kidney shaped buffer then. - } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH +namespace resolve_variant { + +template +struct buffer +{ + template + static inline void apply(Geometry const& geometry, + Distance const& distance, + Distance const& chord_length, + GeometryOut& out) + { + dispatch::buffer::apply(geometry, distance, chord_length, out); + } +}; + +template +struct buffer > +{ + template + struct visitor: boost::static_visitor + { + Distance const& m_distance; + Distance const& m_chord_length; + GeometryOut& m_out; + + visitor(Distance const& distance, + Distance const& chord_length, + GeometryOut& out) + : m_distance(distance), + m_chord_length(chord_length), + m_out(out) + {} + + template + void operator()(Geometry const& geometry) const + { + buffer::apply(geometry, m_distance, m_chord_length, m_out); + } + }; + + template + static inline void apply( + boost::variant const& geometry, + Distance const& distance, + Distance const& chord_length, + GeometryOut& out + ) + { + boost::apply_visitor(visitor(distance, chord_length, out), geometry); + } +}; + +} // namespace resolve_variant + + /*! \brief \brief_calc{buffer} \ingroup buffer @@ -109,7 +171,6 @@ struct buffer \param geometry_out \param_geometry \param distance The distance to be used for the buffer \param chord_length (optional) The length of the chord's in the generated arcs around points or bends -\note Currently only implemented for box, the trivial case, but still useful \qbk{[include reference/algorithms/buffer.qbk]} */ @@ -120,14 +181,7 @@ inline void buffer(Input const& geometry_in, Output& geometry_out, concept::check(); concept::check(); - dispatch::buffer - < - typename tag::type, - typename tag::type, - Input, - Distance, - Output - >::apply(geometry_in, distance, chord_length, geometry_out); + resolve_variant::buffer::apply(geometry_in, distance, chord_length, geometry_out); } /*! @@ -139,29 +193,90 @@ inline void buffer(Input const& geometry_in, Output& geometry_out, \tparam Distance \tparam_numeric \param geometry \param_geometry \param distance The distance to be used for the buffer -\param chord_length (optional) The length of the chord's in the generated arcs around points or bends +\param chord_length (optional) The length of the chord's in the generated arcs + around points or bends (RESERVED, NOT YET USED) \return \return_calc{buffer} */ -template -Output return_buffer(Input const& geometry, T const& distance, T const& chord_length = -1) +template +Output return_buffer(Input const& geometry, Distance const& distance, Distance const& chord_length = -1) { concept::check(); concept::check(); Output geometry_out; - dispatch::buffer - < - typename tag::type, - typename tag::type, - Input, - T, - Output - >::apply(geometry, distance, chord_length, geometry_out); + resolve_variant::buffer::apply(geometry, distance, chord_length, geometry_out); return geometry_out; } +/*! +\brief \brief_calc{buffer} +\ingroup buffer +\details \details_calc{buffer, \det_buffer}. +\tparam GeometryIn \tparam_geometry +\tparam MultiPolygon \tparam_geometry{MultiPolygon} +\tparam DistanceStrategy A strategy defining distance (or radius) +\tparam SideStrategy A strategy defining creation along sides +\tparam JoinStrategy A strategy defining creation around convex corners +\tparam EndStrategy A strategy defining creation at linestring ends +\tparam PointStrategy A strategy defining creation around points +\param geometry_in \param_geometry +\param geometry_out output multi polygon (or std:: collection of polygons), + will contain a buffered version of the input geometry +\param distance_strategy The distance strategy to be used +\param side_strategy The side strategy to be used +\param join_strategy The join strategy to be used +\param end_strategy The end strategy to be used +\param point_strategy The point strategy to be used + +\qbk{distinguish,with strategies} +\qbk{[include reference/algorithms/buffer_with_strategies.qbk]} + */ +template +< + typename GeometryIn, + typename MultiPolygon, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy +> +inline void buffer(GeometryIn const& geometry_in, + MultiPolygon& geometry_out, + DistanceStrategy const& distance_strategy, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + PointStrategy const& point_strategy) +{ + typedef typename boost::range_value::type polygon_type; + concept::check(); + concept::check(); + + typedef typename point_type::type point_type; + typedef typename rescale_policy_type::type rescale_policy_type; + + geometry_out.clear(); + + model::box box; + envelope(geometry_in, box); + buffer(box, box, distance_strategy.max_distance(join_strategy, end_strategy)); + + rescale_policy_type rescale_policy + = boost::geometry::get_rescale_policy(box); + + detail::buffer::buffer_inserter(geometry_in, std::back_inserter(geometry_out), + distance_strategy, + side_strategy, + join_strategy, + end_strategy, + point_strategy, + rescale_policy); +} + + }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_BUFFER_HPP diff --git a/boost/geometry/algorithms/centroid.hpp b/boost/geometry/algorithms/centroid.hpp index 69ad9fe829..65dc9c3753 100644 --- a/boost/geometry/algorithms/centroid.hpp +++ b/boost/geometry/algorithms/centroid.hpp @@ -3,6 +3,12 @@ // 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. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -18,7 +24,9 @@ #include #include -#include +#include +#include +#include #include #include @@ -27,17 +35,27 @@ #include #include #include +#include +#include -#include -#include #include + +#include +#include +#include +#include #include #include +#include #include #include #include +#include +#include + +#include namespace boost { namespace geometry @@ -62,8 +80,15 @@ class centroid_exception : public geometry::exception { public: + /*! + \brief The default constructor + */ inline centroid_exception() {} + /*! + \brief Returns the explanatory string. + \return Pointer to a null-terminated string with explanatory information. + */ virtual char const* what() const throw() { return "Boost.Geometry Centroid calculation exception"; @@ -77,9 +102,9 @@ public: namespace detail { namespace centroid { -template struct centroid_point { + template static inline void apply(Point const& point, PointCentroid& centroid, Strategy const&) { @@ -89,55 +114,56 @@ struct centroid_point template < - typename Box, + typename Indexed, typename Point, std::size_t Dimension, std::size_t DimensionCount > -struct centroid_box_calculator +struct centroid_indexed_calculator { typedef typename select_coordinate_type < - Box, Point + Indexed, Point >::type coordinate_type; - static inline void apply(Box const& box, Point& centroid) + static inline void apply(Indexed const& indexed, Point& centroid) { - coordinate_type const c1 = get(box); - coordinate_type const c2 = get(box); + coordinate_type const c1 = get(indexed); + coordinate_type const c2 = get(indexed); coordinate_type m = c1 + c2; - m /= 2.0; + coordinate_type const two = 2; + m /= two; set(centroid, m); - centroid_box_calculator + centroid_indexed_calculator < - Box, Point, + Indexed, Point, Dimension + 1, DimensionCount - >::apply(box, centroid); + >::apply(indexed, centroid); } }; -template -struct centroid_box_calculator +template +struct centroid_indexed_calculator { - static inline void apply(Box const& , Point& ) + static inline void apply(Indexed const& , Point& ) { } }; -template -struct centroid_box +struct centroid_indexed { - static inline void apply(Box const& box, Point& centroid, + template + static inline void apply(Indexed const& indexed, Point& centroid, Strategy const&) { - centroid_box_calculator + centroid_indexed_calculator < - Box, Point, - 0, dimension::type::value - >::apply(box, centroid); + Indexed, Point, + 0, dimension::type::value + >::apply(indexed, centroid); } }; @@ -169,16 +195,19 @@ inline bool range_ok(Range const& range, Point& centroid) return true; } - /*! - \brief Calculate the centroid of a ring. + \brief Calculate the centroid of a Ring or a Linestring. */ -template +template struct centroid_range_state { + template static inline void apply(Ring const& ring, - Strategy const& strategy, typename Strategy::state_type& state) + PointTransformer const& transformer, + Strategy const& strategy, + typename Strategy::state_type& state) { + typedef typename geometry::point_type::type point_type; typedef typename closeable_view::type view_type; typedef typename boost::range_iterator::type iterator_type; @@ -187,31 +216,42 @@ struct centroid_range_state iterator_type it = boost::begin(view); iterator_type end = boost::end(view); - for (iterator_type previous = it++; - it != end; - ++previous, ++it) + typename PointTransformer::result_type + previous_pt = transformer.apply(*it); + + for ( ++it ; it != end ; ++it) { - strategy.apply(*previous, *it, state); + typename PointTransformer::result_type + pt = transformer.apply(*it); + + strategy.apply(static_cast(previous_pt), + static_cast(pt), + state); + + previous_pt = pt; } } }; -template +template struct centroid_range { + template static inline void apply(Range const& range, Point& centroid, - Strategy const& strategy) + Strategy const& strategy) { if (range_ok(range, centroid)) { + // prepare translation transformer + translating_transformer transformer(*boost::begin(range)); + typename Strategy::state_type state; - centroid_range_state - < - Range, - Closure, - Strategy - >::apply(range, strategy, state); + centroid_range_state::apply(range, transformer, + strategy, state); strategy.result(state, centroid); + + // translate the result back + transformer.apply_reverse(centroid); } } }; @@ -222,48 +262,112 @@ struct centroid_range \note Because outer ring is clockwise, inners are counter clockwise, triangle approach is OK and works for polygons with rings. */ -template struct centroid_polygon_state { - typedef typename ring_type::type ring_type; - + template static inline void apply(Polygon const& poly, - Strategy const& strategy, typename Strategy::state_type& state) + PointTransformer const& transformer, + Strategy const& strategy, + typename Strategy::state_type& state) { - typedef centroid_range_state - < - ring_type, - geometry::closure::value, - Strategy - > per_ring; + typedef typename ring_type::type ring_type; + typedef centroid_range_state::value> per_ring; - per_ring::apply(exterior_ring(poly), strategy, state); + per_ring::apply(exterior_ring(poly), transformer, strategy, state); - typename interior_return_type::type rings - = interior_rings(poly); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + typename interior_return_type::type + rings = interior_rings(poly); + + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) { - per_ring::apply(*it, strategy, state); + per_ring::apply(*it, transformer, strategy, state); } } }; -template struct centroid_polygon { + template static inline void apply(Polygon const& poly, Point& centroid, Strategy const& strategy) { if (range_ok(exterior_ring(poly), centroid)) { + // prepare translation transformer + translating_transformer + transformer(*boost::begin(exterior_ring(poly))); + typename Strategy::state_type state; - centroid_polygon_state - < - Polygon, - Strategy - >::apply(poly, strategy, state); + centroid_polygon_state::apply(poly, transformer, strategy, state); strategy.result(state, centroid); + + // translate the result back + transformer.apply_reverse(centroid); + } + } +}; + + +/*! + \brief Building block of a multi-point, to be used as Policy in the + more generec centroid_multi +*/ +struct centroid_multi_point_state +{ + template + static inline void apply(Point const& point, + PointTransformer const& transformer, + Strategy const& strategy, + typename Strategy::state_type& state) + { + strategy.apply(static_cast(transformer.apply(point)), + state); + } +}; + + +/*! + \brief Generic implementation which calls a policy to calculate the + centroid of the total of its single-geometries + \details The Policy is, in general, the single-version, with state. So + detail::centroid::centroid_polygon_state is used as a policy for this + detail::centroid::centroid_multi + +*/ +template +struct centroid_multi +{ + template + static inline void apply(Multi const& multi, + Point& centroid, + Strategy const& strategy) + { +#if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) + // If there is nothing in any of the ranges, it is not possible + // to calculate the centroid + if (geometry::num_points(multi) == 0) + { + throw centroid_exception(); } +#endif + + // prepare translation transformer + translating_transformer transformer(multi); + + typename Strategy::state_type state; + + for (typename boost::range_iterator::type + it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + Policy::apply(*it, transformer, strategy, state); + } + Strategy::result(state, centroid); + + // translate the result back + transformer.apply_reverse(centroid); } }; @@ -278,64 +382,153 @@ namespace dispatch template < - typename Tag, typename Geometry, - typename Point, - typename Strategy + typename Tag = typename tag::type > -struct centroid {}; +struct centroid: not_implemented +{}; -template -< - typename Geometry, - typename Point, - typename Strategy -> -struct centroid - : detail::centroid::centroid_point +template +struct centroid + : detail::centroid::centroid_point {}; -template -< - typename Box, - typename Point, - typename Strategy -> -struct centroid - : detail::centroid::centroid_box +template +struct centroid + : detail::centroid::centroid_indexed +{}; + +template +struct centroid + : detail::centroid::centroid_indexed +{}; + +template +struct centroid + : detail::centroid::centroid_range::value> +{}; + +template +struct centroid + : detail::centroid::centroid_range {}; -template -struct centroid - : detail::centroid::centroid_range +template +struct centroid + : detail::centroid::centroid_polygon +{}; + +template +struct centroid + : detail::centroid::centroid_multi < - Ring, - Point, - geometry::closure::value, - Strategy + detail::centroid::centroid_range_state > {}; -template -struct centroid - : detail::centroid::centroid_range +template +struct centroid + : detail::centroid::centroid_multi < - Linestring, - Point, - closed, - Strategy + detail::centroid::centroid_polygon_state + > +{}; + +template +struct centroid + : detail::centroid::centroid_multi + < + detail::centroid::centroid_multi_point_state > - {}; +{}; -template -struct centroid - : detail::centroid::centroid_polygon - {}; } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH +namespace resolve_strategy { + +template +struct centroid +{ + template + static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy) + { + dispatch::centroid::apply(geometry, out, strategy); + } + + template + static inline void apply(Geometry const& geometry, Point& out, default_strategy) + { + typedef typename strategy::centroid::services::default_strategy + < + typename cs_tag::type, + typename tag_cast + < + typename tag::type, + pointlike_tag, + linear_tag, + areal_tag + >::type, + dimension::type::value, + Point, + Geometry + >::type strategy_type; + + dispatch::centroid::apply(geometry, out, strategy_type()); + } +}; + +} // namespace resolve_strategy + + +namespace resolve_variant { + +template +struct centroid +{ + template + static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy) + { + concept::check_concepts_and_equal_dimensions(); + resolve_strategy::centroid::apply(geometry, out, strategy); + } +}; + +template +struct centroid > +{ + template + struct visitor: boost::static_visitor + { + Point& m_out; + Strategy const& m_strategy; + + visitor(Point& out, Strategy const& strategy) + : m_out(out), m_strategy(strategy) + {} + + template + void operator()(Geometry const& geometry) const + { + centroid::apply(geometry, m_out, m_strategy); + } + }; + + template + static inline void + apply(boost::variant const& geometry, + Point& out, + Strategy const& strategy) + { + boost::apply_visitor(visitor(out, strategy), geometry); + } +}; + +} // namespace resolve_variant + + /*! \brief \brief_calc{centroid} \brief_strategy \ingroup centroid @@ -357,21 +550,7 @@ template inline void centroid(Geometry const& geometry, Point& c, Strategy const& strategy) { - //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy) ); - - concept::check_concepts_and_equal_dimensions(); - - typedef typename point_type::type point_type; - - // Call dispatch apply method. That one returns true if centroid - // should be taken from state. - dispatch::centroid - < - typename tag::type, - Geometry, - Point, - Strategy - >::apply(geometry, c, strategy); + resolve_variant::centroid::apply(geometry, c, strategy); } @@ -394,24 +573,7 @@ inline void centroid(Geometry const& geometry, Point& c, template inline void centroid(Geometry const& geometry, Point& c) { - concept::check_concepts_and_equal_dimensions(); - - typedef typename strategy::centroid::services::default_strategy - < - typename cs_tag::type, - typename tag_cast - < - typename tag::type, - pointlike_tag, - linear_tag, - areal_tag - >::type, - dimension::type::value, - Point, - Geometry - >::type strategy_type; - - centroid(geometry, c, strategy_type()); + centroid(geometry, c, default_strategy()); } @@ -429,8 +591,6 @@ inline void centroid(Geometry const& geometry, Point& c) template inline Point return_centroid(Geometry const& geometry) { - concept::check_concepts_and_equal_dimensions(); - Point c; centroid(geometry, c); return c; @@ -454,10 +614,6 @@ inline Point return_centroid(Geometry const& geometry) template inline Point return_centroid(Geometry const& geometry, Strategy const& strategy) { - //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy) ); - - concept::check_concepts_and_equal_dimensions(); - Point c; centroid(geometry, c, strategy); return c; diff --git a/boost/geometry/algorithms/clear.hpp b/boost/geometry/algorithms/clear.hpp index d7336587ee..1850816b1b 100644 --- a/boost/geometry/algorithms/clear.hpp +++ b/boost/geometry/algorithms/clear.hpp @@ -14,15 +14,19 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_CLEAR_HPP #define BOOST_GEOMETRY_ALGORITHMS_CLEAR_HPP -#include + #include +#include +#include +#include +#include #include #include #include #include #include - +#include #include @@ -72,6 +76,7 @@ struct no_action } }; + }} // namespace detail::clear #endif // DOXYGEN_NO_DETAIL @@ -84,14 +89,8 @@ template typename Geometry, typename Tag = typename tag_cast::type, multi_tag>::type > -struct clear -{ - BOOST_MPL_ASSERT_MSG - ( - false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE - , (types) - ); -}; +struct clear: not_implemented +{}; // Point/box/segment do not have clear. So specialize to do nothing. template @@ -127,10 +126,48 @@ struct clear {}; +template +struct clear + : detail::clear::collection_clear +{}; + + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH +namespace resolve_variant { + +template +struct clear +{ + static inline void apply(Geometry& geometry) + { + dispatch::clear::apply(geometry); + } +}; + +template +struct clear > +{ + struct visitor: static_visitor + { + template + inline void operator()(Geometry& geometry) const + { + clear::apply(geometry); + } + }; + + static inline void apply(variant& geometry) + { + apply_visitor(visitor(), geometry); + } +}; + +} // namespace resolve_variant + + /*! \brief Clears a linestring, ring or polygon (exterior+interiors) or multi* \details Generic function to clear a geometry. All points will be removed from the collection or collections @@ -149,7 +186,7 @@ inline void clear(Geometry& geometry) { concept::check(); - dispatch::clear::apply(geometry); + resolve_variant::clear::apply(geometry); } diff --git a/boost/geometry/algorithms/comparable_distance.hpp b/boost/geometry/algorithms/comparable_distance.hpp index 3467045ca2..6f009da3ed 100644 --- a/boost/geometry/algorithms/comparable_distance.hpp +++ b/boost/geometry/algorithms/comparable_distance.hpp @@ -1,8 +1,13 @@ // 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. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -14,61 +19,7 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_COMPARABLE_DISTANCE_HPP #define BOOST_GEOMETRY_ALGORITHMS_COMPARABLE_DISTANCE_HPP - -#include - - -namespace boost { namespace geometry -{ - - -/*! -\brief \brief_calc2{comparable distance measurement} -\ingroup distance -\details The free function comparable_distance does not necessarily calculate the distance, - but it calculates a distance measure such that two distances are comparable to each other. - For example: for the Cartesian coordinate system, Pythagoras is used but the square root - is not taken, which makes it faster and the results of two point pairs can still be - compared to each other. -\tparam Geometry1 first geometry type -\tparam Geometry2 second geometry type -\param geometry1 \param_geometry -\param geometry2 \param_geometry -\return \return_calc{comparable distance} - -\qbk{[include reference/algorithms/comparable_distance.qbk]} - */ -template -inline typename default_distance_result::type comparable_distance( - Geometry1 const& geometry1, Geometry2 const& geometry2) -{ - concept::check(); - concept::check(); - - typedef typename point_type::type point1_type; - typedef typename point_type::type point2_type; - - // Define a point-point-distance-strategy - // for either the normal case, either the reversed case - - typedef typename strategy::distance::services::comparable_type - < - typename boost::mpl::if_c - < - geometry::reverse_dispatch - ::type::value, - typename strategy::distance::services::default_strategy - ::type, - typename strategy::distance::services::default_strategy - ::type - >::type - >::type strategy_type; - - return distance(geometry1, geometry2, strategy_type()); -} - - -}} // namespace boost::geometry - +#include +#include #endif // BOOST_GEOMETRY_ALGORITHMS_COMPARABLE_DISTANCE_HPP diff --git a/boost/geometry/algorithms/convert.hpp b/boost/geometry/algorithms/convert.hpp index fbbf74c17f..914ef8f420 100644 --- a/boost/geometry/algorithms/convert.hpp +++ b/boost/geometry/algorithms/convert.hpp @@ -3,6 +3,7 @@ // 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. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -20,6 +21,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -31,19 +36,32 @@ #include #include #include +#include #include #include +#include + #include #include #include +#include + #include namespace boost { namespace geometry { +// Silence warning C4127: conditional expression is constant +// Silence warning C4512: assignment operator could not be generated +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127 4512) +#endif + + #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace conversion { @@ -95,7 +113,7 @@ struct box_to_range assign_box_corners_oriented(box, range); if (Close) { - range[4] = range[0]; + range::at(range, 4) = range::at(range, 0); } } }; @@ -115,22 +133,22 @@ struct segment_to_range } }; -template +template < - typename Range1, - typename Range2, + typename Range1, + typename Range2, bool Reverse = false > struct range_to_range { typedef typename reversible_view < - Range1 const, + Range1 const, Reverse ? iterate_reverse : iterate_forward >::type rview_type; typedef typename closeable_view < - rview_type const, + rview_type const, geometry::closure::value >::type view_type; @@ -144,13 +162,17 @@ struct range_to_range // point for open output. view_type view(rview); - int n = boost::size(view); + typedef typename boost::range_size::type size_type; + size_type n = boost::size(view); if (geometry::closure::value == geometry::open) { n--; } - int i = 0; + // If size == 0 && geometry::open <=> n = numeric_limits::max() + // but ok, sice below it == end() + + size_type i = 0; for (typename boost::range_iterator::type it = boost::begin(view); it != boost::end(view) && i < n; @@ -166,7 +188,7 @@ struct polygon_to_polygon { typedef range_to_range < - typename geometry::ring_type::type, + typename geometry::ring_type::type, typename geometry::ring_type::type, geometry::point_order::value != geometry::point_order::value @@ -176,7 +198,7 @@ struct polygon_to_polygon { // Clearing managed per ring, and in the resizing of interior rings - per_ring::apply(geometry::exterior_ring(source), + per_ring::apply(geometry::exterior_ring(source), geometry::exterior_ring(destination)); // Container should be resizeable @@ -188,13 +210,15 @@ struct polygon_to_polygon >::type >::apply(interior_rings(destination), num_interior_rings(source)); - typename interior_return_type::type rings_source - = interior_rings(source); - typename interior_return_type::type rings_dest - = interior_rings(destination); + typename interior_return_type::type + rings_source = interior_rings(source); + typename interior_return_type::type + rings_dest = interior_rings(destination); - BOOST_AUTO_TPL(it_source, boost::begin(rings_source)); - BOOST_AUTO_TPL(it_dest, boost::begin(rings_dest)); + typename detail::interior_iterator::type + it_source = boost::begin(rings_source); + typename detail::interior_iterator::type + it_dest = boost::begin(rings_dest); for ( ; it_source != boost::end(rings_source); ++it_source, ++it_dest) { @@ -203,6 +227,37 @@ struct polygon_to_polygon } }; +template +struct single_to_multi: private Policy +{ + static inline void apply(Single const& single, Multi& multi) + { + traits::resize::apply(multi, 1); + Policy::apply(single, *boost::begin(multi)); + } +}; + + + +template +struct multi_to_multi: private Policy +{ + static inline void apply(Multi1 const& multi1, Multi2& multi2) + { + traits::resize::apply(multi2, boost::size(multi1)); + + typename boost::range_iterator::type it1 + = boost::begin(multi1); + typename boost::range_iterator::type it2 + = boost::begin(multi2); + + for (; it1 != boost::end(multi1); ++it1, ++it2) + { + Policy::apply(*it1, *it2); + } + } +}; + }} // namespace detail::conversion #endif // DOXYGEN_NO_DETAIL @@ -280,8 +335,8 @@ struct convert struct convert : detail::conversion::range_to_range - < - Ring1, + < + Ring1, Ring2, geometry::point_order::value != geometry::point_order::value @@ -302,8 +357,8 @@ template struct convert : detail::conversion::box_to_range < - Box, - Ring, + Box, + Ring, geometry::closure::value == closed, geometry::point_order::value == counterclockwise > @@ -377,16 +432,108 @@ struct convert }; +// Dispatch for multi <-> multi, specifying their single-version as policy. +// Note that, even if the multi-types are mutually different, their single +// version types might be the same and therefore we call boost::is_same again + +template +struct convert + : detail::conversion::multi_to_multi + < + Multi1, + Multi2, + convert + < + typename boost::range_value::type, + typename boost::range_value::type, + typename single_tag_of + < + typename tag::type + >::type, + typename single_tag_of + < + typename tag::type + >::type, + DimensionCount + > + > +{}; + + +template +struct convert + : detail::conversion::single_to_multi + < + Single, + Multi, + convert + < + Single, + typename boost::range_value::type, + typename tag::type, + typename single_tag_of + < + typename tag::type + >::type, + DimensionCount, + false + > + > +{}; + + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH +namespace resolve_variant { + +template +struct convert +{ + static inline void apply(Geometry1 const& geometry1, Geometry2& geometry2) + { + concept::check_concepts_and_equal_dimensions(); + dispatch::convert::apply(geometry1, geometry2); + } +}; + +template +struct convert, Geometry2> +{ + struct visitor: static_visitor + { + Geometry2& m_geometry2; + + visitor(Geometry2& geometry2) + : m_geometry2(geometry2) + {} + + template + inline void operator()(Geometry1 const& geometry1) const + { + convert::apply(geometry1, m_geometry2); + } + }; + + static inline void apply( + boost::variant const& geometry1, + Geometry2& geometry2 + ) + { + apply_visitor(visitor(geometry2), geometry1); + } +}; + +} + + /*! \brief Converts one geometry to another geometry \details The convert algorithm converts one geometry, e.g. a BOX, to another -geometry, e.g. a RING. This only if it is possible and applicable. -If the point-order is different, or the closure is different between two -geometry types, it will be converted correctly by explicitly reversing the +geometry, e.g. a RING. This only works if it is possible and applicable. +If the point-order is different, or the closure is different between two +geometry types, it will be converted correctly by explicitly reversing the points or closing or opening the polygon rings. \ingroup convert \tparam Geometry1 \tparam_geometry @@ -399,13 +546,13 @@ points or closing or opening the polygon rings. template inline void convert(Geometry1 const& geometry1, Geometry2& geometry2) { - concept::check_concepts_and_equal_dimensions(); - - dispatch::convert::apply(geometry1, geometry2); + resolve_variant::convert::apply(geometry1, geometry2); } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif }} // namespace boost::geometry - #endif // BOOST_GEOMETRY_ALGORITHMS_CONVERT_HPP diff --git a/boost/geometry/algorithms/convex_hull.hpp b/boost/geometry/algorithms/convex_hull.hpp index 56b87c8c15..09f4c5142d 100644 --- a/boost/geometry/algorithms/convex_hull.hpp +++ b/boost/geometry/algorithms/convex_hull.hpp @@ -4,6 +4,11 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -15,15 +20,20 @@ #define BOOST_GEOMETRY_ALGORITHMS_CONVEX_HULL_HPP #include +#include +#include +#include #include #include +#include #include #include #include #include +#include #include @@ -40,45 +50,34 @@ namespace boost { namespace geometry namespace detail { namespace convex_hull { -template -< - typename Geometry, - order_selector Order, - typename Strategy -> +template struct hull_insert { // Member template function (to avoid inconvenient declaration // of output-iterator-type, from hull_to_geometry) - template + template static inline OutputIterator apply(Geometry const& geometry, OutputIterator out, Strategy const& strategy) { typename Strategy::state_type state; strategy.apply(geometry, state); - strategy.result(state, out, Order == clockwise); + strategy.result(state, out, Order == clockwise, Closure != open); return out; } }; -template -< - typename Geometry, - typename Strategy -> struct hull_to_geometry { - template + template static inline void apply(Geometry const& geometry, OutputGeometry& out, Strategy const& strategy) { hull_insert < - Geometry, geometry::point_order::value, - Strategy + geometry::closure::value >::apply(geometry, std::back_inserter( // Handle linestring, ring and polygon the same: @@ -89,18 +88,6 @@ struct hull_to_geometry } }; - -// Helper metafunction for default strategy retrieval -template -struct default_strategy - : strategy_convex_hull - < - Geometry, - typename point_type::type - > -{}; - - }} // namespace detail::convex_hull #endif // DOXYGEN_NO_DETAIL @@ -113,21 +100,16 @@ namespace dispatch template < typename Geometry, - typename Strategy = typename detail::convex_hull::default_strategy::type, typename Tag = typename tag::type > struct convex_hull - : detail::convex_hull::hull_to_geometry + : detail::convex_hull::hull_to_geometry {}; -template -< - typename Box, - typename Strategy -> -struct convex_hull +template +struct convex_hull { - template + template static inline void apply(Box const& box, OutputGeometry& out, Strategy const& ) { @@ -149,13 +131,9 @@ struct convex_hull -template -< - order_selector Order, - typename Geometry, typename Strategy -> +template struct convex_hull_insert - : detail::convex_hull::hull_insert + : detail::convex_hull::hull_insert {}; @@ -163,29 +141,170 @@ struct convex_hull_insert #endif // DOXYGEN_NO_DISPATCH -template -inline void convex_hull(Geometry const& geometry, - OutputGeometry& out, Strategy const& strategy) +namespace resolve_strategy { + +struct convex_hull { - concept::check_concepts_and_equal_dimensions - < + template + static inline void apply(Geometry const& geometry, + OutputGeometry& out, + Strategy const& strategy) + { + BOOST_CONCEPT_ASSERT( (geometry::concept::ConvexHullStrategy) ); + dispatch::convex_hull::apply(geometry, out, strategy); + } + + template + static inline void apply(Geometry const& geometry, + OutputGeometry& out, + default_strategy) + { + typedef typename strategy_convex_hull< + Geometry, + typename point_type::type + >::type strategy_type; + + apply(geometry, out, strategy_type()); + } +}; + +struct convex_hull_insert +{ + template + static inline OutputIterator apply(Geometry const& geometry, + OutputIterator& out, + Strategy const& strategy) + { + BOOST_CONCEPT_ASSERT( (geometry::concept::ConvexHullStrategy) ); + + return dispatch::convex_hull_insert< + geometry::point_order::value, + geometry::closure::value + >::apply(geometry, out, strategy); + } + + template + static inline OutputIterator apply(Geometry const& geometry, + OutputIterator& out, + default_strategy) + { + typedef typename strategy_convex_hull< + Geometry, + typename point_type::type + >::type strategy_type; + + return apply(geometry, out, strategy_type()); + } +}; + +} // namespace resolve_strategy + + +namespace resolve_variant { + +template +struct convex_hull +{ + template + static inline void apply(Geometry const& geometry, OutputGeometry& out, Strategy const& strategy) + { + concept::check_concepts_and_equal_dimensions< const Geometry, OutputGeometry >(); - BOOST_CONCEPT_ASSERT( (geometry::concept::ConvexHullStrategy) ); + resolve_strategy::convex_hull::apply(geometry, out, strategy); + } +}; + +template +struct convex_hull > +{ + template + struct visitor: boost::static_visitor + { + OutputGeometry& m_out; + Strategy const& m_strategy; + + visitor(OutputGeometry& out, Strategy const& strategy) + : m_out(out), m_strategy(strategy) + {} + + template + void operator()(Geometry const& geometry) const + { + convex_hull::apply(geometry, m_out, m_strategy); + } + }; + + template + static inline void + apply(boost::variant const& geometry, + OutputGeometry& out, + Strategy const& strategy) + { + boost::apply_visitor(visitor(out, strategy), geometry); + } +}; + +template +struct convex_hull_insert +{ + template + static inline OutputIterator apply(Geometry const& geometry, OutputIterator& out, Strategy const& strategy) + { + // Concept: output point type = point type of input geometry + concept::check(); + concept::check::type>(); + + return resolve_strategy::convex_hull_insert::apply(geometry, out, strategy); + } +}; + +template +struct convex_hull_insert > +{ + template + struct visitor: boost::static_visitor + { + OutputIterator& m_out; + Strategy const& m_strategy; + + visitor(OutputIterator& out, Strategy const& strategy) + : m_out(out), m_strategy(strategy) + {} + template + OutputIterator operator()(Geometry const& geometry) const + { + return convex_hull_insert::apply(geometry, m_out, m_strategy); + } + }; + + template + static inline OutputIterator + apply(boost::variant const& geometry, + OutputIterator& out, + Strategy const& strategy) + { + return boost::apply_visitor(visitor(out, strategy), geometry); + } +}; + +} // namespace resolve_variant + + +template +inline void convex_hull(Geometry const& geometry, + OutputGeometry& out, Strategy const& strategy) +{ if (geometry::num_points(geometry) == 0) { // Leave output empty return; } - dispatch::convex_hull - < - Geometry, - Strategy - >::apply(geometry, out, strategy); + resolve_variant::convex_hull::apply(geometry, out, strategy); } @@ -193,8 +312,8 @@ inline void convex_hull(Geometry const& geometry, \brief \brief_calc{convex hull} \ingroup convex_hull \details \details_calc{convex_hull,convex hull}. -\tparam Geometry1 \tparam_geometry -\tparam Geometry2 \tparam_geometry +\tparam Geometry the input geometry type +\tparam OutputGeometry the output geometry type \param geometry \param_geometry, input geometry \param hull \param_geometry \param_set{convex hull} @@ -204,15 +323,7 @@ template inline void convex_hull(Geometry const& geometry, OutputGeometry& hull) { - concept::check_concepts_and_equal_dimensions - < - const Geometry, - OutputGeometry - >(); - - typedef typename detail::convex_hull::default_strategy::type strategy_type; - - convex_hull(geometry, hull, strategy_type()); + convex_hull(geometry, hull, default_strategy()); } #ifndef DOXYGEN_NO_DETAIL @@ -224,17 +335,8 @@ template inline OutputIterator convex_hull_insert(Geometry const& geometry, OutputIterator out, Strategy const& strategy) { - // Concept: output point type = point type of input geometry - concept::check(); - concept::check::type>(); - - BOOST_CONCEPT_ASSERT( (geometry::concept::ConvexHullStrategy) ); - - return dispatch::convex_hull_insert - < - geometry::point_order::value, - Geometry, Strategy - >::apply(geometry, out, strategy); + return resolve_variant::convex_hull_insert + ::apply(geometry, out, strategy); } @@ -255,13 +357,7 @@ template inline OutputIterator convex_hull_insert(Geometry const& geometry, OutputIterator out) { - // Concept: output point type = point type of input geometry - concept::check(); - concept::check::type>(); - - typedef typename detail::convex_hull::default_strategy::type strategy_type; - - return convex_hull_insert(geometry, out, strategy_type()); + return convex_hull_insert(geometry, out, default_strategy()); } diff --git a/boost/geometry/algorithms/correct.hpp b/boost/geometry/algorithms/correct.hpp index 583e395f8e..3c61b2c0d2 100644 --- a/boost/geometry/algorithms/correct.hpp +++ b/boost/geometry/algorithms/correct.hpp @@ -3,6 +3,7 @@ // 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. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -21,25 +22,37 @@ #include #include -#include +#include +#include +#include +#include + +#include #include #include -#include -#include #include #include +#include +#include +#include #include #include #include +#include #include - namespace boost { namespace geometry { +// Silence warning C4127: conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace correct { @@ -119,10 +132,8 @@ struct correct_ring typedef detail::area::ring_area < - Ring, order_as_direction::value>::value, - geometry::closure::value, - strategy_type + geometry::closure::value > ring_area_type; @@ -139,7 +150,7 @@ struct correct_ring { geometry::append(r, *boost::begin(r)); } - if (! disjoint && geometry::closure::value != closed) + if (! disjoint && s != closed) { // Open it by removing last point geometry::traits::resize::apply(r, boost::size(r) - 1); @@ -172,9 +183,10 @@ struct correct_polygon std::less >::apply(exterior_ring(poly)); - typename interior_return_type::type rings - = interior_rings(poly); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + typename interior_return_type::type + rings = interior_rings(poly); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) { correct_ring < @@ -234,10 +246,69 @@ struct correct {}; +template +struct correct + : detail::correct::correct_nop +{}; + + +template +struct correct + : detail::correct::correct_nop +{}; + + +template +struct correct + : detail::multi_modify + < + Geometry, + detail::correct::correct_polygon + < + typename boost::range_value::type + > + > +{}; + + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH +namespace resolve_variant { + +template +struct correct +{ + static inline void apply(Geometry& geometry) + { + concept::check(); + dispatch::correct::apply(geometry); + } +}; + +template +struct correct > +{ + struct visitor: boost::static_visitor + { + template + void operator()(Geometry& geometry) const + { + correct::apply(geometry); + } + }; + + static inline void + apply(boost::variant& geometry) + { + boost::apply_visitor(visitor(), geometry); + } +}; + +} // namespace resolve_variant + + /*! \brief Corrects a geometry \details Corrects a geometry: all rings which are wrongly oriented with respect @@ -253,11 +324,12 @@ struct correct template inline void correct(Geometry& geometry) { - concept::check(); - - dispatch::correct::apply(geometry); + resolve_variant::correct::apply(geometry); } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif }} // namespace boost::geometry diff --git a/boost/geometry/algorithms/covered_by.hpp b/boost/geometry/algorithms/covered_by.hpp index c3c406c4ca..e50dc338af 100644 --- a/boost/geometry/algorithms/covered_by.hpp +++ b/boost/geometry/algorithms/covered_by.hpp @@ -4,6 +4,9 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013, 2014 Oracle and/or its affiliates. + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -11,21 +14,52 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + #ifndef BOOST_GEOMETRY_ALGORITHMS_COVERED_BY_HPP #define BOOST_GEOMETRY_ALGORITHMS_COVERED_BY_HPP #include +#include +#include +#include + #include #include #include #include +#include namespace boost { namespace geometry { +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace covered_by { + +struct use_point_in_geometry +{ + template + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) + { + return detail::within::point_in_geometry(geometry1, geometry2, strategy) >= 0; + } +}; + +struct use_relate +{ + template + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& /*strategy*/) + { + return Strategy::apply(geometry1, geometry2); + } +}; + +}} // namespace detail::covered_by +#endif // DOXYGEN_NO_DETAIL + #ifndef DOXYGEN_NO_DISPATCH namespace dispatch { @@ -37,7 +71,8 @@ template typename Tag1 = typename tag::type, typename Tag2 = typename tag::type > -struct covered_by: not_implemented +struct covered_by + : not_implemented {}; @@ -47,6 +82,7 @@ struct covered_by template static inline bool apply(Point const& point, Box const& box, Strategy const& strategy) { + ::boost::ignore_unused_variable_warning(strategy); return strategy.apply(point, box); } }; @@ -58,48 +94,332 @@ struct covered_by static inline bool apply(Box1 const& box1, Box2 const& box2, Strategy const& strategy) { assert_dimension_equal(); + ::boost::ignore_unused_variable_warning(strategy); return strategy.apply(box1, box2); } }; +// P/P + +template +struct covered_by + : public detail::covered_by::use_point_in_geometry +{}; + +template +struct covered_by + : public detail::covered_by::use_point_in_geometry +{}; + +// P/L + +template +struct covered_by + : public detail::covered_by::use_point_in_geometry +{}; + +template +struct covered_by + : public detail::covered_by::use_point_in_geometry +{}; + +template +struct covered_by + : public detail::covered_by::use_point_in_geometry +{}; + +// P/A template struct covered_by + : public detail::covered_by::use_point_in_geometry +{}; + +template +struct covered_by + : public detail::covered_by::use_point_in_geometry +{}; + +template +struct covered_by + : public detail::covered_by::use_point_in_geometry +{}; + +// L/L + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +// L/A + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +// A/A + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +template +struct covered_by + : public detail::covered_by::use_relate +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_strategy { + +struct covered_by { - template - static inline bool apply(Point const& point, Ring const& ring, Strategy const& strategy) + template + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) { - return detail::within::point_in_ring + concept::within::check < - Point, - Ring, - order_as_direction::value>::value, - geometry::closure::value, + typename tag::type, + typename tag::type, + typename tag_cast::type, areal_tag>::type, Strategy - >::apply(point, ring, strategy) >= 0; + >(); + concept::check(); + concept::check(); + assert_dimension_equal(); + + return dispatch::covered_by::apply(geometry1, + geometry2, + strategy); + } + + template + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + default_strategy) + { + typedef typename point_type::type point_type1; + typedef typename point_type::type point_type2; + + typedef typename strategy::covered_by::services::default_strategy + < + typename tag::type, + typename tag::type, + typename tag::type, + typename tag_cast::type, areal_tag>::type, + typename tag_cast + < + typename cs_tag::type, spherical_tag + >::type, + typename tag_cast + < + typename cs_tag::type, spherical_tag + >::type, + Geometry1, + Geometry2 + >::type strategy_type; + + return covered_by::apply(geometry1, geometry2, strategy_type()); } }; -template -struct covered_by +} // namespace resolve_strategy + + +namespace resolve_variant { + +template +struct covered_by { template - static inline bool apply(Point const& point, Polygon const& polygon, Strategy const& strategy) + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) { - return detail::within::point_in_polygon - < - Point, - Polygon, - order_as_direction::value>::value, - geometry::closure::value, - Strategy - >::apply(point, polygon, strategy) >= 0; + return resolve_strategy::covered_by + ::apply(geometry1, geometry2, strategy); } }; -} // namespace dispatch -#endif // DOXYGEN_NO_DISPATCH +template +struct covered_by, Geometry2> +{ + template + struct visitor: boost::static_visitor + { + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, Strategy const& strategy) + : m_geometry2(geometry2), m_strategy(strategy) {} + + template + bool operator()(Geometry1 const& geometry1) const + { + return covered_by + ::apply(geometry1, m_geometry2, m_strategy); + } + }; + + template + static inline bool + apply(boost::variant const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor(geometry2, strategy), geometry1); + } +}; + +template +struct covered_by > +{ + template + struct visitor: boost::static_visitor + { + Geometry1 const& m_geometry1; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, Strategy const& strategy) + : m_geometry1(geometry1), m_strategy(strategy) {} + + template + bool operator()(Geometry2 const& geometry2) const + { + return covered_by + ::apply(m_geometry1, geometry2, m_strategy); + } + }; + + template + static inline bool + apply(Geometry1 const& geometry1, + boost::variant const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor(geometry1, strategy), geometry2); + } +}; + +template < + BOOST_VARIANT_ENUM_PARAMS(typename T1), + BOOST_VARIANT_ENUM_PARAMS(typename T2) +> +struct covered_by< + boost::variant, + boost::variant +> +{ + template + struct visitor: boost::static_visitor + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy): m_strategy(strategy) {} + + template + bool operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + return covered_by + ::apply(geometry1, geometry2, m_strategy); + } + }; + + template + static inline bool + apply(boost::variant const& geometry1, + boost::variant const& geometry2, + Strategy const& strategy) + { + return boost::apply_visitor(visitor(strategy), geometry1, geometry2); + } +}; + +} // namespace resolve_variant /*! @@ -120,36 +440,8 @@ struct covered_by template inline bool covered_by(Geometry1 const& geometry1, Geometry2 const& geometry2) { - concept::check(); - concept::check(); - assert_dimension_equal(); - - typedef typename point_type::type point_type1; - typedef typename point_type::type point_type2; - - typedef typename strategy::covered_by::services::default_strategy - < - typename tag::type, - typename tag::type, - typename tag::type, - typename tag_cast::type, areal_tag>::type, - typename tag_cast - < - typename cs_tag::type, spherical_tag - >::type, - typename tag_cast - < - typename cs_tag::type, spherical_tag - >::type, - Geometry1, - Geometry2 - >::type strategy_type; - - return dispatch::covered_by - < - Geometry1, - Geometry2 - >::apply(geometry1, geometry2, strategy_type()); + return resolve_variant::covered_by + ::apply(geometry1, geometry2, default_strategy()); } /*! @@ -172,22 +464,8 @@ template inline bool covered_by(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) { - concept::within::check - < - typename tag::type, - typename tag::type, - typename tag_cast::type, areal_tag>::type, - Strategy - >(); - concept::check(); - concept::check(); - assert_dimension_equal(); - - return dispatch::covered_by - < - Geometry1, - Geometry2 - >::apply(geometry1, geometry2, strategy); + return resolve_variant::covered_by + ::apply(geometry1, geometry2, strategy); } }} // namespace boost::geometry diff --git a/boost/geometry/algorithms/crosses.hpp b/boost/geometry/algorithms/crosses.hpp new file mode 100644 index 0000000000..91ed3e0806 --- /dev/null +++ b/boost/geometry/algorithms/crosses.hpp @@ -0,0 +1,194 @@ +// 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. +// Copyright (c) 2014 Samuel Debionne, Grenoble, France. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_CROSSES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_CROSSES_HPP + +#include +#include + +#include + +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1 = typename tag::type, + typename Tag2 = typename tag::type +> +struct crosses + : detail::relate::relate_base + < + detail::relate::static_mask_crosses_type, + Geometry1, + Geometry2 + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_variant +{ + template + struct crosses + { + static inline bool + apply( + const Geometry1& geometry1, + const Geometry2& geometry2) + { + concept::check(); + concept::check(); + + return dispatch::crosses::apply(geometry1, geometry2); + } + }; + + + template + struct crosses, Geometry2> + { + struct visitor: static_visitor + { + Geometry2 const& m_geometry2; + + visitor(Geometry2 const& geometry2) + : m_geometry2(geometry2) + {} + + template + result_type operator()(Geometry1 const& geometry1) const + { + return crosses + < + Geometry1, + Geometry2 + >::apply + (geometry1, m_geometry2); + } + }; + + static inline bool + apply(variant const& geometry1, + Geometry2 const& geometry2) + { + return apply_visitor(visitor(geometry2), geometry1); + } + }; + + + template + struct crosses > + { + struct visitor: static_visitor + { + Geometry1 const& m_geometry1; + + visitor(Geometry1 const& geometry1) + : m_geometry1(geometry1) + {} + + template + result_type operator()(Geometry2 const& geometry2) const + { + return crosses + < + Geometry1, + Geometry2 + >::apply + (m_geometry1, geometry2); + } + }; + + static inline bool + apply( + Geometry1 const& geometry1, + const variant& geometry2) + { + return apply_visitor(visitor(geometry1), geometry2); + } + }; + + + template + struct crosses, variant > + { + struct visitor: static_visitor + { + template + result_type operator()( + Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + return crosses + < + Geometry1, + Geometry2 + >::apply + (geometry1, geometry2); + } + }; + + static inline bool + apply( + const variant& geometry1, + const variant& geometry2) + { + return apply_visitor(visitor(), geometry1, geometry2); + } + }; + +} // namespace resolve_variant + + +/*! +\brief \brief_check2{crosses} +\ingroup crosses +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{crosses} + +\qbk{[include reference/algorithms/crosses.qbk]} +*/ +template +inline bool crosses(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + return resolve_variant::crosses::apply(geometry1, geometry2); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_CROSSES_HPP diff --git a/boost/geometry/algorithms/detail/assign_box_corners.hpp b/boost/geometry/algorithms/detail/assign_box_corners.hpp index 1fd41733f2..669d6d3655 100644 --- a/boost/geometry/algorithms/detail/assign_box_corners.hpp +++ b/boost/geometry/algorithms/detail/assign_box_corners.hpp @@ -19,14 +19,14 @@ #include #include - +#include namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL -namespace detail +namespace detail { // Note: this is moved to namespace detail because the names and parameter orders // are not yet 100% clear. @@ -67,20 +67,34 @@ inline void assign_box_corners(Box const& box, (box, upper_right); } +// Silence warning C4127: conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + + template inline void assign_box_corners_oriented(Box const& box, Range& corners) { if (Reverse) { // make counterclockwise ll,lr,ur,ul - assign_box_corners(box, corners[0], corners[1], corners[3], corners[2]); + assign_box_corners(box, + range::at(corners, 0), range::at(corners, 1), + range::at(corners, 3), range::at(corners, 2)); } else { // make clockwise ll,ul,ur,lr - assign_box_corners(box, corners[0], corners[3], corners[1], corners[2]); + assign_box_corners(box, + range::at(corners, 0), range::at(corners, 3), + range::at(corners, 1), range::at(corners, 2)); } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif } // namespace detail diff --git a/boost/geometry/algorithms/detail/assign_indexed_point.hpp b/boost/geometry/algorithms/detail/assign_indexed_point.hpp index a1cffb80a7..acfc37e250 100644 --- a/boost/geometry/algorithms/detail/assign_indexed_point.hpp +++ b/boost/geometry/algorithms/detail/assign_indexed_point.hpp @@ -25,7 +25,7 @@ namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL -namespace detail +namespace detail { /*! diff --git a/boost/geometry/algorithms/detail/assign_values.hpp b/boost/geometry/algorithms/detail/assign_values.hpp index ed4713493f..5e4a1795b5 100644 --- a/boost/geometry/algorithms/detail/assign_values.hpp +++ b/boost/geometry/algorithms/detail/assign_values.hpp @@ -46,36 +46,30 @@ namespace detail { namespace assign { -template -< - typename Box, std::size_t Index, - std::size_t Dimension, std::size_t DimensionCount -> +template struct initialize { - typedef typename coordinate_type::type coordinate_type; - - static inline void apply(Box& box, coordinate_type const& value) + template + static inline void apply(Box& box, typename coordinate_type::type const& value) { geometry::set(box, value); - initialize::apply(box, value); + initialize::apply(box, value); } }; -template -struct initialize +template +struct initialize { - typedef typename coordinate_type::type coordinate_type; - - static inline void apply(Box&, coordinate_type const& ) + template + static inline void apply(Box&, typename coordinate_type::type const&) {} }; -template struct assign_zero_point { + template static inline void apply(Point& point) { geometry::assign_value(point, 0); @@ -83,44 +77,38 @@ struct assign_zero_point }; -template struct assign_inverse_box_or_segment { - typedef typename point_type::type point_type; + template static inline void apply(BoxOrSegment& geometry) { + typedef typename point_type::type point_type; typedef typename coordinate_type::type bound_type; - initialize - < - BoxOrSegment, 0, 0, dimension::type::value - >::apply( - geometry, boost::numeric::bounds::highest()); - initialize - < - BoxOrSegment, 1, 0, dimension::type::value - >::apply( - geometry, boost::numeric::bounds::lowest()); + initialize<0, 0, dimension::type::value>::apply( + geometry, boost::numeric::bounds::highest() + ); + initialize<1, 0, dimension::type::value>::apply( + geometry, boost::numeric::bounds::lowest() + ); } }; -template struct assign_zero_box_or_segment { + template static inline void apply(BoxOrSegment& geometry) { typedef typename coordinate_type::type coordinate_type; - initialize - < - BoxOrSegment, 0, 0, dimension::type::value - >::apply(geometry, coordinate_type()); - initialize - < - BoxOrSegment, 1, 0, dimension::type::value - >::apply(geometry, coordinate_type()); + initialize<0, 0, dimension::type::value>::apply( + geometry, coordinate_type() + ); + initialize<1, 0, dimension::type::value>::apply( + geometry, coordinate_type() + ); } }; @@ -312,17 +300,17 @@ struct assign_zero {}; template struct assign_zero - : detail::assign::assign_zero_point + : detail::assign::assign_zero_point {}; template struct assign_zero - : detail::assign::assign_zero_box_or_segment + : detail::assign::assign_zero_box_or_segment {}; template struct assign_zero - : detail::assign::assign_zero_box_or_segment + : detail::assign::assign_zero_box_or_segment {}; @@ -331,112 +319,18 @@ struct assign_inverse {}; template struct assign_inverse - : detail::assign::assign_inverse_box_or_segment + : detail::assign::assign_inverse_box_or_segment {}; template struct assign_inverse - : detail::assign::assign_inverse_box_or_segment + : detail::assign::assign_inverse_box_or_segment {}; } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH - -/*! -\brief Assign two coordinates to a geometry (usually a 2D point) -\ingroup assign -\tparam Geometry \tparam_geometry -\tparam Type \tparam_numeric to specify the coordinates -\param geometry \param_geometry -\param c1 \param_x -\param c2 \param_y - -\qbk{distinguish, 2 coordinate values} -\qbk{ -[heading Example] -[assign_2d_point] [assign_2d_point_output] - -[heading See also] -\* [link geometry.reference.algorithms.make.make_2_2_coordinate_values make] -} - */ -template -inline void assign_values(Geometry& geometry, Type const& c1, Type const& c2) -{ - concept::check(); - - dispatch::assign - < - typename tag::type, - Geometry, - geometry::dimension::type::value - >::apply(geometry, c1, c2); -} - -/*! -\brief Assign three values to a geometry (usually a 3D point) -\ingroup assign -\tparam Geometry \tparam_geometry -\tparam Type \tparam_numeric to specify the coordinates -\param geometry \param_geometry -\param c1 \param_x -\param c2 \param_y -\param c3 \param_z - -\qbk{distinguish, 3 coordinate values} -\qbk{ -[heading Example] -[assign_3d_point] [assign_3d_point_output] - -[heading See also] -\* [link geometry.reference.algorithms.make.make_3_3_coordinate_values make] -} - */ -template -inline void assign_values(Geometry& geometry, - Type const& c1, Type const& c2, Type const& c3) -{ - concept::check(); - - dispatch::assign - < - typename tag::type, - Geometry, - geometry::dimension::type::value - >::apply(geometry, c1, c2, c3); -} - -/*! -\brief Assign four values to a geometry (usually a box or segment) -\ingroup assign -\tparam Geometry \tparam_geometry -\tparam Type \tparam_numeric to specify the coordinates -\param geometry \param_geometry -\param c1 First coordinate (usually x1) -\param c2 Second coordinate (usually y1) -\param c3 Third coordinate (usually x2) -\param c4 Fourth coordinate (usually y2) - -\qbk{distinguish, 4 coordinate values} - */ -template -inline void assign_values(Geometry& geometry, - Type const& c1, Type const& c2, Type const& c3, Type const& c4) -{ - concept::check(); - - dispatch::assign - < - typename tag::type, - Geometry, - geometry::dimension::type::value - >::apply(geometry, c1, c2, c3, c4); -} - - - }} // namespace boost::geometry diff --git a/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp new file mode 100644 index 0000000000..c959ee849b --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -0,0 +1,940 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_INSERTER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_INSERTER_HPP + +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#if defined(BOOST_GEOMETRY_BUFFER_SIMPLIFY_WITH_AX) +#include +#endif + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +template +inline void simplify_input(Range const& range, + DistanceStrategy const& distance, + Range& simplified) +{ + // We have to simplify the ring before to avoid very small-scaled + // features in the original (convex/concave/convex) being enlarged + // in a very large scale and causing issues (IP's within pieces). + // This might be reconsidered later. Simplifying with a very small + // distance (1%% of the buffer) will never be visible in the result, + // if it is using round joins. For miter joins they are even more + // sensitive to small scale input features, however the result will + // look better. + // It also gets rid of duplicate points +#if ! defined(BOOST_GEOMETRY_BUFFER_SIMPLIFY_WITH_AX) + geometry::simplify(range, simplified, distance.simplify_distance()); +#else + + typedef typename boost::range_value::type point_type; + typedef strategy::distance::detail::projected_point_ax<> ax_type; + typedef typename strategy::distance::services::return_type + < + strategy::distance::detail::projected_point_ax<>, + point_type, + point_type + >::type return_type; + + typedef strategy::distance::detail::projected_point_ax_less + < + return_type + > comparator_type; + + typedef strategy::simplify::detail::douglas_peucker + < + point_type, + strategy::distance::detail::projected_point_ax<>, + comparator_type + > dp_ax; + + return_type max_distance(distance.simplify_distance() * 2.0, + distance.simplify_distance()); + comparator_type comparator(max_distance); + dp_ax strategy(comparator); + + geometry::simplify(range, simplified, max_distance, strategy); +#endif + + if (boost::size(simplified) == 2 + && geometry::equals(geometry::range::front(simplified), + geometry::range::back(simplified))) + { + traits::resize::apply(simplified, 1); + } +} + + +template +struct buffer_range +{ + typedef typename point_type::type output_point_type; + typedef typename coordinate_type::type coordinate_type; + + template + < + typename Collection, + typename Point, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline + void add_join(Collection& collection, + Point const& penultimate_input, + Point const& previous_input, + output_point_type const& prev_perp1, + output_point_type const& prev_perp2, + Point const& input, + output_point_type const& perp1, + output_point_type const& perp2, + strategy::buffer::buffer_side_selector side, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& ) + { + output_point_type intersection_point; + + strategy::buffer::join_selector join + = get_join_type(penultimate_input, previous_input, input); + if (join == strategy::buffer::join_convex) + { + // Calculate the intersection-point formed by the two sides. + // It might be that the two sides are not convex, but continue + // or spikey, we then change the join-type + join = line_line_intersection::apply( + perp1, perp2, prev_perp1, prev_perp2, + intersection_point); + + } + switch(join) + { + case strategy::buffer::join_continue : + // No join, we get two consecutive sides + return; + case strategy::buffer::join_concave : + collection.add_piece(strategy::buffer::buffered_concave, + previous_input, prev_perp2, perp1); + return; + case strategy::buffer::join_spike : + { + // For linestrings, only add spike at one side to avoid + // duplicates + std::vector range_out; + end_strategy.apply(penultimate_input, prev_perp2, previous_input, perp1, side, distance, range_out); + collection.add_endcap(end_strategy, range_out, previous_input); + } + return; + case strategy::buffer::join_convex : + break; // All code below handles this + } + + // The corner is convex, we create a join + // TODO (future) - avoid a separate vector, add the piece directly + std::vector range_out; + if (join_strategy.apply(intersection_point, + previous_input, prev_perp2, perp1, + distance.apply(previous_input, input, side), + range_out)) + { + collection.add_piece(strategy::buffer::buffered_join, + previous_input, range_out); + } + } + + static inline strategy::buffer::join_selector get_join_type( + output_point_type const& p0, + output_point_type const& p1, + output_point_type const& p2) + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + int const side = side_strategy::apply(p0, p1, p2); + return side == -1 ? strategy::buffer::join_convex + : side == 1 ? strategy::buffer::join_concave + : parallel_continue + ( + get<0>(p2) - get<0>(p1), + get<1>(p2) - get<1>(p1), + get<0>(p1) - get<0>(p0), + get<1>(p1) - get<1>(p0) + ) ? strategy::buffer::join_continue + : strategy::buffer::join_spike; + } + + template + < + typename Collection, + typename Iterator, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline bool iterate(Collection& collection, + Iterator begin, Iterator end, + strategy::buffer::buffer_side_selector side, + DistanceStrategy const& distance_strategy, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy, + output_point_type& first_p1, + output_point_type& first_p2, + output_point_type& last_p1, + output_point_type& last_p2) + { + typedef typename std::iterator_traits + < + Iterator + >::value_type point_type; + + typedef typename robust_point_type + < + point_type, + RobustPolicy + >::type robust_point_type; + + robust_point_type previous_robust_input; + point_type second_point, penultimate_point, ultimate_point; // last two points from begin/end + + /* + * last.p1 last.p2 these are the "previous (last) perpendicular points" + * -------------- + * | | + * *------------*____ <- *prev + * pup | | p1 "current perpendicular point 1" + * | | + * | | this forms a "side", a side is a piece + * | | + * *____| p2 + * + * ^ + * *it + * + * pup: penultimate_point + */ + + bool result = false; + bool first = true; + + Iterator it = begin; + + geometry::recalculate(previous_robust_input, *begin, robust_policy); + + std::vector generated_side; + generated_side.reserve(2); + + for (Iterator prev = it++; it != end; ++it) + { + robust_point_type robust_input; + geometry::recalculate(robust_input, *it, robust_policy); + // Check on equality - however, if input is simplified, this is + // unlikely (though possible by rescaling or for degenerated pointlike polygons) + if (! detail::equals::equals_point_point(previous_robust_input, robust_input)) + { + generated_side.clear(); + side_strategy.apply(*prev, *it, side, + distance_strategy, generated_side); + + if (generated_side.empty()) + { + break; + } + + result = true; + + if (! first) + { + add_join(collection, + penultimate_point, + *prev, last_p1, last_p2, + *it, generated_side.front(), generated_side.back(), + side, + distance_strategy, join_strategy, end_strategy, + robust_policy); + } + + collection.add_side_piece(*prev, *it, generated_side, first); + + penultimate_point = *prev; + ultimate_point = *it; + last_p1 = generated_side.front(); + last_p2 = generated_side.back(); + prev = it; + if (first) + { + first = false; + second_point = *it; + first_p1 = generated_side.front(); + first_p2 = generated_side.back(); + } + } + previous_robust_input = robust_input; + } + return result; + } +}; + +template +< + typename Multi, + typename PolygonOutput, + typename Policy +> +struct buffer_multi +{ + template + < + typename Collection, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy, + typename RobustPolicy + > + static inline void apply(Multi const& multi, + Collection& collection, + DistanceStrategy const& distance_strategy, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + PointStrategy const& point_strategy, + RobustPolicy const& robust_policy) + { + for (typename boost::range_iterator::type + it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + Policy::apply(*it, collection, + distance_strategy, side_strategy, + join_strategy, end_strategy, point_strategy, + robust_policy); + } + } +}; + +struct visit_pieces_default_policy +{ + template + static inline void apply(Collection const&, int) + {} +}; + +template +< + typename OutputPointType, + typename Point, + typename Collection, + typename DistanceStrategy, + typename PointStrategy +> +inline void buffer_point(Point const& point, Collection& collection, + DistanceStrategy const& distance_strategy, + PointStrategy const& point_strategy) +{ + collection.start_new_ring(); + std::vector range_out; + point_strategy.apply(point, distance_strategy, range_out); + collection.add_piece(strategy::buffer::buffered_point, range_out, false); + collection.finish_ring(); +} + + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag, + typename RingInput, + typename RingOutput +> +struct buffer_inserter +{}; + + + +template +< + typename Point, + typename RingOutput +> +struct buffer_inserter +{ + template + < + typename Collection, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy, + typename RobustPolicy + > + static inline void apply(Point const& point, Collection& collection, + DistanceStrategy const& distance_strategy, + SideStrategy const& , + JoinStrategy const& , + EndStrategy const& , + PointStrategy const& point_strategy, + RobustPolicy const& ) + { + detail::buffer::buffer_point + < + typename point_type::type + >(point, collection, distance_strategy, point_strategy); + } +}; + + +template +< + typename RingInput, + typename RingOutput +> +struct buffer_inserter +{ + typedef typename point_type::type output_point_type; + + template + < + typename Collection, + typename Iterator, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline bool iterate(Collection& collection, + Iterator begin, Iterator end, + strategy::buffer::buffer_side_selector side, + DistanceStrategy const& distance_strategy, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) + { + output_point_type first_p1, first_p2, last_p1, last_p2; + + typedef detail::buffer::buffer_range buffer_range; + + bool result = buffer_range::iterate(collection, begin, end, + side, + distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy, + first_p1, first_p2, last_p1, last_p2); + + // Generate closing join + if (result) + { + buffer_range::add_join(collection, + *(end - 2), + *(end - 1), last_p1, last_p2, + *(begin + 1), first_p1, first_p2, + side, + distance_strategy, join_strategy, end_strategy, + robust_policy); + } + + // Buffer is closed automatically by last closing corner + return result; + } + + template + < + typename Collection, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy, + typename RobustPolicy + > + static inline void apply(RingInput const& ring, + Collection& collection, + DistanceStrategy const& distance, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + PointStrategy const& point_strategy, + RobustPolicy const& robust_policy) + { + RingInput simplified; + detail::buffer::simplify_input(ring, distance, simplified); + + bool has_output = false; + + std::size_t n = boost::size(simplified); + std::size_t const min_points = core_detail::closure::minimum_ring_size + < + geometry::closure::value + >::value; + + if (n >= min_points) + { + detail::normalized_view view(simplified); + if (distance.negative()) + { + // Walk backwards (rings will be reversed afterwards) + // It might be that this will be changed later. + // TODO: decide this. + has_output = iterate(collection, boost::rbegin(view), boost::rend(view), + strategy::buffer::buffer_side_right, + distance, side_strategy, join_strategy, end_strategy, robust_policy); + } + else + { + has_output = iterate(collection, boost::begin(view), boost::end(view), + strategy::buffer::buffer_side_left, + distance, side_strategy, join_strategy, end_strategy, robust_policy); + } + } + + if (! has_output && n >= 1) + { + // Use point_strategy to buffer degenerated ring + detail::buffer::buffer_point + ( + geometry::range::front(simplified), + collection, distance, point_strategy + ); + } + } +}; + + +template +< + typename Linestring, + typename Polygon +> +struct buffer_inserter +{ + typedef typename ring_type::type output_ring_type; + typedef typename point_type::type output_point_type; + typedef typename point_type::type input_point_type; + + template + < + typename Collection, + typename Iterator, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline bool iterate(Collection& collection, + Iterator begin, Iterator end, + strategy::buffer::buffer_side_selector side, + DistanceStrategy const& distance_strategy, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy, + output_point_type& first_p1) + { + input_point_type const& ultimate_point = *(end - 1); + input_point_type const& penultimate_point = *(end - 2); + + // For the end-cap, we need to have the last perpendicular point on the + // other side of the linestring. If it is the second pass (right), + // we have it already from the first phase (left). + // But for the first pass, we have to generate it + output_point_type reverse_p1; + if (side == strategy::buffer::buffer_side_right) + { + reverse_p1 = first_p1; + } + else + { + std::vector generated_side; + side_strategy.apply(ultimate_point, penultimate_point, + strategy::buffer::buffer_side_right, + distance_strategy, generated_side); + if (generated_side.empty()) + { + return false; + } + reverse_p1 = generated_side.front(); + } + + output_point_type first_p2, last_p1, last_p2; + + detail::buffer::buffer_range::iterate(collection, + begin, end, side, + distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy, + first_p1, first_p2, last_p1, last_p2); + + std::vector range_out; + end_strategy.apply(penultimate_point, last_p2, ultimate_point, reverse_p1, side, distance_strategy, range_out); + collection.add_endcap(end_strategy, range_out, ultimate_point); + return true; + } + + template + < + typename Collection, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy, + typename RobustPolicy + > + static inline void apply(Linestring const& linestring, Collection& collection, + DistanceStrategy const& distance, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + PointStrategy const& point_strategy, + RobustPolicy const& robust_policy) + { + Linestring simplified; + detail::buffer::simplify_input(linestring, distance, simplified); + + bool has_output = false; + std::size_t n = boost::size(simplified); + if (n > 1) + { + collection.start_new_ring(); + output_point_type first_p1; + has_output = iterate(collection, + boost::begin(simplified), boost::end(simplified), + strategy::buffer::buffer_side_left, + distance, side_strategy, join_strategy, end_strategy, robust_policy, + first_p1); + + if (has_output) + { + iterate(collection, boost::rbegin(simplified), boost::rend(simplified), + strategy::buffer::buffer_side_right, + distance, side_strategy, join_strategy, end_strategy, robust_policy, + first_p1); + } + collection.finish_ring(); + } + if (! has_output && n >= 1) + { + // Use point_strategy to buffer degenerated linestring + detail::buffer::buffer_point + ( + geometry::range::front(simplified), + collection, distance, point_strategy + ); + } + } +}; + + +template +< + typename PolygonInput, + typename PolygonOutput +> +struct buffer_inserter +{ +private: + typedef typename ring_type::type input_ring_type; + typedef typename ring_type::type output_ring_type; + + typedef buffer_inserter policy; + + + template + < + typename Iterator, + typename Collection, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy, + typename RobustPolicy + > + static inline + void iterate(Iterator begin, Iterator end, + Collection& collection, + DistanceStrategy const& distance, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + PointStrategy const& point_strategy, + RobustPolicy const& robust_policy, + bool is_interior) + { + for (Iterator it = begin; it != end; ++it) + { + collection.start_new_ring(); + policy::apply(*it, collection, distance, side_strategy, + join_strategy, end_strategy, point_strategy, + robust_policy); + collection.finish_ring(is_interior); + } + } + + template + < + typename InteriorRings, + typename Collection, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy, + typename RobustPolicy + > + static inline + void apply_interior_rings(InteriorRings const& interior_rings, + Collection& collection, + DistanceStrategy const& distance, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + PointStrategy const& point_strategy, + RobustPolicy const& robust_policy) + { + iterate(boost::begin(interior_rings), boost::end(interior_rings), + collection, distance, side_strategy, + join_strategy, end_strategy, point_strategy, + robust_policy, true); + } + +public: + template + < + typename Collection, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy, + typename RobustPolicy + > + static inline void apply(PolygonInput const& polygon, + Collection& collection, + DistanceStrategy const& distance, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + PointStrategy const& point_strategy, + RobustPolicy const& robust_policy) + { + { + collection.start_new_ring(); + policy::apply(exterior_ring(polygon), collection, + distance, side_strategy, + join_strategy, end_strategy, point_strategy, + robust_policy); + collection.finish_ring(); + } + + apply_interior_rings(interior_rings(polygon), + collection, distance, side_strategy, + join_strategy, end_strategy, point_strategy, + robust_policy); + } +}; + + +template +< + typename Multi, + typename PolygonOutput +> +struct buffer_inserter + : public detail::buffer::buffer_multi + < + Multi, + PolygonOutput, + dispatch::buffer_inserter + < + typename single_tag_of + < + typename tag::type + >::type, + typename boost::range_value::type, + typename geometry::ring_type::type + > + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +template +< + typename GeometryOutput, + typename GeometryInput, + typename OutputIterator, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy, + typename RobustPolicy, + typename VisitPiecesPolicy +> +inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator out, + DistanceStrategy const& distance_strategy, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + PointStrategy const& point_strategy, + RobustPolicy const& robust_policy, + VisitPiecesPolicy& visit_pieces_policy + ) +{ + typedef detail::buffer::buffered_piece_collection + < + typename geometry::ring_type::type, + RobustPolicy + > collection_type; + collection_type collection(robust_policy); + collection_type const& const_collection = collection; + + bool const areal = boost::is_same + < + typename tag_cast::type, areal_tag>::type, + areal_tag + >::type::value; + + dispatch::buffer_inserter + < + typename tag_cast + < + typename tag::type, + multi_tag + >::type, + GeometryInput, + GeometryOutput + >::apply(geometry_input, collection, + distance_strategy, side_strategy, join_strategy, + end_strategy, point_strategy, + robust_policy); + + collection.get_turns(); + if (areal) + { + collection.check_remaining_points(distance_strategy.factor()); + } + + // Visit the piece collection. This does nothing (by default), but + // optionally a debugging tool can be attached (e.g. console or svg), + // or the piece collection can be unit-tested + // phase 0: turns (before discarded) + visit_pieces_policy.apply(const_collection, 0); + + collection.discard_rings(); + collection.block_turns(); + collection.enrich(); + collection.traverse(); + + // Reverse all offsetted rings / traversed rings if: + // - they were generated on the negative side (deflate) of polygons + // - the output is counter clockwise + // and avoid reversing twice + bool reverse = distance_strategy.negative() && areal; + if (geometry::point_order::value == counterclockwise) + { + reverse = ! reverse; + } + if (reverse) + { + collection.reverse(); + } + + collection.template assign(out); + + // Visit collection again + // phase 1: rings (after discarding and traversing) + visit_pieces_policy.apply(const_collection, 1); +} + +template +< + typename GeometryOutput, + typename GeometryInput, + typename OutputIterator, + typename DistanceStrategy, + typename SideStrategy, + typename JoinStrategy, + typename EndStrategy, + typename PointStrategy, + typename RobustPolicy +> +inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator out, + DistanceStrategy const& distance_strategy, + SideStrategy const& side_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + PointStrategy const& point_strategy, + RobustPolicy const& robust_policy) +{ + detail::buffer::visit_pieces_default_policy visitor; + buffer_inserter(geometry_input, out, + distance_strategy, side_strategy, join_strategy, + end_strategy, point_strategy, + robust_policy, visitor); +} +#endif // DOXYGEN_NO_DETAIL + +}} // namespace detail::buffer + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_INSERTER_HPP diff --git a/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp b/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp new file mode 100644 index 0000000000..6a2e6b32c5 --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp @@ -0,0 +1,171 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_POLICIES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_POLICIES_HPP + + +#include + +#include + +#include +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + + +enum intersection_location_type +{ + location_ok, inside_buffer, inside_original +}; + +class backtrack_for_buffer +{ +public : + typedef detail::overlay::backtrack_state state_type; + + template + static inline void apply(std::size_t size_at_start, + Rings& rings, typename boost::range_value::type& ring, + Turns& turns, Operation& operation, + std::string const& /*reason*/, + Geometry const& , + Geometry const& , + RobustPolicy const& , + state_type& state + ) + { +#if defined(BOOST_GEOMETRY_COUNT_BACKTRACK_WARNINGS) +extern int g_backtrack_warning_count; +g_backtrack_warning_count++; +#endif +//std::cout << "!"; +//std::cout << "WARNING " << reason << std::endl; + + state.m_good = false; + + // Make bad output clean + rings.resize(size_at_start); + ring.clear(); + + // Reject this as a starting point + operation.visited.set_rejected(); + + // And clear all visit info + clear_visit_info(turns); + } +}; + +// Should follow traversal-turn-concept (enrichment, visit structure) +// and adds index in piece vector to find it back +template +struct buffer_turn_operation + : public detail::overlay::traversal_turn_operation +{ + int piece_index; + int index_in_robust_ring; + + inline buffer_turn_operation() + : piece_index(-1) + , index_in_robust_ring(-1) + {} +}; + +// Version for buffer including type of location, is_opposite, and helper variables +template +struct buffer_turn_info + : public detail::overlay::turn_info + < + Point, + SegmentRatio, + buffer_turn_operation + > +{ + typedef Point point_type; + typedef RobustPoint robust_point_type; + + int turn_index; // TODO: this might go if partition can operate on non-const input + + RobustPoint robust_point; +#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) + // Will (most probably) be removed later + RobustPoint mapped_robust_point; // alas... we still need to adapt our points, offsetting them 1 integer to be co-located with neighbours +#endif + + + inline RobustPoint const& get_robust_point() const + { +#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) + return mapped_robust_point; +#endif + return robust_point; + } + + + intersection_location_type location; + + int count_within; + int count_on_offsetted; + int count_on_helper; + int count_within_near_offsetted; + + bool remove_on_multi; + + // Obsolete: + int count_on_occupied; + int count_on_multi; + + inline buffer_turn_info() + : turn_index(-1) + , location(location_ok) + , count_within(0) + , count_on_offsetted(0) + , count_on_helper(0) + , count_within_near_offsetted(0) + , remove_on_multi(false) + , count_on_occupied(0) + , count_on_multi(0) + {} +}; + +struct buffer_operation_less +{ + template + inline bool operator()(Turn const& left, Turn const& right) const + { + segment_identifier const& sl = left.seg_id; + segment_identifier const& sr = right.seg_id; + + // Sort them descending + return sl == sr + ? left.fraction < right.fraction + : sl < sr; + } +}; + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_POLICIES_HPP diff --git a/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp new file mode 100644 index 0000000000..558a61fcb4 --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -0,0 +1,954 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_PIECE_COLLECTION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_PIECE_COLLECTION_HPP + +#include +#include +#include +#include + + +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +enum segment_relation_code +{ + segment_relation_on_left, + segment_relation_on_right, + segment_relation_within, + segment_relation_disjoint +}; + +/* + * Terminology + * + * Suppose we make a buffer (using blocked corners) of this rectangle: + * + * +-------+ + * | | + * | rect | + * | | + * +-------+ + * + * For the sides we get these four buffered side-pieces (marked with s) + * and four buffered corner pieces (marked with c) + * + * c---+---s---+---c + * | | piece | | <- see below for details of the middle top-side-piece + * +---+-------+---+ + * | | | | + * s | rect | s <- two side pieces left/right of rect + * | | | | + * +---+-------+---+ + * | | piece | | <- one side-piece below, and two corner pieces + * c---+---s---+---c + * + * The outer part of the picture above, using all pieces, + * form together the offsetted ring (marked with o below) + * The 8 pieces are part of the piece collection and use for inside-checks + * The inner parts form (using 1 or 2 points per piece, often co-located) + * form together the robust_ring (marked with r below) + * The remaining piece-segments are helper-segments (marked with h) + * + * ooooooooooooooooo + * o h h o + * ohhhrrrrrrrrrhhho + * o r r o + * o r r o + * o r r o + * ohhhrrrrrrrrrhhho + * o h h o + * ooooooooooooooooo + * + */ + + +template +struct buffered_piece_collection +{ + typedef buffered_piece_collection this_type; + + typedef typename geometry::point_type::type point_type; + typedef typename geometry::coordinate_type::type coordinate_type; + typedef typename geometry::robust_point_type + < + point_type, + RobustPolicy + >::type robust_point_type; + + // Robust ring/polygon type, always clockwise + typedef geometry::model::ring robust_ring_type; + typedef geometry::model::polygon robust_polygon_type; + + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + typedef typename geometry::rescale_policy_type + < + typename geometry::point_type::type + >::type rescale_policy_type; + + typedef typename geometry::segment_ratio_type + < + point_type, + RobustPolicy + >::type segment_ratio_type; + + typedef buffer_turn_info + < + point_type, + robust_point_type, + segment_ratio_type + > buffer_turn_info_type; + + typedef buffer_turn_operation + < + point_type, + segment_ratio_type + > buffer_turn_operation_type; + + typedef std::vector turn_vector_type; + + struct robust_turn + { + int turn_index; + int operation_index; + robust_point_type point; + segment_identifier seg_id; + segment_ratio_type fraction; + }; + + struct piece + { + strategy::buffer::piece_type type; + int index; + + int left_index; // points to previous piece of same ring + int right_index; // points to next piece of same ring + + // The next two members (1, 2) form together a complete clockwise ring + // for each piece (with one dupped point) + // The complete clockwise ring is also included as a robust ring (3) + + // 1: half, part of offsetted_rings + segment_identifier first_seg_id; + int last_segment_index; // no segment-identifier - it is the same as first_seg_id + int offsetted_count; // part in robust_ring which is part of offsetted ring + +#if defined(BOOST_GEOMETRY_BUFFER_USE_HELPER_POINTS) + // 2: half, not part of offsetted rings - part of robust ring + std::vector helper_points; // 4 points for segment, 3 points for join - 0 points for flat-end +#endif + + // Robust representations + // 3: complete ring + robust_ring_type robust_ring; + + geometry::model::box robust_envelope; + + std::vector robust_turns; // Used only in insert_rescaled_piece_turns - we might use a map instead + }; + + typedef std::vector piece_vector_type; + + piece_vector_type m_pieces; + turn_vector_type m_turns; + int m_first_piece_index; + + buffered_ring_collection > offsetted_rings; // indexed by multi_index + buffered_ring_collection robust_polygons; // robust representation of the original(s) + robust_ring_type current_robust_ring; + buffered_ring_collection traversed_rings; + segment_identifier current_segment_id; + + RobustPolicy const& m_robust_policy; + + struct redundant_turn + { + inline bool operator()(buffer_turn_info_type const& turn) const + { + return turn.remove_on_multi; + } + }; + + buffered_piece_collection(RobustPolicy const& robust_policy) + : m_first_piece_index(-1) + , m_robust_policy(robust_policy) + {} + + +#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) + // Will (most probably) be removed later + template + inline void adapt_mapped_robust_point(OccupationMap const& map, + buffer_turn_info_type& turn, int distance) const + { + for (int x = -distance; x <= distance; x++) + { + for (int y = -distance; y <= distance; y++) + { + robust_point_type rp = turn.robust_point; + geometry::set<0>(rp, geometry::get<0>(rp) + x); + geometry::set<1>(rp, geometry::get<1>(rp) + y); + if (map.find(rp) != map.end()) + { + turn.mapped_robust_point = rp; + return; + } + } + } + } +#endif + + inline void get_occupation( +#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) + int distance = 0 +#endif + ) + { + typedef occupation_info > + buffer_occupation_info; + + typedef std::map + < + robust_point_type, + buffer_occupation_info, + geometry::less + > occupation_map_type; + + occupation_map_type occupation_map; + + // 1: Add all intersection points to occupation map + typedef typename boost::range_iterator::type + iterator_type; + + for (iterator_type it = boost::begin(m_turns); + it != boost::end(m_turns); + ++it) + { + if (it->location == location_ok) + { +#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) + if (distance > 0 && ! occupation_map.empty()) + { + adapt_mapped_robust_point(occupation_map, *it, distance); + } +#endif + occupation_map[it->get_robust_point()].count++; + } + } + + // Remove all points with one or more u/u points from the map + // (Alternatively, we could NOT do this here and change all u/u + // behaviour in overlay. Currently nothing is done: each polygon is + // just followed there. We could also always switch polygons there. For + // buffer behaviour, where 3 pieces might meet of which 2 (or more) form + // a u/u turn, this last option would have been better, probably). + for (iterator_type it = boost::begin(m_turns); + it != boost::end(m_turns); + ++it) + { + if (it->both(detail::overlay::operation_union)) + { + typename occupation_map_type::iterator mit = + occupation_map.find(it->get_robust_point()); + + if (mit != occupation_map.end()) + { + occupation_map.erase(mit); + } + } + } + + // 2: Remove all points from map which has only one + typename occupation_map_type::iterator it = occupation_map.begin(); + while (it != occupation_map.end()) + { + if (it->second.count <= 1) + { + typename occupation_map_type::iterator to_erase = it; + ++it; + occupation_map.erase(to_erase); + } + else + { + ++it; + } + } + + if (occupation_map.empty()) + { + return; + } + + // 3: Add vectors (incoming->intersection-point, + // intersection-point -> outgoing) + // for all (co-located) points still present in the map + + for (iterator_type it = boost::begin(m_turns); + it != boost::end(m_turns); + ++it) + { + typename occupation_map_type::iterator mit = + occupation_map.find(it->get_robust_point()); + + if (mit != occupation_map.end()) + { + buffer_occupation_info& info = mit->second; + for (int i = 0; i < 2; i++) + { + add_incoming_and_outgoing_angles(it->get_robust_point(), *it, + m_pieces, + i, it->operations[i].seg_id, + info); + } + + it->count_on_multi++; + } + } + +#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) + // X: Check rounding issues + if (distance == 0) + { + for (typename occupation_map_type::const_iterator it = occupation_map.begin(); + it != occupation_map.end(); ++it) + { + if (it->second.has_rounding_issues(it->first)) + { + if(distance == 0) + { + get_occupation(distance + 1); + return; + } + } + } + } +#endif + + // Get left turns from all clusters + for (typename occupation_map_type::iterator it = occupation_map.begin(); + it != occupation_map.end(); ++it) + { + it->second.get_left_turns(it->first, m_turns); + } + } + + inline void classify_turns() + { + for (typename boost::range_iterator::type it = + boost::begin(m_turns); it != boost::end(m_turns); ++it) + { + if (it->count_within > 0) + { + it->location = inside_buffer; + } + if (it->count_within_near_offsetted > 0) + { + // Within can have in rare cases a rounding issue. We don't discard this + // point, so it can be used to continue started rings in traversal. But + // will never start a new ring from this type of points. + it->selectable_start = false; + } + + } + } + + inline void check_remaining_points(int factor) + { + // TODO: use partition + + for (typename boost::range_iterator::type it = + boost::begin(m_turns); it != boost::end(m_turns); ++it) + { + if (it->location == location_ok) + { + int code = -1; + for (std::size_t i = 0; i < robust_polygons.size(); i++) + { + if (geometry::covered_by(it->robust_point, robust_polygons[i])) + { + code = 1; + break; + } + } + if (code * factor == 1) + { + it->location = inside_original; + } + } + } + } + + inline bool assert_indices_in_robust_rings() const + { + geometry::equal_to comparator; + for (typename boost::range_iterator::type it = + boost::begin(m_turns); it != boost::end(m_turns); ++it) + { + for (int i = 0; i < 2; i++) + { + robust_point_type const &p1 + = m_pieces[it->operations[i].piece_index].robust_ring + [it->operations[i].index_in_robust_ring]; + robust_point_type const &p2 = it->robust_point; + if (! comparator(p1, p2)) + { + return false; + } + } + } + return true; + } + + inline void insert_rescaled_piece_turns() + { + // Add rescaled turn points to corresponding pieces + // (after this, each turn occurs twice) + int index = 0; + for (typename boost::range_iterator::type it = + boost::begin(m_turns); it != boost::end(m_turns); ++it, ++index) + { + geometry::recalculate(it->robust_point, it->point, m_robust_policy); +#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) + it->mapped_robust_point = it->robust_point; +#endif + + robust_turn turn; + it->turn_index = index; + turn.turn_index = index; + turn.point = it->robust_point; + for (int i = 0; i < 2; i++) + { + turn.operation_index = i; + turn.seg_id = it->operations[i].seg_id; + turn.fraction = it->operations[i].fraction; + + piece& pc = m_pieces[it->operations[i].piece_index]; + pc.robust_turns.push_back(turn); + + // Take into account for the box (intersection points should fall inside, + // but in theory they can be one off because of rounding + geometry::expand(pc.robust_envelope, it->robust_point); + } + } + + // Insert all rescaled turn-points into these rings, to form a + // reliable integer-based ring. All turns can be compared (inside) to this + // rings to see if they are inside. + + for (typename piece_vector_type::iterator it = boost::begin(m_pieces); + it != boost::end(m_pieces); + ++it) + { + piece& pc = *it; + int piece_segment_index = pc.first_seg_id.segment_index; + if (! pc.robust_turns.empty()) + { + if (pc.robust_turns.size() > 1u) + { + std::sort(pc.robust_turns.begin(), pc.robust_turns.end(), buffer_operation_less()); + } + // Walk through them, in reverse to insert at right index + int index_offset = pc.robust_turns.size() - 1; + for (typename std::vector::const_reverse_iterator + rit = pc.robust_turns.rbegin(); + rit != pc.robust_turns.rend(); + ++rit, --index_offset) + { + int const index_in_vector = 1 + rit->seg_id.segment_index - piece_segment_index; + BOOST_ASSERT + ( + index_in_vector > 0 && index_in_vector < pc.offsetted_count + ); + + pc.robust_ring.insert(boost::begin(pc.robust_ring) + index_in_vector, rit->point); + pc.offsetted_count++; + + m_turns[rit->turn_index].operations[rit->operation_index].index_in_robust_ring = index_in_vector + index_offset; + } + } + } + + BOOST_ASSERT(assert_indices_in_robust_rings()); + } + + inline void get_turns() + { + { + // Calculate the turns + piece_turn_visitor + < + buffered_ring_collection >, + turn_vector_type, + RobustPolicy + > visitor(offsetted_rings, m_turns, m_robust_policy); + + geometry::partition + < + model::box, piece_get_box, piece_ovelaps_box + >::apply(m_pieces, visitor); + } + + insert_rescaled_piece_turns(); + + { + // Check if it is inside any of the pieces + turn_in_piece_visitor + < + turn_vector_type, piece_vector_type + > visitor(m_turns, m_pieces); + + geometry::partition + < + model::box, + turn_get_box, turn_ovelaps_box, + piece_get_box, piece_ovelaps_box + >::apply(m_turns, m_pieces, visitor); + + } + + classify_turns(); + + //get_occupation(); + } + + inline void start_new_ring() + { + int const n = offsetted_rings.size(); + current_segment_id.source_index = 0; + current_segment_id.multi_index = n; + current_segment_id.ring_index = -1; + current_segment_id.segment_index = 0; + + offsetted_rings.resize(n + 1); + current_robust_ring.clear(); + + m_first_piece_index = boost::size(m_pieces); + } + + inline void finish_ring(bool is_interior = false) + { + if (m_first_piece_index == -1) + { + return; + } + + if (m_first_piece_index < static_cast(boost::size(m_pieces))) + { + // If piece was added + // Reassign left-of-first and right-of-last + geometry::range::at(m_pieces, m_first_piece_index).left_index + = boost::size(m_pieces) - 1; + geometry::range::back(m_pieces).right_index = m_first_piece_index; + } + m_first_piece_index = -1; + + if (!current_robust_ring.empty()) + { + BOOST_ASSERT(geometry::equals(current_robust_ring.front(), current_robust_ring.back())); + + if (is_interior) + { + if (!robust_polygons.empty()) + { + robust_polygons.back().inners().push_back(current_robust_ring); + } + } + else + { + robust_polygons.resize(robust_polygons.size() + 1); + robust_polygons.back().outer() = current_robust_ring; + } + } + } + + inline int add_point(point_type const& p) + { + BOOST_ASSERT + ( + boost::size(offsetted_rings) > 0 + ); + + current_segment_id.segment_index++; + offsetted_rings.back().push_back(p); + return offsetted_rings.back().size(); + } + + //------------------------------------------------------------------------- + + inline piece& create_piece(strategy::buffer::piece_type type, bool decrease_segment_index_by_one) + { + piece pc; + pc.type = type; + pc.index = boost::size(m_pieces); + pc.first_seg_id = current_segment_id; + + // Assign left/right (for first/last piece per ring they will be re-assigned later) + pc.left_index = pc.index - 1; + pc.right_index = pc.index + 1; + + std::size_t const n = boost::size(offsetted_rings.back()); + pc.first_seg_id.segment_index = decrease_segment_index_by_one ? n - 1 : n; + + m_pieces.push_back(pc); + return m_pieces.back(); + } + + inline void init_rescale_piece(piece& pc, std::size_t helper_points_size) + { + pc.offsetted_count = pc.last_segment_index - pc.first_seg_id.segment_index; + BOOST_ASSERT(pc.offsetted_count >= 0); + + pc.robust_ring.reserve(pc.offsetted_count + helper_points_size); + + // Add rescaled offsetted segments + { + buffered_ring const& ring = offsetted_rings[pc.first_seg_id.multi_index]; + + typedef typename boost::range_iterator >::type it_type; + for (it_type it = boost::begin(ring) + pc.first_seg_id.segment_index; + it != boost::begin(ring) + pc.last_segment_index; + ++it) + { + robust_point_type point; + geometry::recalculate(point, *it, m_robust_policy); + pc.robust_ring.push_back(point); + } + } + } + + inline robust_point_type add_helper_point(piece& pc, const point_type& point) + { +#if defined(BOOST_GEOMETRY_BUFFER_USE_HELPER_POINTS) + pc.helper_points.push_back(point); +#endif + + robust_point_type rob_point; + geometry::recalculate(rob_point, point, m_robust_policy); + pc.robust_ring.push_back(rob_point); + return rob_point; + } + + inline void calculate_robust_envelope(piece& pc) + { + geometry::detail::envelope::envelope_range::apply(pc.robust_ring, + pc.robust_envelope); + } + + inline void finish_piece(piece& pc) + { + init_rescale_piece(pc, 0u); + calculate_robust_envelope(pc); + } + + inline void finish_piece(piece& pc, + const point_type& point1, + const point_type& point2, + const point_type& point3) + { + init_rescale_piece(pc, 3u); + add_helper_point(pc, point1); + robust_point_type mid_point = add_helper_point(pc, point2); + add_helper_point(pc, point3); + calculate_robust_envelope(pc); + + current_robust_ring.push_back(mid_point); + } + + inline void finish_piece(piece& pc, + const point_type& point1, + const point_type& point2, + const point_type& point3, + const point_type& point4) + { + init_rescale_piece(pc, 4u); + add_helper_point(pc, point1); + robust_point_type mid_point2 = add_helper_point(pc, point2); + robust_point_type mid_point1 = add_helper_point(pc, point3); + add_helper_point(pc, point4); + calculate_robust_envelope(pc); + + // Add mid-points in other order to current helper_ring + current_robust_ring.push_back(mid_point1); + current_robust_ring.push_back(mid_point2); + } + + inline void add_piece(strategy::buffer::piece_type type, point_type const& p, + point_type const& b1, point_type const& b2) + { + piece& pc = create_piece(type, false); + add_point(b1); + pc.last_segment_index = add_point(b2); + finish_piece(pc, b2, p, b1); + } + + template + inline void add_range_to_piece(piece& pc, Range const& range, bool add_front) + { + if (boost::size(range) == 0u) + { + return; + } + + typename Range::const_iterator it = boost::begin(range); + + // If it follows a non-join (so basically the same piece-type) point b1 should be added. + // There should be two intersections later and it should be discarded. + // But for now we need it to calculate intersections + if (add_front) + { + add_point(*it); + } + + for (++it; it != boost::end(range); ++it) + { + pc.last_segment_index = add_point(*it); + } + } + + + template + inline void add_piece(strategy::buffer::piece_type type, Range const& range, bool decrease_segment_index_by_one) + { + piece& pc = create_piece(type, decrease_segment_index_by_one); + add_range_to_piece(pc, range, offsetted_rings.back().empty()); + finish_piece(pc); + } + + template + inline void add_side_piece(point_type const& p1, point_type const& p2, + Range const& range, bool first) + { + BOOST_ASSERT(boost::size(range) >= 2u); + + piece& pc = create_piece(strategy::buffer::buffered_segment, ! first); + add_range_to_piece(pc, range, first); + finish_piece(pc, range.back(), p2, p1, range.front()); + } + + template + inline void add_piece(strategy::buffer::piece_type type, point_type const& p, Range const& range) + { + piece& pc = create_piece(type, true); + + add_range_to_piece(pc, range, offsetted_rings.back().empty()); + if (boost::size(range) > 0) + { + finish_piece(pc, range.back(), p, range.front()); + } + else + { + finish_piece(pc); + } + } + + template + inline void add_endcap(EndcapStrategy const& strategy, Range const& range, point_type const& end_point) + { + if (range.empty()) + { + return; + } + strategy::buffer::piece_type pt = strategy.get_piece_type(); + if (pt == strategy::buffer::buffered_flat_end) + { + // It is flat, should just be added, without helper segments + add_piece(pt, range, true); + } + else + { + // Normal case, it has an "inside", helper segments should be added + add_piece(pt, end_point, range); + } + } + + //------------------------------------------------------------------------- + + inline void enrich() + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy_type; + + enrich_intersection_points(m_turns, + detail::overlay::operation_union, + offsetted_rings, offsetted_rings, + m_robust_policy, side_strategy_type()); + } + + // Discards all rings which do have not-OK intersection points only. + // Those can never be traversed and should not be part of the output. + inline void discard_rings() + { + for (typename boost::range_iterator::type it = + boost::begin(m_turns); it != boost::end(m_turns); ++it) + { + if (it->location != location_ok) + { + offsetted_rings[it->operations[0].seg_id.multi_index].has_discarded_intersections = true; + offsetted_rings[it->operations[1].seg_id.multi_index].has_discarded_intersections = true; + } + else if (! it->both(detail::overlay::operation_union)) + { + offsetted_rings[it->operations[0].seg_id.multi_index].has_accepted_intersections = true; + offsetted_rings[it->operations[1].seg_id.multi_index].has_accepted_intersections = true; + } + } + } + + inline void block_turns() + { + // To fix left-turn issues like #rt_u13 + // But currently it causes more other issues than it fixes +// m_turns.erase +// ( +// std::remove_if(boost::begin(m_turns), boost::end(m_turns), +// redundant_turn()), +// boost::end(m_turns) +// ); + + for (typename boost::range_iterator::type it = + boost::begin(m_turns); it != boost::end(m_turns); ++it) + { + if (it->location != location_ok) + { + // Set it to blocked. They should not be discarded, to avoid + // generating rings over these turns + // Performance goes down a tiny bit from 161 s to 173 because there + // are sometimes much more turns. + // We might speed it up a bit by keeping only one blocked + // intersection per segment, but that is complex to program + // because each turn involves two segments + it->operations[0].operation = detail::overlay::operation_blocked; + it->operations[1].operation = detail::overlay::operation_blocked; + } + } + } + + inline void traverse() + { + typedef detail::overlay::traverse + < + false, false, + buffered_ring_collection >, buffered_ring_collection >, + backtrack_for_buffer + > traverser; + + traversed_rings.clear(); + traverser::apply(offsetted_rings, offsetted_rings, + detail::overlay::operation_union, + m_robust_policy, m_turns, traversed_rings); + } + + inline void reverse() + { + for(typename buffered_ring_collection >::iterator it = boost::begin(offsetted_rings); + it != boost::end(offsetted_rings); + ++it) + { + if (! it->has_intersections()) + { + std::reverse(it->begin(), it->end()); + } + } + for (typename boost::range_iterator >::type + it = boost::begin(traversed_rings); + it != boost::end(traversed_rings); + ++it) + { + std::reverse(it->begin(), it->end()); + } + + } + + template + inline OutputIterator assign(OutputIterator out) const + { + typedef detail::overlay::ring_properties properties; + + std::map selected; + + // Select all rings which do not have any self-intersection (other ones should be traversed) + int index = 0; + for(typename buffered_ring_collection >::const_iterator it = boost::begin(offsetted_rings); + it != boost::end(offsetted_rings); + ++it, ++index) + { + if (! it->has_intersections()) + { + ring_identifier id(0, index, -1); + selected[id] = properties(*it, true); + } + } + + // Select all created rings + index = 0; + for (typename boost::range_iterator const>::type + it = boost::begin(traversed_rings); + it != boost::end(traversed_rings); + ++it, ++index) + { + ring_identifier id(2, index, -1); + selected[id] = properties(*it, true); + } + + detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected, true); + return detail::overlay::add_rings(selected, offsetted_rings, traversed_rings, out); + } + +}; + + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_PIECE_COLLECTION_HPP diff --git a/boost/geometry/algorithms/detail/buffer/buffered_ring.hpp b/boost/geometry/algorithms/detail/buffer/buffered_ring.hpp new file mode 100644 index 0000000000..03ec598c90 --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/buffered_ring.hpp @@ -0,0 +1,238 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_RING +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_RING + + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +struct buffered_ring_collection_tag : polygonal_tag, multi_tag +{}; + + +template +struct buffered_ring : public Ring +{ + bool has_accepted_intersections; + bool has_discarded_intersections; + + inline buffered_ring() + : has_accepted_intersections(false) + , has_discarded_intersections(false) + {} + + inline bool discarded() const + { + return has_discarded_intersections && ! has_accepted_intersections; + } + inline bool has_intersections() const + { + return has_discarded_intersections || has_accepted_intersections; + } +}; + +// This is a collection now special for overlay (needs vector of rings) +template +struct buffered_ring_collection : public std::vector +{ +}; + +}} // namespace detail::buffer + + +// Turn off concept checking (for now) +namespace dispatch +{ +template +struct check +{ +}; + +} + + +#endif // DOXYGEN_NO_DETAIL + + + +// Register the types +namespace traits +{ + + +template +struct tag > +{ + typedef ring_tag type; +}; + + +template +struct point_order > +{ + static const order_selector value = geometry::point_order::value; +}; + + +template +struct closure > +{ + static const closure_selector value = geometry::closure::value; +}; + + +template +struct point_type > +{ + typedef typename geometry::point_type::type type; +}; + +template +struct tag > +{ + typedef detail::buffer::buffered_ring_collection_tag type; +}; + + +} // namespace traits + + + + +namespace core_dispatch +{ + +template +struct ring_type +< + detail::buffer::buffered_ring_collection_tag, + detail::buffer::buffered_ring_collection +> +{ + typedef Ring type; +}; + +} + +namespace dispatch +{ + +template +< + typename MultiRing, + bool Reverse, + typename SegmentIdentifier, + typename PointOut +> +struct copy_segment_point + < + detail::buffer::buffered_ring_collection_tag, + MultiRing, + Reverse, + SegmentIdentifier, + PointOut + > + : detail::copy_segments::copy_segment_point_multi + < + MultiRing, + SegmentIdentifier, + PointOut, + detail::copy_segments::copy_segment_point_range + < + typename boost::range_value::type, + Reverse, + SegmentIdentifier, + PointOut + > + > +{}; + + +template +struct copy_segments + < + detail::buffer::buffered_ring_collection_tag, + Reverse + > + : detail::copy_segments::copy_segments_multi + < + detail::copy_segments::copy_segments_ring + > +{}; + +template +struct within +< + Point, + MultiGeometry, + point_tag, + detail::buffer::buffered_ring_collection_tag +> +{ + template + static inline bool apply(Point const& point, + MultiGeometry const& multi, Strategy const& strategy) + { + return detail::within::point_in_geometry(point, multi, strategy) == 1; + } +}; + + +} // namespace dispatch + +namespace detail { namespace overlay +{ + +template<> +struct get_ring +{ + template + static inline typename ring_type::type const& apply( + ring_identifier const& id, + MultiGeometry const& multi_ring) + { + BOOST_ASSERT + ( + id.multi_index >= 0 + && id.multi_index < int(boost::size(multi_ring)) + ); + return get_ring::apply(id, multi_ring[id.multi_index]); + } +}; + +}} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_RING diff --git a/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp b/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp new file mode 100644 index 0000000000..395921ccaa --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp @@ -0,0 +1,191 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_GET_PIECE_TURNS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_GET_PIECE_TURNS_HPP + +#include + +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + + +struct piece_get_box +{ + template + static inline void apply(Box& total, Piece const& piece) + { + geometry::expand(total, piece.robust_envelope); + } +}; + +struct piece_ovelaps_box +{ + template + static inline bool apply(Box const& box, Piece const& piece) + { + return ! geometry::detail::disjoint::disjoint_box_box(box, piece.robust_envelope); + } +}; + +template +< + typename Rings, + typename Turns, + typename RobustPolicy +> +class piece_turn_visitor +{ + Rings const& m_rings; + Turns& m_turns; + RobustPolicy const& m_robust_policy; + + template + inline bool is_adjacent(Piece const& piece1, Piece const& piece2) const + { + if (piece1.first_seg_id.multi_index != piece2.first_seg_id.multi_index) + { + return false; + } + + return piece1.index == piece2.left_index + || piece1.index == piece2.right_index; + } + + template + inline void move_to_next_point(Range const& range, Iterator& next) const + { + ++next; + if (next == boost::end(range)) + { + next = boost::begin(range) + 1; + } + } + + template + inline Iterator next_point(Range const& range, Iterator it) const + { + Iterator result = it; + move_to_next_point(range, result); + // TODO: we could use either piece-boundaries, or comparison with + // robust points, to check if the point equals the last one + while(geometry::equals(*it, *result)) + { + move_to_next_point(range, result); + } + return result; + } + + template + inline void calculate_turns(Piece const& piece1, Piece const& piece2) + { + typedef typename boost::range_value::type ring_type; + typedef typename boost::range_value::type turn_type; + typedef typename boost::range_iterator::type iterator; + + segment_identifier seg_id1 = piece1.first_seg_id; + segment_identifier seg_id2 = piece2.first_seg_id; + + if (seg_id1.segment_index < 0 || seg_id2.segment_index < 0) + { + return; + } + + ring_type const& ring1 = m_rings[seg_id1.multi_index]; + iterator it1_first = boost::begin(ring1) + seg_id1.segment_index; + iterator it1_last = boost::begin(ring1) + piece1.last_segment_index; + + ring_type const& ring2 = m_rings[seg_id2.multi_index]; + iterator it2_first = boost::begin(ring2) + seg_id2.segment_index; + iterator it2_last = boost::begin(ring2) + piece2.last_segment_index; + + turn_type the_model; + the_model.operations[0].piece_index = piece1.index; + the_model.operations[0].seg_id = piece1.first_seg_id; + + iterator it1 = it1_first; + for (iterator prev1 = it1++; + it1 != it1_last; + prev1 = it1++, the_model.operations[0].seg_id.segment_index++) + { + the_model.operations[1].piece_index = piece2.index; + the_model.operations[1].seg_id = piece2.first_seg_id; + + iterator next1 = next_point(ring1, it1); + + iterator it2 = it2_first; + for (iterator prev2 = it2++; + it2 != it2_last; + prev2 = it2++, the_model.operations[1].seg_id.segment_index++) + { + iterator next2 = next_point(ring2, it2); + + // TODO: internally get_turn_info calculates robust points. + // But they are already calculated. + // We should be able to use them. + // this means passing them to this visitor, + // and iterating in sync with them... + typedef detail::overlay::get_turn_info + < + detail::overlay::assign_null_policy + > turn_policy; + + turn_policy::apply(*prev1, *it1, *next1, + *prev2, *it2, *next2, + false, false, false, false, + the_model, m_robust_policy, + std::back_inserter(m_turns)); + } + } + } + +public: + + piece_turn_visitor(Rings const& ring_collection, + Turns& turns, + RobustPolicy const& robust_policy) + : m_rings(ring_collection) + , m_turns(turns) + , m_robust_policy(robust_policy) + {} + + template + inline void apply(Piece const& piece1, Piece const& piece2, + bool first = true) + { + boost::ignore_unused_variable_warning(first); + if ( is_adjacent(piece1, piece2) + || detail::disjoint::disjoint_box_box(piece1.robust_envelope, + piece2.robust_envelope)) + { + return; + } + calculate_turns(piece1, piece2); + } +}; + + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_GET_PIECE_TURNS_HPP diff --git a/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp b/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp new file mode 100644 index 0000000000..618afe5fba --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp @@ -0,0 +1,88 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP + + +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + + +// TODO: once change this to proper strategy +// It is different from current segment intersection because these are not segments but lines +// If we have the Line concept, we can create a strategy +// Assumes a convex corner +struct line_line_intersection +{ + + template + static inline strategy::buffer::join_selector apply(Point const& pi, Point const& pj, + Point const& qi, Point const& qj, Point& ip) + { + // See http://mathworld.wolfram.com/Line-LineIntersection.html + typedef typename coordinate_type::type coordinate_type; + + coordinate_type const denominator + = determinant(get<0>(pi) - get<0>(pj), + get<1>(pi) - get<1>(pj), + get<0>(qi) - get<0>(qj), + get<1>(qi) - get<1>(qj)); + + // Even if the corner was checked before (so it is convex now), that + // was done on the original geometry. This function runs on the buffered + // geometries, where sides are generated and might be slightly off. In + // Floating Point, that slightly might just exceed the limit and we have + // to check it again. + + // For round joins, it will not be used at all. + // For miter joints, there is a miter limit + // If segments are parallel/collinear we must be distinguish two cases: + // they continue each other, or they form a spike + if (math::equals(denominator, coordinate_type())) + { + return parallel_continue(get<0>(qj) - get<0>(qi), + get<1>(qj) - get<1>(qi), + get<0>(pj) - get<0>(pi), + get<1>(pj) - get<1>(pi)) + ? strategy::buffer::join_continue + : strategy::buffer::join_spike + ; + } + + coordinate_type d1 = determinant(get<0>(pi), get<1>(pi), get<0>(pj), get<1>(pj)); + coordinate_type d2 = determinant(get<0>(qi), get<1>(qi), get<0>(qj), get<1>(qj)); + + double const multiplier = 1.0 / denominator; + + set<0>(ip, determinant(d1, get<0>(pi) - get<0>(pj), d2, get<0>(qi) - get<0>(qj)) * multiplier); + set<1>(ip, determinant(d1, get<1>(pi) - get<1>(pj), d2, get<1>(qi) - get<1>(qj)) * multiplier); + + return strategy::buffer::join_convex; + } +}; + + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP diff --git a/boost/geometry/algorithms/detail/buffer/parallel_continue.hpp b/boost/geometry/algorithms/detail/buffer/parallel_continue.hpp new file mode 100644 index 0000000000..119d64de74 --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/parallel_continue.hpp @@ -0,0 +1,33 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +template +inline bool parallel_continue(T dx1, T dy1, T dx2, T dy2) +{ + T const dot = dx1 * dx2 + dy1 * dy2; + return dot > 0; +} + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP diff --git a/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp b/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp new file mode 100644 index 0000000000..2b1c33d291 --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp @@ -0,0 +1,98 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_INPUT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_INPUT_HPP + +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +// Checks if an turn/intersection point is inside (or covered by) the input geometry + +template +struct turn_in_input +{ +}; + +template <> +struct turn_in_input +{ + template + static inline int apply(Point const& point, Geometry const& geometry) + { + return geometry::covered_by(point, geometry) ? 1 : -1; + } +}; + +template <> +struct turn_in_input +{ + template + static inline int apply(Point const& , Geometry const& ) + { + return 0; + } +}; + +template <> +struct turn_in_input +{ + template + static inline int apply(Point const& , Geometry const& ) + { + return 0; + } +}; + +template <> +struct turn_in_input +{ + template + static inline int apply(Point const& point, Geometry const& geometry) + { + return geometry::covered_by(point, geometry) ? 1 : -1; + } +}; + +template <> +struct turn_in_input +{ + template + static inline int apply(Point const& , Geometry const& ) + { + return 0; + } +}; + +template <> +struct turn_in_input +{ + template + static inline int apply(Point const& , Geometry const& ) + { + return 0; + } +}; + + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_INPUT_HPP diff --git a/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp b/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp new file mode 100644 index 0000000000..c02f56de3f --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp @@ -0,0 +1,246 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + + +struct turn_get_box +{ + template + static inline void apply(Box& total, Turn const& turn) + { + geometry::expand(total, turn.robust_point); + } +}; + +struct turn_ovelaps_box +{ + template + static inline bool apply(Box const& box, Turn const& turn) + { + return ! dispatch::disjoint + < + typename Turn::robust_point_type, + Box + >::apply(turn.robust_point, box); + } +}; + +template +class turn_in_piece_visitor +{ + Turns& m_turns; // because partition is currently operating on const input only + Pieces const& m_pieces; // to check for piece-type + + typedef boost::long_long_type calculation_type; + + template + static inline bool projection_on_segment(Point const& subject, Point const& p, Point const& q) + { + typedef Point vector_type; + typedef typename geometry::coordinate_type::type coordinate_type; + + vector_type v = q; + vector_type w = subject; + subtract_point(v, p); + subtract_point(w, p); + + coordinate_type const zero = coordinate_type(); + coordinate_type const c1 = dot_product(w, v); + + if (c1 < zero) + { + return false; + } + coordinate_type const c2 = dot_product(v, v); + if (c2 < c1) + { + return false; + } + + return true; + } + + template + inline bool on_offsetted(Point const& point, Piece const& piece) const + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + geometry::equal_to comparator; + + for (int i = 1; i < piece.offsetted_count; i++) + { + Point const& previous = piece.robust_ring[i - 1]; + Point const& current = piece.robust_ring[i]; + + // The robust ring contains duplicates, avoid applying side on them (will be 0) + if (! comparator(previous, current)) + { + int const side = side_strategy::apply(previous, current, point); + if (side == 0) + { + // Collinear, check if projection falls on it + if (projection_on_segment(point, previous, current)) + { + return true; + } + } + } + } + return false; + } + + template + static inline + calculation_type comparable_distance_from_offsetted(Point const& point, + Piece const& piece) + { + // TODO: pass subrange to dispatch to avoid making copy + geometry::model::linestring ls; + std::copy(piece.robust_ring.begin(), + piece.robust_ring.begin() + piece.offsetted_count, + std::back_inserter(ls)); + typename default_comparable_distance_result::type + const comp = geometry::comparable_distance(point, ls); + + return static_cast(comp); + } + +public: + + inline turn_in_piece_visitor(Turns& turns, Pieces const& pieces) + : m_turns(turns) + , m_pieces(pieces) + {} + + template + inline void apply(Turn const& turn, Piece const& piece, bool first = true) + { + boost::ignore_unused_variable_warning(first); + + if (turn.count_within > 0) + { + // Already inside - no need to check again + return; + } + + if (piece.type == strategy::buffer::buffered_flat_end + || piece.type == strategy::buffer::buffered_concave) + { + // Turns cannot be inside a flat end (though they can be on border) + // Neither we need to check if they are inside concave helper pieces + return; + } + + bool neighbour = false; + for (int i = 0; i < 2; i++) + { + // Don't compare against one of the two source-pieces + if (turn.operations[i].piece_index == piece.index) + { + return; + } + + typename boost::range_value::type const& pc + = m_pieces[turn.operations[i].piece_index]; + if (pc.left_index == piece.index + || pc.right_index == piece.index) + { + if (pc.type == strategy::buffer::buffered_flat_end) + { + // If it is a flat end, don't compare against its neighbor: + // it will always be located on one of the helper segments + return; + } + neighbour = true; + } + } + + int geometry_code = detail::within::point_in_geometry(turn.robust_point, piece.robust_ring); + + if (geometry_code == -1) + { + return; + } + if (geometry_code == 0 && neighbour) + { + return; + } + + Turn& mutable_turn = m_turns[turn.turn_index]; + if (geometry_code == 0) + { + // If it is on the border and they are not neighbours, it should be + // on the offsetted ring + + if (! on_offsetted(turn.robust_point, piece)) + { + // It is on the border but not on the offsetted ring. + // Then it is somewhere on the helper-segments + // Classify it as "within" + geometry_code = 1; + mutable_turn.count_on_helper++; // can still become "near_offsetted" + } + else + { + mutable_turn.count_on_offsetted++; // value is not used anymore + } + } + + if (geometry_code == 1) + { + calculation_type const distance + = comparable_distance_from_offsetted(turn.robust_point, piece); + if (distance >= 4) + { + // This is too far from the border, it counts as "really within" + mutable_turn.count_within++; + } + else + { + // Other points count as still "on border" because they might be + // travelled through, but not used as starting point + mutable_turn.count_within_near_offsetted++; + } + } + } +}; + + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR diff --git a/boost/geometry/algorithms/detail/calculate_null.hpp b/boost/geometry/algorithms/detail/calculate_null.hpp index 4b48d62fc2..3ebca83506 100644 --- a/boost/geometry/algorithms/detail/calculate_null.hpp +++ b/boost/geometry/algorithms/detail/calculate_null.hpp @@ -21,9 +21,9 @@ namespace boost { namespace geometry namespace detail { -template struct calculate_null { + template static inline ReturnType apply(Geometry const& , Strategy const&) { return ReturnType(); diff --git a/boost/geometry/algorithms/detail/calculate_sum.hpp b/boost/geometry/algorithms/detail/calculate_sum.hpp index 2ad349080b..b23e70171b 100644 --- a/boost/geometry/algorithms/detail/calculate_sum.hpp +++ b/boost/geometry/algorithms/detail/calculate_sum.hpp @@ -3,6 +3,7 @@ // 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. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -14,9 +15,7 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_SUM_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_SUM_HPP - #include -#include namespace boost { namespace geometry { @@ -26,20 +25,14 @@ namespace detail { -template -< - typename ReturnType, - typename Polygon, - typename Strategy, - typename Policy -> class calculate_polygon_sum { - template + template static inline ReturnType sum_interior_rings(Rings const& rings, Strategy const& strategy) { ReturnType sum = ReturnType(); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + for (typename boost::range_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) { sum += Policy::apply(*it, strategy); } @@ -47,10 +40,11 @@ class calculate_polygon_sum } public : + template static inline ReturnType apply(Polygon const& poly, Strategy const& strategy) { return Policy::apply(exterior_ring(poly), strategy) - + sum_interior_rings(interior_rings(poly), strategy) + + sum_interior_rings(interior_rings(poly), strategy) ; } }; diff --git a/boost/geometry/algorithms/detail/centroid/translating_transformer.hpp b/boost/geometry/algorithms/detail/centroid/translating_transformer.hpp new file mode 100644 index 0000000000..56a7e3ec91 --- /dev/null +++ b/boost/geometry/algorithms/detail/centroid/translating_transformer.hpp @@ -0,0 +1,119 @@ +// 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. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CENTROID_TRANSLATING_TRANSFORMER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CENTROID_TRANSLATING_TRANSFORMER_HPP + + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace centroid +{ + + +// NOTE: There is no need to translate in other coordinate systems than +// cartesian. But if it was needed then one should translate using +// CS-specific technique, e.g. in spherical/geographic a translation +// vector should contain coordinates being multiplies of 2PI or 360 deg. +template ::type, + areal_tag + >::type, + typename CSTag = typename cs_tag::type> +struct translating_transformer +{ + typedef typename geometry::point_type::type point_type; + typedef boost::reference_wrapper result_type; + + explicit translating_transformer(Geometry const&) {} + explicit translating_transformer(point_type const&) {} + + result_type apply(point_type const& pt) const + { + return result_type(pt); + } + + template + void apply_reverse(ResPt &) const {} +}; + +// Specialization for Areal Geometries in cartesian CS +template +struct translating_transformer +{ + typedef typename geometry::point_type::type point_type; + typedef point_type result_type; + + explicit translating_transformer(Geometry const& geom) + : m_origin(NULL) + { + geometry::point_iterator + pt_it = geometry::points_begin(geom); + if ( pt_it != geometry::points_end(geom) ) + { + m_origin = boost::addressof(*pt_it); + } + } + + explicit translating_transformer(point_type const& origin) + : m_origin(boost::addressof(origin)) + {} + + result_type apply(point_type const& pt) const + { + point_type res = pt; + if ( m_origin ) + geometry::subtract_point(res, *m_origin); + return res; + } + + template + void apply_reverse(ResPt & res_pt) const + { + if ( m_origin ) + geometry::add_point(res_pt, *m_origin); + } + + const point_type * m_origin; +}; + + +}} // namespace detail::centroid +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CENTROID_TRANSLATING_TRANSFORMER_HPP diff --git a/boost/geometry/algorithms/detail/check_iterator_range.hpp b/boost/geometry/algorithms/detail/check_iterator_range.hpp new file mode 100644 index 0000000000..09ea7f79a0 --- /dev/null +++ b/boost/geometry/algorithms/detail/check_iterator_range.hpp @@ -0,0 +1,71 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CHECK_ITERATOR_RANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CHECK_ITERATOR_RANGE_HPP + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +// Check whether (each element of) an iterator range satisfies a given +// predicate. +// The predicate must be implemented as having a static apply unary +// method that returns a bool. +// By default an empty range is accepted +template +struct check_iterator_range +{ + template + static inline bool apply(InputIterator first, InputIterator beyond) + { + for (InputIterator it = first; it != beyond; ++it) + { + if ( !Predicate::apply(*it) ) + { + return false; + } + } + return AllowEmptyRange || first != beyond; + } + + + // version where we can pass a predicate object + template + static inline bool apply(InputIterator first, + InputIterator beyond, + Predicate const& predicate) + { + // in case predicate's apply method is static, MSVC will + // complain that predicate is not used + boost::ignore_unused(predicate); + + for (InputIterator it = first; it != beyond; ++it) + { + if ( !predicate.apply(*it) ) + { + return false; + } + } + return AllowEmptyRange || first != beyond; + } +}; + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CHECK_ITERATOR_RANGE_HPP diff --git a/boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp b/boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp new file mode 100644 index 0000000000..c7558b4ff1 --- /dev/null +++ b/boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp @@ -0,0 +1,147 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_GEOMETRY_TO_RANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_GEOMETRY_TO_RANGE_HPP + +#include + +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace closest_feature +{ + + + +// returns the range iterator the realizes the closest +// distance between the geometry and the element of the range +class geometry_to_range +{ +private: + template + < + typename Geometry, + typename RangeIterator, + typename Strategy, + typename Distance + > + static inline void apply(Geometry const& geometry, + RangeIterator first, + RangeIterator last, + Strategy const& strategy, + RangeIterator& it_min, + Distance& dist_min) + { + BOOST_ASSERT( first != last ); + + Distance const zero = Distance(0); + + // start with first distance + it_min = first; + dist_min = dispatch::distance + < + Geometry, + typename std::iterator_traits::value_type, + Strategy + >::apply(geometry, *it_min, strategy); + + // check if other elements in the range are closer + RangeIterator it = first; + for (++it; it != last; ++it) + { + Distance dist = dispatch::distance + < + Geometry, + typename std::iterator_traits::value_type, + Strategy + >::apply(geometry, *it, strategy); + + if (geometry::math::equals(dist, zero)) + { + dist_min = dist; + it_min = it; + return; + } + else if (dist < dist_min) + { + dist_min = dist; + it_min = it; + } + } + } + +public: + template + < + typename Geometry, + typename RangeIterator, + typename Strategy, + typename Distance + > + static inline RangeIterator apply(Geometry const& geometry, + RangeIterator first, + RangeIterator last, + Strategy const& strategy, + Distance& dist_min) + { + RangeIterator it_min; + apply(geometry, first, last, strategy, it_min, dist_min); + + return it_min; + } + + + template + < + typename Geometry, + typename RangeIterator, + typename Strategy + > + static inline RangeIterator apply(Geometry const& geometry, + RangeIterator first, + RangeIterator last, + Strategy const& strategy) + { + typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type + < + typename std::iterator_traits + < + RangeIterator + >::value_type + >::type + >::type dist_min; + + return apply(geometry, first, last, strategy, dist_min); + } +}; + + + +}} // namespace detail::closest_feature +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_GEOMETRY_TO_RANGE_HPP diff --git a/boost/geometry/algorithms/detail/closest_feature/point_to_range.hpp b/boost/geometry/algorithms/detail/closest_feature/point_to_range.hpp new file mode 100644 index 0000000000..91be6b0ad0 --- /dev/null +++ b/boost/geometry/algorithms/detail/closest_feature/point_to_range.hpp @@ -0,0 +1,250 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_POINT_TO_RANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_POINT_TO_RANGE_HPP + +#include + +#include +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace closest_feature +{ + + +// returns the segment (pair of iterators) that realizes the closest +// distance of the point to the range +template +< + typename Point, + typename Range, + closure_selector Closure, + typename Strategy +> +class point_to_point_range +{ +protected: + typedef typename boost::range_iterator::type iterator_type; + + template + static inline void apply(Point const& point, + iterator_type first, + iterator_type last, + Strategy const& strategy, + iterator_type& it_min1, + iterator_type& it_min2, + Distance& dist_min) + { + BOOST_ASSERT( first != last ); + + Distance const zero = Distance(0); + + iterator_type it = first; + iterator_type prev = it++; + if (it == last) + { + it_min1 = it_min2 = first; + dist_min = strategy.apply(point, *first, *first); + return; + } + + // start with first segment distance + dist_min = strategy.apply(point, *prev, *it); + iterator_type prev_min_dist = prev; + + // check if other segments are closer + for (++prev, ++it; it != last; ++prev, ++it) + { + Distance dist = strategy.apply(point, *prev, *it); + if (geometry::math::equals(dist, zero)) + { + dist_min = zero; + it_min1 = prev; + it_min2 = it; + return; + } + else if (dist < dist_min) + { + dist_min = dist; + prev_min_dist = prev; + } + } + + it_min1 = it_min2 = prev_min_dist; + ++it_min2; + } + +public: + typedef typename std::pair return_type; + + template + static inline return_type apply(Point const& point, + iterator_type first, + iterator_type last, + Strategy const& strategy, + Distance& dist_min) + { + iterator_type it_min1, it_min2; + apply(point, first, last, strategy, it_min1, it_min2, dist_min); + + return std::make_pair(it_min1, it_min2); + } + + static inline return_type apply(Point const& point, + iterator_type first, + iterator_type last, + Strategy const& strategy) + { + typename strategy::distance::services::return_type + < + Strategy, + Point, + typename boost::range_value::type + >::type dist_min; + + return apply(point, first, last, strategy, dist_min); + } + + template + static inline return_type apply(Point const& point, + Range const& range, + Strategy const& strategy, + Distance& dist_min) + { + return apply(point, + boost::begin(range), + boost::end(range), + strategy, + dist_min); + } + + static inline return_type apply(Point const& point, + Range const& range, + Strategy const& strategy) + { + return apply(point, boost::begin(range), boost::end(range), strategy); + } +}; + + + +// specialization for open ranges +template +class point_to_point_range + : point_to_point_range +{ +private: + typedef point_to_point_range base_type; + typedef typename base_type::iterator_type iterator_type; + + template + static inline void apply(Point const& point, + iterator_type first, + iterator_type last, + Strategy const& strategy, + iterator_type& it_min1, + iterator_type& it_min2, + Distance& dist_min) + { + BOOST_ASSERT( first != last ); + + base_type::apply(point, first, last, strategy, + it_min1, it_min2, dist_min); + + iterator_type it_back = --last; + Distance const zero = Distance(0); + Distance dist = strategy.apply(point, *it_back, *first); + + if (geometry::math::equals(dist, zero)) + { + dist_min = zero; + it_min1 = it_back; + it_min2 = first; + } + else if (dist < dist_min) + { + dist_min = dist; + it_min1 = it_back; + it_min2 = first; + } + } + +public: + typedef typename std::pair return_type; + + template + static inline return_type apply(Point const& point, + iterator_type first, + iterator_type last, + Strategy const& strategy, + Distance& dist_min) + { + iterator_type it_min1, it_min2; + + apply(point, first, last, strategy, it_min1, it_min2, dist_min); + + return std::make_pair(it_min1, it_min2); + } + + static inline return_type apply(Point const& point, + iterator_type first, + iterator_type last, + Strategy const& strategy) + { + typedef typename strategy::distance::services::return_type + < + Strategy, + Point, + typename boost::range_value::type + >::type distance_return_type; + + distance_return_type dist_min; + + return apply(point, first, last, strategy, dist_min); + } + + template + static inline return_type apply(Point const& point, + Range const& range, + Strategy const& strategy, + Distance& dist_min) + { + return apply(point, + boost::begin(range), + boost::end(range), + strategy, + dist_min); + } + + static inline return_type apply(Point const& point, + Range const& range, + Strategy const& strategy) + { + return apply(point, boost::begin(range), boost::end(range), strategy); + } +}; + + +}} // namespace detail::closest_feature +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_POINT_TO_RANGE_HPP diff --git a/boost/geometry/algorithms/detail/closest_feature/range_to_range.hpp b/boost/geometry/algorithms/detail/closest_feature/range_to_range.hpp new file mode 100644 index 0000000000..90248767ec --- /dev/null +++ b/boost/geometry/algorithms/detail/closest_feature/range_to_range.hpp @@ -0,0 +1,196 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_RANGE_TO_RANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_RANGE_TO_RANGE_HPP + +#include + +#include +#include + +#include + +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace closest_feature +{ + + +// returns a pair of a objects where the first is an object of the +// r-tree range and the second an object of the query range that +// realizes the closest feature of the two ranges +class range_to_range_rtree +{ +private: + template + < + typename RTreeRangeIterator, + typename QueryRangeIterator, + typename Strategy, + typename RTreeValueType, + typename Distance + > + static inline void apply(RTreeRangeIterator rtree_first, + RTreeRangeIterator rtree_last, + QueryRangeIterator queries_first, + QueryRangeIterator queries_last, + Strategy const& strategy, + RTreeValueType& rtree_min, + QueryRangeIterator& qit_min, + Distance& dist_min) + { + typedef index::rtree > rtree_type; + + BOOST_ASSERT( rtree_first != rtree_last ); + BOOST_ASSERT( queries_first != queries_last ); + + Distance const zero = Distance(0); + + // create -- packing algorithm + rtree_type rt(rtree_first, rtree_last); + + RTreeValueType t_v; + bool first = true; + + for (QueryRangeIterator qit = queries_first; + qit != queries_last; ++qit, first = false) + { + std::size_t n = rt.query(index::nearest(*qit, 1), &t_v); + + BOOST_ASSERT( n > 0 ); + // n above is unused outside BOOST_ASSERT, hence the call + // to boost::ignore_unused below + // + // however, t_v (initialized by the call to rt.query(...)) + // is used below, which is why we cannot put the call to + // rt.query(...) inside BOOST_ASSERT + boost::ignore_unused(n); + + Distance dist = dispatch::distance + < + RTreeValueType, + typename std::iterator_traits + < + QueryRangeIterator + >::value_type, + Strategy + >::apply(t_v, *qit, strategy); + + if (first || dist < dist_min) + { + dist_min = dist; + rtree_min = t_v; + qit_min = qit; + if ( math::equals(dist_min, zero) ) + { + return; + } + } + } + } + +public: + template + struct return_type + { + typedef std::pair + < + typename std::iterator_traits::value_type, + QueryRangeIterator + > type; + }; + + + template + < + typename RTreeRangeIterator, + typename QueryRangeIterator, + typename Strategy, + typename Distance + > + static inline typename return_type + < + RTreeRangeIterator, QueryRangeIterator + >::type apply(RTreeRangeIterator rtree_first, + RTreeRangeIterator rtree_last, + QueryRangeIterator queries_first, + QueryRangeIterator queries_last, + Strategy const& strategy, + Distance& dist_min) + { + typedef typename std::iterator_traits + < + RTreeRangeIterator + >::value_type rtree_value_type; + + rtree_value_type rtree_min; + QueryRangeIterator qit_min; + + apply(rtree_first, rtree_last, queries_first, queries_last, + strategy, rtree_min, qit_min, dist_min); + + return std::make_pair(rtree_min, qit_min); + } + + + template + < + typename RTreeRangeIterator, + typename QueryRangeIterator, + typename Strategy + > + static inline typename return_type + < + RTreeRangeIterator, QueryRangeIterator + >::type apply(RTreeRangeIterator rtree_first, + RTreeRangeIterator rtree_last, + QueryRangeIterator queries_first, + QueryRangeIterator queries_last, + Strategy const& strategy) + { + typedef typename std::iterator_traits + < + RTreeRangeIterator + >::value_type rtree_value_type; + + typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type + < + typename std::iterator_traits + < + QueryRangeIterator + >::value_type + >::type + >::type dist_min; + + return apply(rtree_first, rtree_last, queries_first, queries_last, + strategy, dist_min); + } +}; + + +}} // namespace detail::closest_feature +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CLOSEST_FEATURE_RANGE_TO_RANGE_HPP diff --git a/boost/geometry/algorithms/detail/comparable_distance/implementation.hpp b/boost/geometry/algorithms/detail/comparable_distance/implementation.hpp new file mode 100644 index 0000000000..b6eb7a27f1 --- /dev/null +++ b/boost/geometry/algorithms/detail/comparable_distance/implementation.hpp @@ -0,0 +1,24 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_COMPARABLE_DISTANCE_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_COMPARABLE_DISTANCE_IMPLEMENTATION_HPP + +#include + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_COMPARABLE_DISTANCE_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/comparable_distance/interface.hpp b/boost/geometry/algorithms/detail/comparable_distance/interface.hpp new file mode 100644 index 0000000000..1a57c8f4b3 --- /dev/null +++ b/boost/geometry/algorithms/detail/comparable_distance/interface.hpp @@ -0,0 +1,363 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_COMPARABLE_DISTANCE_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_COMPARABLE_DISTANCE_INTERFACE_HPP + +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +namespace resolve_strategy +{ + +struct comparable_distance +{ + template + static inline + typename comparable_distance_result::type + apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + typedef typename strategy::distance::services::comparable_type + < + Strategy + >::type comparable_strategy_type; + + return dispatch::distance + < + Geometry1, Geometry2, comparable_strategy_type + >::apply(geometry1, + geometry2, + strategy::distance::services::get_comparable + < + Strategy + >::apply(strategy)); + } + + template + static inline typename comparable_distance_result + < + Geometry1, Geometry2, default_strategy + >::type + apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + default_strategy) + { + typedef typename strategy::distance::services::comparable_type + < + typename detail::distance::default_strategy + < + Geometry1, Geometry2 + >::type + >::type comparable_strategy_type; + + return dispatch::distance + < + Geometry1, Geometry2, comparable_strategy_type + >::apply(geometry1, geometry2, comparable_strategy_type()); + } +}; + +} // namespace resolve_strategy + + +namespace resolve_variant +{ + + +template +struct comparable_distance +{ + template + static inline + typename comparable_distance_result::type + apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return resolve_strategy::comparable_distance::apply(geometry1, + geometry2, + strategy); + } +}; + + +template +struct comparable_distance + < + boost::variant, + Geometry2 + > +{ + template + struct visitor: static_visitor + < + typename comparable_distance_result + < + boost::variant, + Geometry2, + Strategy + >::type + > + { + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, + Strategy const& strategy) + : m_geometry2(geometry2), + m_strategy(strategy) + {} + + template + typename comparable_distance_result + < + Geometry1, Geometry2, Strategy + >::type + operator()(Geometry1 const& geometry1) const + { + return comparable_distance + < + Geometry1, + Geometry2 + >::template apply + < + Strategy + >(geometry1, m_geometry2, m_strategy); + } + }; + + template + static inline typename comparable_distance_result + < + boost::variant, + Geometry2, + Strategy + >::type + apply(boost::variant const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return apply_visitor(visitor(geometry2, strategy), geometry1); + } +}; + + +template +struct comparable_distance + < + Geometry1, + boost::variant + > +{ + template + struct visitor: static_visitor + < + typename comparable_distance_result + < + Geometry1, + boost::variant, + Strategy + >::type + > + { + Geometry1 const& m_geometry1; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, + Strategy const& strategy) + : m_geometry1(geometry1), + m_strategy(strategy) + {} + + template + typename comparable_distance_result + < + Geometry1, Geometry2, Strategy + >::type + operator()(Geometry2 const& geometry2) const + { + return comparable_distance + < + Geometry1, + Geometry2 + >::template apply + < + Strategy + >(m_geometry1, geometry2, m_strategy); + } + }; + + template + static inline typename comparable_distance_result + < + Geometry1, + boost::variant, + Strategy + >::type + apply(Geometry1 const& geometry1, + boost::variant const& geometry2, + Strategy const& strategy) + { + return apply_visitor(visitor(geometry1, strategy), geometry2); + } +}; + + +template +< + BOOST_VARIANT_ENUM_PARAMS(typename A), + BOOST_VARIANT_ENUM_PARAMS(typename B) +> +struct comparable_distance + < + boost::variant, + boost::variant + > +{ + template + struct visitor: static_visitor + < + typename comparable_distance_result + < + boost::variant, + boost::variant, + Strategy + >::type + > + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy) + : m_strategy(strategy) + {} + + template + typename comparable_distance_result + < + Geometry1, Geometry2, Strategy + >::type + operator()(Geometry1 const& geometry1, Geometry2 const& geometry2) const + { + return comparable_distance + < + Geometry1, + Geometry2 + >::template apply + < + Strategy + >(geometry1, geometry2, m_strategy); + } + }; + + template + static inline typename comparable_distance_result + < + boost::variant, + boost::variant, + Strategy + >::type + apply(boost::variant const& geometry1, + boost::variant const& geometry2, + Strategy const& strategy) + { + return apply_visitor(visitor(strategy), geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + + +/*! +\brief \brief_calc2{comparable distance measurement} \brief_strategy +\ingroup distance +\details The free function comparable_distance does not necessarily calculate the distance, + but it calculates a distance measure such that two distances are comparable to each other. + For example: for the Cartesian coordinate system, Pythagoras is used but the square root + is not taken, which makes it faster and the results of two point pairs can still be + compared to each other. +\tparam Geometry1 first geometry type +\tparam Geometry2 second geometry type +\tparam Strategy \tparam_strategy{Distance} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy \param_strategy{distance} +\return \return_calc{comparable distance} + +\qbk{distinguish,with strategy} + */ +template +inline typename comparable_distance_result::type +comparable_distance(Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + concept::check(); + concept::check(); + + return resolve_variant::comparable_distance + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, strategy); +} + + + +/*! +\brief \brief_calc2{comparable distance measurement} +\ingroup distance +\details The free function comparable_distance does not necessarily calculate the distance, + but it calculates a distance measure such that two distances are comparable to each other. + For example: for the Cartesian coordinate system, Pythagoras is used but the square root + is not taken, which makes it faster and the results of two point pairs can still be + compared to each other. +\tparam Geometry1 first geometry type +\tparam Geometry2 second geometry type +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_calc{comparable distance} + +\qbk{[include reference/algorithms/comparable_distance.qbk]} + */ +template +inline typename default_comparable_distance_result::type +comparable_distance(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check(); + concept::check(); + + return comparable_distance(geometry1, geometry2, default_strategy()); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_COMPARABLE_DISTANCE_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp b/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp index d39824a61d..fccdf4bb1d 100644 --- a/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp +++ b/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp @@ -33,9 +33,9 @@ namespace detail { namespace conversion template < - typename Source, - typename Destination, - std::size_t Dimension, + typename Source, + typename Destination, + std::size_t Dimension, std::size_t DimensionCount > struct indexed_to_indexed @@ -44,25 +44,25 @@ struct indexed_to_indexed { typedef typename coordinate_type::type coordinate_type; - geometry::set(destination, + geometry::set(destination, boost::numeric_cast( geometry::get(source))); - geometry::set(destination, + geometry::set(destination, boost::numeric_cast( geometry::get(source))); - + indexed_to_indexed < - Source, Destination, + Source, Destination, Dimension + 1, DimensionCount >::apply(source, destination); } }; -template +template < - typename Source, - typename Destination, + typename Source, + typename Destination, std::size_t DimensionCount > struct indexed_to_indexed diff --git a/boost/geometry/algorithms/detail/counting.hpp b/boost/geometry/algorithms/detail/counting.hpp new file mode 100644 index 0000000000..dc5bb26c10 --- /dev/null +++ b/boost/geometry/algorithms/detail/counting.hpp @@ -0,0 +1,107 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_COUNTING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_COUNTING_HPP + +#include + +#include + +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace counting +{ + + +template +struct other_count +{ + template + static inline std::size_t apply(Geometry const&) + { + return D; + } + + template + static inline std::size_t apply(Geometry const&, bool) + { + return D; + } +}; + + +template +struct polygon_count +{ + template + static inline std::size_t apply(Polygon const& poly) + { + std::size_t n = RangeCount::apply(exterior_ring(poly)); + + typename interior_return_type::type + rings = interior_rings(poly); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) + { + n += RangeCount::apply(*it); + } + + return n; + } +}; + + +template +struct multi_count +{ + template + static inline std::size_t apply(MultiGeometry const& geometry) + { + std::size_t n = 0; + for (typename boost::range_iterator::type + it = boost::begin(geometry); + it != boost::end(geometry); + ++it) + { + n += SingleCount::apply(*it); + } + return n; + } +}; + + +}} // namespace detail::counting +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_COUNTING_HPP diff --git a/boost/geometry/algorithms/detail/course.hpp b/boost/geometry/algorithms/detail/course.hpp new file mode 100644 index 0000000000..e1a74c0fee --- /dev/null +++ b/boost/geometry/algorithms/detail/course.hpp @@ -0,0 +1,56 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_COURSE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_COURSE_HPP + +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +/// Calculate course (bearing) between two points. +template +inline ReturnType course(Point1 const& p1, Point2 const& p2) +{ + // http://williams.best.vwh.net/avform.htm#Crs + ReturnType dlon = get_as_radian<0>(p2) - get_as_radian<0>(p1); + ReturnType cos_p2lat = cos(get_as_radian<1>(p2)); + + // An optimization which should kick in often for Boxes + //if ( math::equals(dlon, ReturnType(0)) ) + //if ( get<0>(p1) == get<0>(p2) ) + //{ + // return - sin(get_as_radian<1>(p1)) * cos_p2lat); + //} + + // "An alternative formula, not requiring the pre-computation of d" + // In the formula below dlon is used as "d" + return atan2(sin(dlon) * cos_p2lat, + cos(get_as_radian<1>(p1)) * sin(get_as_radian<1>(p2)) + - sin(get_as_radian<1>(p1)) * cos_p2lat * cos(dlon)); +} + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_COURSE_HPP diff --git a/boost/geometry/algorithms/detail/disjoint.hpp b/boost/geometry/algorithms/detail/disjoint.hpp deleted file mode 100644 index 2ced5b1ce3..0000000000 --- a/boost/geometry/algorithms/detail/disjoint.hpp +++ /dev/null @@ -1,225 +0,0 @@ -// 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_DETAIL_DISJOINT_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_HPP - -// Note: contrary to most files, the geometry::detail::disjoint namespace -// is partly implemented in a separate file, to avoid circular references -// disjoint -> get_turns -> disjoint - -#include - -#include - -#include -#include -#include - - -#include - - - -namespace boost { namespace geometry -{ - - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace disjoint -{ - - -struct disjoint_interrupt_policy -{ - static bool const enabled = true; - bool has_intersections; - - inline disjoint_interrupt_policy() - : has_intersections(false) - {} - - template - inline bool apply(Range const& range) - { - // If there is any IP in the range, it is NOT disjoint - if (boost::size(range) > 0) - { - has_intersections = true; - return true; - } - return false; - } -}; - - - -template -< - typename Point1, typename Point2, - std::size_t Dimension, std::size_t DimensionCount -> -struct point_point -{ - static inline bool apply(Point1 const& p1, Point2 const& p2) - { - if (! geometry::math::equals(get(p1), get(p2))) - { - return true; - } - return point_point - < - Point1, Point2, - Dimension + 1, DimensionCount - >::apply(p1, p2); - } -}; - - -template -struct point_point -{ - static inline bool apply(Point1 const& , Point2 const& ) - { - return false; - } -}; - - -template -< - typename Point, typename Box, - std::size_t Dimension, std::size_t DimensionCount -> -struct point_box -{ - static inline bool apply(Point const& point, Box const& box) - { - if (get(point) < get(box) - || get(point) > get(box)) - { - return true; - } - return point_box - < - Point, Box, - Dimension + 1, DimensionCount - >::apply(point, box); - } -}; - - -template -struct point_box -{ - static inline bool apply(Point const& , Box const& ) - { - return false; - } -}; - - -template -< - typename Box1, typename Box2, - std::size_t Dimension, std::size_t DimensionCount -> -struct box_box -{ - static inline bool apply(Box1 const& box1, Box2 const& box2) - { - if (get(box1) < get(box2)) - { - return true; - } - if (get(box1) > get(box2)) - { - return true; - } - return box_box - < - Box1, Box2, - Dimension + 1, DimensionCount - >::apply(box1, box2); - } -}; - - -template -struct box_box -{ - static inline bool apply(Box1 const& , Box2 const& ) - { - return false; - } -}; - - - -/*! - \brief Internal utility function to detect of boxes are disjoint - \note Is used from other algorithms, declared separately - to avoid circular references - */ -template -inline bool disjoint_box_box(Box1 const& box1, Box2 const& box2) -{ - return box_box - < - Box1, Box2, - 0, dimension::type::value - >::apply(box1, box2); -} - - - -/*! - \brief Internal utility function to detect of points are disjoint - \note To avoid circular references - */ -template -inline bool disjoint_point_point(Point1 const& point1, Point2 const& point2) -{ - return point_point - < - Point1, Point2, - 0, dimension::type::value - >::apply(point1, point2); -} - - -}} // namespace detail::disjoint -#endif // DOXYGEN_NO_DETAIL - - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace equals -{ - -/*! - \brief Internal utility function to detect of points are disjoint - \note To avoid circular references - */ -template -inline bool equals_point_point(Point1 const& point1, Point2 const& point2) -{ - return ! detail::disjoint::disjoint_point_point(point1, point2); -} - - -}} // namespace detail::equals -#endif // DOXYGEN_NO_DETAIL - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/areal_areal.hpp b/boost/geometry/algorithms/detail/disjoint/areal_areal.hpp new file mode 100644 index 0000000000..284858a130 --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/areal_areal.hpp @@ -0,0 +1,134 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_AREAL_AREAL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_AREAL_AREAL_HPP + +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +template +struct check_each_ring_for_within +{ + bool not_disjoint; + Geometry const& m_geometry; + + inline check_each_ring_for_within(Geometry const& g) + : not_disjoint(false) + , m_geometry(g) + {} + + template + inline void apply(Range const& range) + { + typename point_type::type pt; + not_disjoint = not_disjoint + || ( geometry::point_on_border(pt, range) + && geometry::covered_by(pt, m_geometry) ); + } +}; + + + +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.not_disjoint; +} + + + +template +struct general_areal +{ + static inline + bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + if ( ! disjoint_linear::apply(geometry1, geometry2) ) + { + return false; + } + + // If there is no intersection of segments, they might located + // inside each other + + // We check that using a point on the border (external boundary), + // and see if that is contained in the other geometry. And vice versa. + + if ( rings_containing(geometry1, geometry2) + || rings_containing(geometry2, geometry1) ) + { + return false; + } + + return true; + } +}; + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct disjoint + : detail::disjoint::general_areal +{}; + + +template +struct disjoint + : detail::disjoint::general_areal +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_AREAL_AREAL_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/box_box.hpp b/boost/geometry/algorithms/detail/disjoint/box_box.hpp new file mode 100644 index 0000000000..ccff9799fd --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/box_box.hpp @@ -0,0 +1,114 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_BOX_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_BOX_BOX_HPP + +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +template +< + typename Box1, typename Box2, + std::size_t Dimension, std::size_t DimensionCount +> +struct box_box +{ + static inline bool apply(Box1 const& box1, Box2 const& box2) + { + if (get(box1) < get(box2)) + { + return true; + } + if (get(box1) > get(box2)) + { + return true; + } + return box_box + < + Box1, Box2, + Dimension + 1, DimensionCount + >::apply(box1, box2); + } +}; + + +template +struct box_box +{ + static inline bool apply(Box1 const& , Box2 const& ) + { + return false; + } +}; + + +/*! + \brief Internal utility function to detect of boxes are disjoint + \note Is used from other algorithms, declared separately + to avoid circular references + */ +template +inline bool disjoint_box_box(Box1 const& box1, Box2 const& box2) +{ + return box_box + < + Box1, Box2, + 0, dimension::type::value + >::apply(box1, box2); +} + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct disjoint + : detail::disjoint::box_box +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_BOX_BOX_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/implementation.hpp b/boost/geometry/algorithms/detail/disjoint/implementation.hpp new file mode 100644 index 0000000000..0c8079b8e4 --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/implementation.hpp @@ -0,0 +1,36 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_IMPLEMENTATION_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/interface.hpp b/boost/geometry/algorithms/detail/disjoint/interface.hpp new file mode 100644 index 0000000000..ec9057ba0d --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/interface.hpp @@ -0,0 +1,187 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_INTERFACE_HPP + +#include + +#include +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// If reversal is needed, perform it +template +< + typename Geometry1, typename Geometry2, + std::size_t DimensionCount, + typename Tag1, typename Tag2 +> +struct disjoint +{ + static inline bool apply(Geometry1 const& g1, Geometry2 const& g2) + { + return disjoint + < + Geometry2, Geometry1, + DimensionCount, + Tag2, Tag1 + >::apply(g2, g1); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_variant { + +template +struct disjoint +{ + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + concept::check_concepts_and_equal_dimensions + < + Geometry1 const, + Geometry2 const + >(); + + return dispatch::disjoint::apply(geometry1, geometry2); + } +}; + +template +struct disjoint, Geometry2> +{ + struct visitor: boost::static_visitor + { + Geometry2 const& m_geometry2; + + visitor(Geometry2 const& geometry2): m_geometry2(geometry2) {} + + template + bool operator()(Geometry1 const& geometry1) const + { + return disjoint::apply(geometry1, m_geometry2); + } + }; + + static inline bool + apply(boost::variant const& geometry1, + Geometry2 const& geometry2) + { + return boost::apply_visitor(visitor(geometry2), geometry1); + } +}; + +template +struct disjoint > +{ + struct visitor: boost::static_visitor + { + Geometry1 const& m_geometry1; + + visitor(Geometry1 const& geometry1): m_geometry1(geometry1) {} + + template + bool operator()(Geometry2 const& geometry2) const + { + return disjoint::apply(m_geometry1, geometry2); + } + }; + + static inline bool + apply(Geometry1 const& geometry1, + boost::variant const& geometry2) + { + return boost::apply_visitor(visitor(geometry1), geometry2); + } +}; + +template < + BOOST_VARIANT_ENUM_PARAMS(typename T1), + BOOST_VARIANT_ENUM_PARAMS(typename T2) +> +struct disjoint< + boost::variant, + boost::variant +> +{ + struct visitor: boost::static_visitor + { + template + bool operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + return disjoint::apply(geometry1, geometry2); + } + }; + + static inline bool + apply(boost::variant const& geometry1, + boost::variant const& geometry2) + { + return boost::apply_visitor(visitor(), geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + + +/*! +\brief \brief_check2{are disjoint} +\ingroup disjoint +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{are disjoint} + +\qbk{[include reference/algorithms/disjoint.qbk]} +*/ +template +inline bool disjoint(Geometry1 const& geometry1, + Geometry2 const& geometry2) +{ + return resolve_variant::disjoint::apply(geometry1, geometry2); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/linear_areal.hpp b/boost/geometry/algorithms/detail/disjoint/linear_areal.hpp new file mode 100644 index 0000000000..eefd351b8d --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/linear_areal.hpp @@ -0,0 +1,244 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_LINEAR_AREAL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_LINEAR_AREAL_HPP + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +template +struct disjoint_linear_areal +{ + static inline bool apply(Geometry1 const& g1, Geometry2 const& g2) + { + // if there are intersections - return false + if ( !disjoint_linear::apply(g1, g2) ) + return false; + + typedef typename point_type::type point1_type; + point1_type p; + geometry::point_on_border(p, g1); + return !geometry::covered_by(p, g2); + } +}; + + + + +template +< + typename Segment, + typename Areal, + typename Tag = typename tag::type +> +struct disjoint_segment_areal + : not_implemented +{}; + + +template +class disjoint_segment_areal +{ +private: + template + static inline bool check_interior_rings(RingIterator first, + RingIterator beyond, + Segment const& segment) + { + for (RingIterator it = first; it != beyond; ++it) + { + if ( !disjoint_range_segment_or_box + < + typename std::iterator_traits + < + RingIterator + >::value_type, + closure::value, + Segment + >::apply(*it, segment) ) + { + return false; + } + } + return true; + } + + + template + static inline + bool check_interior_rings(InteriorRings const& interior_rings, + Segment const& segment) + { + return check_interior_rings(boost::begin(interior_rings), + boost::end(interior_rings), + segment); + } + + +public: + static inline bool apply(Segment const& segment, Polygon const& polygon) + { + typedef typename geometry::ring_type::type ring; + + if ( !disjoint_range_segment_or_box + < + ring, closure::value, Segment + >::apply(geometry::exterior_ring(polygon), segment) ) + { + return false; + } + + if ( !check_interior_rings(geometry::interior_rings(polygon), segment) ) + { + return false; + } + + typename point_type::type p; + detail::assign_point_from_index<0>(segment, p); + + return !geometry::covered_by(p, polygon); + } +}; + + +template +struct disjoint_segment_areal +{ + static inline + bool apply(Segment const& segment, MultiPolygon const& multipolygon) + { + return disjoint_multirange_segment_or_box + < + MultiPolygon, Segment + >::apply(multipolygon, segment); + } +}; + + +template +struct disjoint_segment_areal +{ + static inline bool apply(Segment const& segment, Ring const& ring) + { + if ( !disjoint_range_segment_or_box + < + Ring, closure::value, Segment + >::apply(ring, segment) ) + { + return false; + } + + typename point_type::type p; + detail::assign_point_from_index<0>(segment, p); + + return !geometry::covered_by(p, ring); + } +}; + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct disjoint + : public detail::disjoint::disjoint_linear_areal +{}; + + +template +struct disjoint +{ + static inline + bool apply(Areal const& areal, Linear const& linear) + { + return detail::disjoint::disjoint_linear_areal + < + Linear, Areal + >::apply(linear, areal); + } +}; + + +template +struct disjoint +{ + static inline bool apply(Areal const& g1, Segment const& g2) + { + return detail::disjoint::disjoint_segment_areal + < + Segment, Areal + >::apply(g2, g1); + } +}; + + +template +struct disjoint + : detail::disjoint::disjoint_segment_areal +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_LINEAR_AREAL_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp b/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp new file mode 100644 index 0000000000..ad84d7191d --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp @@ -0,0 +1,174 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_LINEAR_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_LINEAR_LINEAR_HPP + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + +template +struct disjoint_segment +{ + static inline bool apply(Segment1 const& segment1, Segment2 const& segment2) + { + typedef typename point_type::type point_type; + + // We don't need to rescale to detect disjointness + typedef no_rescale_policy rescale_policy_type; + rescale_policy_type robust_policy; + + typedef segment_intersection_points + < + point_type, + typename segment_ratio_type + < + point_type, + rescale_policy_type + >::type + > intersection_return_type; + + intersection_return_type is + = strategy::intersection::relate_cartesian_segments + < + policies::relate::segments_intersection_points + < + intersection_return_type + > + >::apply(segment1, segment2, robust_policy); + + return is.count == 0; + } +}; + + +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 + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply(Info& , Point1 const& , Point2 const&, + IntersectionInfo const&, DirInfo const&) + {} +}; + + +template +struct disjoint_linear +{ + static inline + bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + typedef typename geometry::point_type::type point_type; + typedef detail::no_rescale_policy rescale_policy_type; + typedef overlay::turn_info + < + point_type, + typename segment_ratio_type::type + > turn_info; + std::deque turns; + + static const bool reverse1 = overlay::do_reverse::value>::value; // should be false + static const bool reverse2 = overlay::do_reverse::value>::value; // should be false + + // Specify two policies: + // 1) Stop at any intersection + // 2) In assignment, include also degenerate points (which are normally skipped) + disjoint_interrupt_policy policy; + rescale_policy_type robust_policy; + geometry::get_turns + < + reverse1, reverse2, + assign_disjoint_policy + >(geometry1, geometry2, robust_policy, turns, policy); + + return !policy.has_intersections; + } +}; + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct disjoint + : detail::disjoint::disjoint_linear +{}; + + +template +struct disjoint + : detail::disjoint::disjoint_segment +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_LINEAR_LINEAR_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp b/boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp new file mode 100644 index 0000000000..d181726e2e --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp @@ -0,0 +1,195 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_LINEAR_SEGMENT_OR_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_LINEAR_SEGMENT_OR_BOX_HPP + +#include +#include + +#include + +#include + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +template +struct disjoint_multirange_segment_or_box +{ + static inline + bool apply(MultiRange const& multirange, SegmentOrBox const& segment_or_box) + { + typedef typename boost::range_iterator + < + MultiRange const + >::type const_iterator; + + for (const_iterator it = boost::begin(multirange); + it != boost::end(multirange); ++it) + { + if ( !dispatch::disjoint + < + typename boost::range_value::type, + SegmentOrBox + >::apply(*it, segment_or_box) ) + { + return false; + } + } + return true; + } +}; + + +template +< + typename Range, + closure_selector Closure, + typename SegmentOrBox +> +struct disjoint_range_segment_or_box +{ + static inline + bool apply(Range const& range, SegmentOrBox const& segment_or_box) + { + typedef typename closeable_view::type view_type; + + typedef typename ::boost::range_value::type point_type; + typedef typename ::boost::range_iterator + < + view_type const + >::type const_iterator; + + typedef typename ::boost::range_size::type size_type; + + typedef typename geometry::model::referring_segment + < + point_type const + > range_segment; + + view_type view(range); + + const size_type count = ::boost::size(view); + + if ( count == 0 ) + { + return false; + } + else if ( count == 1 ) + { + return dispatch::disjoint + < + point_type, SegmentOrBox + >::apply(geometry::range::front(view), + segment_or_box); + } + else + { + const_iterator it0 = ::boost::begin(view); + const_iterator it1 = ::boost::begin(view) + 1; + const_iterator last = ::boost::end(view); + + for ( ; it1 != last ; ++it0, ++it1 ) + { + range_segment rng_segment(*it0, *it1); + if ( !dispatch::disjoint + < + range_segment, SegmentOrBox + >::apply(rng_segment, segment_or_box) ) + { + return false; + } + } + return true; + } + } +}; + + + + +template +< + typename Linear, + typename SegmentOrBox, + typename Tag = typename tag::type +> +struct disjoint_linear_segment_or_box + : not_implemented +{}; + + +template +struct disjoint_linear_segment_or_box + : disjoint_range_segment_or_box +{}; + + +template +struct disjoint_linear_segment_or_box + < + MultiLinestring, SegmentOrBox, multi_linestring_tag + > : disjoint_multirange_segment_or_box +{}; + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct disjoint + : detail::disjoint::disjoint_linear_segment_or_box +{}; + + +template +struct disjoint + : detail::disjoint::disjoint_linear_segment_or_box +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_LINEAR_SEGMENT_OR_BOX_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/point_box.hpp b/boost/geometry/algorithms/detail/disjoint/point_box.hpp new file mode 100644 index 0000000000..ea6609a153 --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/point_box.hpp @@ -0,0 +1,94 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_POINT_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_POINT_BOX_HPP + +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +template +< + typename Point, typename Box, + std::size_t Dimension, std::size_t DimensionCount +> +struct point_box +{ + static inline bool apply(Point const& point, Box const& box) + { + if (get(point) < get(box) + || get(point) > get(box)) + { + return true; + } + return point_box + < + Point, Box, + Dimension + 1, DimensionCount + >::apply(point, box); + } +}; + + +template +struct point_box +{ + static inline bool apply(Point const& , Box const& ) + { + return false; + } +}; + + +}} // namespace detail::equals +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct disjoint + : detail::disjoint::point_box +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_POINT_BOX_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/point_geometry.hpp b/boost/geometry/algorithms/detail/disjoint/point_geometry.hpp new file mode 100644 index 0000000000..a58bff41da --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/point_geometry.hpp @@ -0,0 +1,111 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_POINT_GEOMETRY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_POINT_GEOMETRY_HPP + +#include + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +template +struct disjoint_point_linear +{ + static inline + bool apply(Point const& pt, Geometry const& g) + { + return !geometry::covered_by(pt, g); + } +}; + + +template +struct reverse_covered_by +{ + static inline + bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + return !geometry::covered_by(geometry1, geometry2); + } +}; + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct disjoint + : public detail::disjoint::disjoint_point_linear +{}; + + +template +struct disjoint + : detail::disjoint::reverse_covered_by +{}; + + +template +struct disjoint +{ + static inline bool apply(Point const& point, Segment const& segment) + { + typedef geometry::model::referring_segment other_segment; + + other_segment other(point, point); + return detail::disjoint::disjoint_segment + < + Segment, other_segment + >::apply(segment, other); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_POINT_GEOMETRY_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/point_point.hpp b/boost/geometry/algorithms/detail/disjoint/point_point.hpp new file mode 100644 index 0000000000..b1d32bf95e --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/point_point.hpp @@ -0,0 +1,112 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_POINT_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_POINT_POINT_HPP + +#include + +#include +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + +template +< + typename Point1, typename Point2, + std::size_t Dimension, std::size_t DimensionCount +> +struct point_point +{ + static inline bool apply(Point1 const& p1, Point2 const& p2) + { + if (! geometry::math::equals(get(p1), get(p2))) + { + return true; + } + return point_point + < + Point1, Point2, + Dimension + 1, DimensionCount + >::apply(p1, p2); + } +}; + + +template +struct point_point +{ + static inline bool apply(Point1 const& , Point2 const& ) + { + return false; + } +}; + + +/*! + \brief Internal utility function to detect of points are disjoint + \note To avoid circular references + */ +template +inline bool disjoint_point_point(Point1 const& point1, Point2 const& point2) +{ + return point_point + < + Point1, Point2, + 0, dimension::type::value + >::apply(point1, point2); +} + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct disjoint + : detail::disjoint::point_point +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_POINT_POINT_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/segment_box.hpp b/boost/geometry/algorithms/detail/disjoint/segment_box.hpp new file mode 100644 index 0000000000..5368432ed4 --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/segment_box.hpp @@ -0,0 +1,291 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISJOINT_SEGMENT_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_SEGMENT_BOX_HPP + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +template +struct compute_tmin_tmax_per_dim +{ + template + static inline void apply(SegmentPoint const& p0, + SegmentPoint const& p1, + Box const& box, + RelativeDistance& ti_min, + RelativeDistance& ti_max, + RelativeDistance& diff) + { + typedef typename coordinate_type::type box_coordinate_type; + typedef typename coordinate_type + < + SegmentPoint + >::type point_coordinate_type; + + RelativeDistance c_p0 = boost::numeric_cast + < + point_coordinate_type + >( geometry::get(p0) ); + + RelativeDistance c_p1 = boost::numeric_cast + < + point_coordinate_type + >( geometry::get(p1) ); + + RelativeDistance c_b_min = boost::numeric_cast + < + box_coordinate_type + >( geometry::get(box) ); + + RelativeDistance c_b_max = boost::numeric_cast + < + box_coordinate_type + >( geometry::get(box) ); + + if ( geometry::get(p1) >= geometry::get(p0) ) + { + diff = c_p1 - c_p0; + ti_min = c_b_min - c_p0; + ti_max = c_b_max - c_p0; + } + else + { + diff = c_p0 - c_p1; + ti_min = c_p0 - c_b_max; + ti_max = c_p0 - c_b_min; + } + } +}; + + +template +< + typename RelativeDistance, + typename SegmentPoint, + typename Box, + std::size_t I, + std::size_t Dimension +> +struct disjoint_segment_box_impl +{ + template + static inline bool apply(SegmentPoint const& p0, + SegmentPoint const& p1, + Box const& box, + RelativeDistancePair& t_min, + RelativeDistancePair& t_max) + { + RelativeDistance ti_min, ti_max, diff; + + compute_tmin_tmax_per_dim::apply(p0, p1, box, ti_min, ti_max, diff); + + if ( geometry::math::equals(diff, 0) ) + { + if ( (geometry::math::equals(t_min.second, 0) + && t_min.first > ti_max) + || + (geometry::math::equals(t_max.second, 0) + && t_max.first < ti_min) ) + { + return true; + } + } + + RelativeDistance t_min_x_diff = t_min.first * diff; + RelativeDistance t_max_x_diff = t_max.first * diff; + + if ( t_min_x_diff > ti_max * t_min.second + || t_max_x_diff < ti_min * t_max.second ) + { + return true; + } + + if ( ti_min * t_min.second > t_min_x_diff ) + { + t_min.first = ti_min; + t_min.second = diff; + } + if ( ti_max * t_max.second < t_max_x_diff ) + { + t_max.first = ti_max; + t_max.second = diff; + } + + if ( t_min.first > t_min.second || t_max.first < 0 ) + { + return true; + } + + return disjoint_segment_box_impl + < + RelativeDistance, + SegmentPoint, + Box, + I + 1, + Dimension + >::apply(p0, p1, box, t_min, t_max); + } +}; + + +template +< + typename RelativeDistance, + typename SegmentPoint, + typename Box, + std::size_t Dimension +> +struct disjoint_segment_box_impl + < + RelativeDistance, SegmentPoint, Box, 0, Dimension + > +{ + static inline bool apply(SegmentPoint const& p0, + SegmentPoint const& p1, + Box const& box) + { + std::pair t_min, t_max; + RelativeDistance diff; + + compute_tmin_tmax_per_dim<0>::apply(p0, p1, box, + t_min.first, t_max.first, diff); + + if ( geometry::math::equals(diff, 0) ) + { + if ( geometry::math::equals(t_min.first, 0) ) { t_min.first = -1; } + if ( geometry::math::equals(t_max.first, 0) ) { t_max.first = 1; } + } + + if ( t_min.first > diff || t_max.first < 0 ) + { + return true; + } + + t_min.second = t_max.second = diff; + + return disjoint_segment_box_impl + < + RelativeDistance, SegmentPoint, Box, 1, Dimension + >::apply(p0, p1, box, t_min, t_max); + } +}; + + +template +< + typename RelativeDistance, + typename SegmentPoint, + typename Box, + std::size_t Dimension +> +struct disjoint_segment_box_impl + < + RelativeDistance, SegmentPoint, Box, Dimension, Dimension + > +{ + template + static inline bool apply(SegmentPoint const&, SegmentPoint const&, + Box const&, + RelativeDistancePair&, RelativeDistancePair&) + { + return false; + } +}; + + +//========================================================================= + + +template +struct disjoint_segment_box +{ + static inline bool apply(Segment const& segment, Box const& box) + { + assert_dimension_equal(); + + typedef typename util::calculation_type::geometric::binary + < + Segment, Box, void + >::type relative_distance_type; + + typedef typename point_type::type segment_point_type; + segment_point_type p0, p1; + geometry::detail::assign_point_from_index<0>(segment, p0); + geometry::detail::assign_point_from_index<1>(segment, p1); + + return disjoint_segment_box_impl + < + relative_distance_type, segment_point_type, Box, + 0, dimension::value + >::apply(p0, p1, box); + } +}; + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct disjoint + : detail::disjoint::disjoint_segment_box +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_SEGMENT_BOX_HPP diff --git a/boost/geometry/algorithms/detail/distance/backward_compatibility.hpp b/boost/geometry/algorithms/detail/distance/backward_compatibility.hpp new file mode 100644 index 0000000000..5b49e571dd --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/backward_compatibility.hpp @@ -0,0 +1,333 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISTANCE_BACKWARD_COMPATIBILITY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_BACKWARD_COMPATIBILITY_HPP + +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +template +struct point_to_segment +{ + static inline typename strategy::distance::services::return_type + < + Strategy, + Point, + typename point_type::type + >::type + apply(Point const& point, Segment const& segment, Strategy const& ) + { + typename detail::distance::default_ps_strategy + < + Point, + typename point_type::type, + Strategy + >::type segment_strategy; + + typename point_type::type p[2]; + geometry::detail::assign_point_from_index<0>(segment, p[0]); + geometry::detail::assign_point_from_index<1>(segment, p[1]); + return segment_strategy.apply(point, p[0], p[1]); + } +}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// Point-segment version 1, with point-point strategy +template +struct distance +< + Point, Segment, Strategy, + point_tag, segment_tag, strategy_tag_distance_point_point, + false +> : detail::distance::point_to_segment +{}; + + +// Point-line version 1, where point-point strategy is specified +template +struct distance +< + Point, Linestring, Strategy, + point_tag, linestring_tag, strategy_tag_distance_point_point, + false +> +{ + + static inline typename strategy::distance::services::return_type + < + Strategy, Point, typename point_type::type + >::type + apply(Point const& point, + Linestring const& linestring, + Strategy const&) + { + typedef typename detail::distance::default_ps_strategy + < + Point, + typename point_type::type, + Strategy + >::type ps_strategy_type; + + return detail::distance::point_to_range + < + Point, Linestring, closed, ps_strategy_type + >::apply(point, linestring, ps_strategy_type()); + } +}; + + +// Point-ring , where point-point strategy is specified +template +struct distance +< + Point, Ring, Strategy, + point_tag, ring_tag, strategy_tag_distance_point_point, + false +> +{ + typedef typename strategy::distance::services::return_type + < + Strategy, Point, typename point_type::type + >::type return_type; + + static inline return_type apply(Point const& point, + Ring const& ring, + Strategy const&) + { + typedef typename detail::distance::default_ps_strategy + < + Point, + typename point_type::type, + Strategy + >::type ps_strategy_type; + + return detail::distance::point_to_ring + < + Point, Ring, + geometry::closure::value, + ps_strategy_type + >::apply(point, ring, ps_strategy_type()); + } +}; + + +// Point-polygon , where point-point strategy is specified +template +struct distance +< + Point, Polygon, Strategy, + point_tag, polygon_tag, strategy_tag_distance_point_point, + false +> +{ + typedef typename strategy::distance::services::return_type + < + Strategy, Point, typename point_type::type + >::type return_type; + + static inline return_type apply(Point const& point, + Polygon const& polygon, + Strategy const&) + { + typedef typename detail::distance::default_ps_strategy + < + Point, + typename point_type::type, + Strategy + >::type ps_strategy_type; + + return detail::distance::point_to_polygon + < + Point, + Polygon, + geometry::closure::value, + ps_strategy_type + >::apply(point, polygon, ps_strategy_type()); + } +}; + + + + +template +< + typename Point, + typename MultiGeometry, + typename MultiGeometryTag, + typename Strategy +> +struct distance + < + Point, MultiGeometry, Strategy, + point_tag, MultiGeometryTag, + strategy_tag_distance_point_point, false + > +{ + typedef typename strategy::distance::services::return_type + < + Strategy, Point, typename point_type::type + >::type return_type; + + static inline return_type apply(Point const& point, + MultiGeometry const& multigeometry, + Strategy const&) + { + typedef typename detail::distance::default_ps_strategy + < + Point, + typename point_type::type, + Strategy + >::type ps_strategy_type; + + return distance + < + Point, MultiGeometry, ps_strategy_type, + point_tag, MultiGeometryTag, + strategy_tag_distance_point_segment, false + >::apply(point, multigeometry, ps_strategy_type()); + } +}; + + +template +< + typename Geometry, + typename MultiPoint, + typename GeometryTag, + typename Strategy +> +struct distance + < + Geometry, MultiPoint, Strategy, + GeometryTag, multi_point_tag, + strategy_tag_distance_point_point, false + > +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(Geometry const& geometry, + MultiPoint const& multipoint, + Strategy const&) + { + typedef typename detail::distance::default_ps_strategy + < + typename point_type::type, + typename point_type::type, + Strategy + >::type ps_strategy_type; + + return distance + < + Geometry, MultiPoint, ps_strategy_type, + GeometryTag, multi_point_tag, + strategy_tag_distance_point_segment, false + >::apply(geometry, multipoint, ps_strategy_type()); + } +}; + + +template +< + typename MultiPoint, + typename MultiGeometry, + typename MultiGeometryTag, + typename Strategy +> +struct distance + < + MultiPoint, MultiGeometry, Strategy, + multi_point_tag, MultiGeometryTag, + strategy_tag_distance_point_point, false + > +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(MultiPoint const& multipoint, + MultiGeometry const& multigeometry, + Strategy const&) + { + typedef typename detail::distance::default_ps_strategy + < + typename point_type::type, + typename point_type::type, + Strategy + >::type ps_strategy_type; + + return distance + < + MultiPoint, MultiGeometry, ps_strategy_type, + multi_point_tag, MultiGeometryTag, + strategy_tag_distance_point_segment, false + >::apply(multipoint, multigeometry, ps_strategy_type()); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_BACKWARD_COMPATIBILITY_HPP diff --git a/boost/geometry/algorithms/detail/distance/box_to_box.hpp b/boost/geometry/algorithms/detail/distance/box_to_box.hpp new file mode 100644 index 0000000000..44778e9e06 --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/box_to_box.hpp @@ -0,0 +1,60 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_BOX_TO_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_BOX_TO_BOX_HPP + +#include + +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct distance + < + Box1, Box2, Strategy, box_tag, box_tag, + strategy_tag_distance_box_box, false + > +{ + static inline typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type + apply(Box1 const& box1, Box2 const& box2, Strategy const& strategy) + { + boost::ignore_unused(strategy); + return strategy.apply(box1, box2); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_BOX_TO_BOX_HPP diff --git a/boost/geometry/algorithms/detail/distance/default_strategies.hpp b/boost/geometry/algorithms/detail/distance/default_strategies.hpp new file mode 100644 index 0000000000..a01ace2b58 --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/default_strategies.hpp @@ -0,0 +1,137 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISTANCE_DEFAULT_STRATEGIES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_DEFAULT_STRATEGIES_HPP + +#include +#include +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + + +// Helper metafunction for default strategy retrieval +template +< + typename Geometry1, + typename Geometry2 = Geometry1, + typename Tag1 = typename tag_cast + < + typename tag::type, pointlike_tag + >::type, + typename Tag2 = typename tag_cast + < + typename tag::type, pointlike_tag + >::type, + bool Reverse = geometry::reverse_dispatch::type::value +> +struct default_strategy + : strategy::distance::services::default_strategy + < + point_tag, segment_tag, + typename point_type::type, + typename point_type::type + > +{}; + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1, + typename Tag2 +> +struct default_strategy + : default_strategy +{}; + + +template +struct default_strategy + < + Pointlike1, Pointlike2, + pointlike_tag, pointlike_tag, false + > : strategy::distance::services::default_strategy + < + point_tag, point_tag, + typename point_type::type, + typename point_type::type + > +{}; + + +template +struct default_strategy + : strategy::distance::services::default_strategy + < + point_tag, box_tag, + typename point_type::type, + typename point_type::type + > +{}; + + +template +struct default_strategy + : strategy::distance::services::default_strategy + < + box_tag, box_tag, + typename point_type::type, + typename point_type::type + > +{}; + + + +// Helper metafunction for default point-segment strategy retrieval +template +struct default_ps_strategy + : strategy::distance::services::default_strategy + < + point_tag, segment_tag, + typename point_type::type, + typename point_type::type, + typename cs_tag::type>::type, + typename cs_tag::type>::type, + Strategy + > +{}; + + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_DEFAULT_STRATEGIES_HPP diff --git a/boost/geometry/algorithms/detail/distance/geometry_to_segment_or_box.hpp b/boost/geometry/algorithms/detail/distance/geometry_to_segment_or_box.hpp new file mode 100644 index 0000000000..04d1095cba --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/geometry_to_segment_or_box.hpp @@ -0,0 +1,462 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_GEOMETRY_TO_SEGMENT_OR_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_GEOMETRY_TO_SEGMENT_OR_BOX_HPP + +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +// closure of segment or box point range +template +< + typename SegmentOrBox, + typename Tag = typename tag::type +> +struct segment_or_box_point_range_closure + : not_implemented +{}; + +template +struct segment_or_box_point_range_closure +{ + static const closure_selector value = closed; +}; + +template +struct segment_or_box_point_range_closure +{ + static const closure_selector value = open; +}; + + + +template +< + typename Geometry, + typename SegmentOrBox, + typename Strategy, + typename Tag = typename tag::type +> +class geometry_to_segment_or_box +{ +private: + typedef typename point_type::type segment_or_box_point; + + typedef typename strategy::distance::services::comparable_type + < + Strategy + >::type comparable_strategy; + + typedef detail::closest_feature::point_to_point_range + < + typename point_type::type, + std::vector, + segment_or_box_point_range_closure::value, + comparable_strategy + > point_to_point_range; + + typedef detail::closest_feature::geometry_to_range geometry_to_range; + + typedef typename strategy::distance::services::return_type + < + comparable_strategy, + typename point_type::type, + segment_or_box_point + >::type comparable_return_type; + + + // assign the new minimum value for an iterator of the point range + // of a segment or a box + template + < + typename SegOrBox, + typename SegOrBoxTag = typename tag::type + > + struct assign_new_min_iterator + : not_implemented + {}; + + template + struct assign_new_min_iterator + { + template + static inline void apply(Iterator&, Iterator) + { + } + }; + + template + struct assign_new_min_iterator + { + template + static inline void apply(Iterator& it_min, Iterator it) + { + it_min = it; + } + }; + + + // assign the points of a segment or a box to a range + template + < + typename SegOrBox, + typename PointRange, + typename SegOrBoxTag = typename tag::type + > + struct assign_segment_or_box_points + {}; + + template + struct assign_segment_or_box_points + { + static inline void apply(Segment const& segment, PointRange& range) + { + detail::assign_point_from_index<0>(segment, range[0]); + detail::assign_point_from_index<1>(segment, range[1]); + } + }; + + template + struct assign_segment_or_box_points + { + static inline void apply(Box const& box, PointRange& range) + { + detail::assign_box_corners_oriented(box, range); + } + }; + + +public: + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + segment_or_box_point + >::type return_type; + + static inline return_type apply(Geometry const& geometry, + SegmentOrBox const& segment_or_box, + Strategy const& strategy, + bool check_intersection = true) + { + typedef geometry::point_iterator point_iterator_type; + typedef geometry::segment_iterator + < + Geometry const + > segment_iterator_type; + + typedef typename std::vector + < + segment_or_box_point + >::const_iterator seg_or_box_iterator_type; + + typedef assign_new_min_iterator assign_new_value; + + + if (check_intersection + && geometry::intersects(geometry, segment_or_box)) + { + return 0; + } + + comparable_strategy cstrategy = + strategy::distance::services::get_comparable + < + Strategy + >::apply(strategy); + + // get all points of the segment or the box + std::vector + seg_or_box_points(geometry::num_points(segment_or_box)); + + assign_segment_or_box_points + < + SegmentOrBox, + std::vector + >::apply(segment_or_box, seg_or_box_points); + + // consider all distances of the points in the geometry to the + // segment or box + comparable_return_type cd_min1; + point_iterator_type pit_min; + seg_or_box_iterator_type it_min1 = seg_or_box_points.begin(); + seg_or_box_iterator_type it_min2 = ++seg_or_box_points.begin(); + bool first = true; + + for (point_iterator_type pit = points_begin(geometry); + pit != points_end(geometry); ++pit, first = false) + { + comparable_return_type cd; + std::pair + < + seg_or_box_iterator_type, seg_or_box_iterator_type + > it_pair + = point_to_point_range::apply(*pit, + seg_or_box_points.begin(), + seg_or_box_points.end(), + cstrategy, + cd); + + if (first || cd < cd_min1) + { + cd_min1 = cd; + pit_min = pit; + assign_new_value::apply(it_min1, it_pair.first); + assign_new_value::apply(it_min2, it_pair.second); + } + } + + // consider all distances of the points in the segment or box to the + // segments of the geometry + comparable_return_type cd_min2; + segment_iterator_type sit_min; + typename std::vector::const_iterator it_min; + + first = true; + for (typename std::vector::const_iterator it + = seg_or_box_points.begin(); + it != seg_or_box_points.end(); ++it, first = false) + { + comparable_return_type cd; + segment_iterator_type sit + = geometry_to_range::apply(*it, + segments_begin(geometry), + segments_end(geometry), + cstrategy, + cd); + + if (first || cd < cd_min2) + { + cd_min2 = cd; + it_min = it; + sit_min = sit; + } + } + + if (is_comparable::value) + { + return (std::min)(cd_min1, cd_min2); + } + + if (cd_min1 < cd_min2) + { + return strategy.apply(*pit_min, *it_min1, *it_min2); + } + else + { + return dispatch::distance + < + segment_or_box_point, + typename std::iterator_traits + < + segment_iterator_type + >::value_type, + Strategy + >::apply(*it_min, *sit_min, strategy); + } + } + + + static inline return_type + apply(SegmentOrBox const& segment_or_box, Geometry const& geometry, + Strategy const& strategy, bool check_intersection = true) + { + return apply(geometry, segment_or_box, strategy, check_intersection); + } +}; + + + +template +class geometry_to_segment_or_box + < + MultiPoint, SegmentOrBox, Strategy, multi_point_tag + > +{ +private: + typedef detail::closest_feature::geometry_to_range base_type; + + typedef typename boost::range_iterator + < + MultiPoint const + >::type iterator_type; + + typedef detail::closest_feature::geometry_to_range geometry_to_range; + +public: + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(MultiPoint const& multipoint, + SegmentOrBox const& segment_or_box, + Strategy const& strategy) + { + namespace sds = strategy::distance::services; + + typename sds::return_type + < + typename sds::comparable_type::type, + typename point_type::type, + typename point_type::type + >::type cd_min; + + iterator_type it_min + = geometry_to_range::apply(segment_or_box, + boost::begin(multipoint), + boost::end(multipoint), + sds::get_comparable + < + Strategy + >::apply(strategy), + cd_min); + + return + is_comparable::value + ? + cd_min + : + dispatch::distance + < + typename point_type::type, + SegmentOrBox, + Strategy + >::apply(*it_min, segment_or_box, strategy); + } +}; + + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct distance + < + Linear, Segment, Strategy, linear_tag, segment_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::geometry_to_segment_or_box +{}; + + +template +struct distance + < + Areal, Segment, Strategy, areal_tag, segment_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::geometry_to_segment_or_box +{}; + + +template +struct distance + < + Segment, Areal, Strategy, segment_tag, areal_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::geometry_to_segment_or_box +{}; + + +template +struct distance + < + Linear, Box, Strategy, linear_tag, box_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::geometry_to_segment_or_box + < + Linear, Box, Strategy + > +{}; + + +template +struct distance + < + Areal, Box, Strategy, areal_tag, box_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::geometry_to_segment_or_box +{}; + + +template +struct distance + < + MultiPoint, Segment, Strategy, + multi_point_tag, segment_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::geometry_to_segment_or_box + < + MultiPoint, Segment, Strategy + > +{}; + + +template +struct distance + < + MultiPoint, Box, Strategy, + multi_point_tag, box_tag, + strategy_tag_distance_point_box, false + > : detail::distance::geometry_to_segment_or_box + < + MultiPoint, Box, Strategy + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_GEOMETRY_TO_SEGMENT_OR_BOX_HPP diff --git a/boost/geometry/algorithms/detail/distance/implementation.hpp b/boost/geometry/algorithms/detail/distance/implementation.hpp new file mode 100644 index 0000000000..7c009f4d79 --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/implementation.hpp @@ -0,0 +1,35 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISTANCE_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_IMPLEMENTATION_HPP + +// the implementation details +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/distance/interface.hpp b/boost/geometry/algorithms/detail/distance/interface.hpp new file mode 100644 index 0000000000..9b377f524b --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/interface.hpp @@ -0,0 +1,403 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2014 Samuel Debionne, Grenoble, France. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISTANCE_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_INTERFACE_HPP + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// If reversal is needed, perform it +template +< + typename Geometry1, typename Geometry2, typename Strategy, + typename Tag1, typename Tag2, typename StrategyTag +> +struct distance +< + Geometry1, Geometry2, Strategy, + Tag1, Tag2, StrategyTag, + true +> + : distance +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply( + Geometry1 const& g1, + Geometry2 const& g2, + Strategy const& strategy) + { + return distance + < + Geometry2, Geometry1, Strategy, + Tag2, Tag1, StrategyTag, + false + >::apply(g2, g1, strategy); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_strategy +{ + +struct distance +{ + template + static inline typename distance_result::type + apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return dispatch::distance + < + Geometry1, Geometry2, Strategy + >::apply(geometry1, geometry2, strategy); + } + + template + static inline + typename distance_result::type + apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + default_strategy) + { + typedef typename detail::distance::default_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + + return dispatch::distance + < + Geometry1, Geometry2, strategy_type + >::apply(geometry1, geometry2, strategy_type()); + } +}; + +} // namespace resolve_strategy + + +namespace resolve_variant +{ + + +template +struct distance +{ + template + static inline typename distance_result::type + apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return + resolve_strategy::distance::apply(geometry1, geometry2, strategy); + } +}; + + +template +struct distance, Geometry2> +{ + template + struct visitor: static_visitor + < + typename distance_result + < + variant, + Geometry2, + Strategy + >::type + > + { + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, + Strategy const& strategy) + : m_geometry2(geometry2), + m_strategy(strategy) + {} + + template + typename distance_result::type + operator()(Geometry1 const& geometry1) const + { + return distance + < + Geometry1, + Geometry2 + >::template apply + < + Strategy + >(geometry1, m_geometry2, m_strategy); + } + }; + + template + static inline typename distance_result + < + variant, + Geometry2, + Strategy + >::type + apply(variant const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) + { + return apply_visitor(visitor(geometry2, strategy), geometry1); + } +}; + + +template +struct distance > +{ + template + struct visitor: static_visitor + < + typename distance_result + < + Geometry1, + variant, + Strategy + >::type + > + { + Geometry1 const& m_geometry1; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, + Strategy const& strategy) + : m_geometry1(geometry1), + m_strategy(strategy) + {} + + template + typename distance_result::type + operator()(Geometry2 const& geometry2) const + { + return distance + < + Geometry1, + Geometry2 + >::template apply + < + Strategy + >(m_geometry1, geometry2, m_strategy); + } + }; + + template + static inline typename distance_result + < + Geometry1, + variant, + Strategy + >::type + apply( + Geometry1 const& geometry1, + const variant& geometry2, + Strategy const& strategy) + { + return apply_visitor(visitor(geometry1, strategy), geometry2); + } +}; + + +template +< + BOOST_VARIANT_ENUM_PARAMS(typename A), + BOOST_VARIANT_ENUM_PARAMS(typename B) +> +struct distance + < + boost::variant, + boost::variant + > +{ + template + struct visitor: static_visitor + < + typename distance_result + < + boost::variant, + boost::variant, + Strategy + >::type + > + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy) + : m_strategy(strategy) + {} + + template + typename distance_result::type + operator()(Geometry1 const& geometry1, Geometry2 const& geometry2) const + { + return distance + < + Geometry1, + Geometry2 + >::template apply + < + Strategy + >(geometry1, geometry2, m_strategy); + } + }; + + template + static inline typename distance_result + < + boost::variant, + boost::variant, + Strategy + >::type + apply(boost::variant const& geometry1, + boost::variant const& geometry2, + Strategy const& strategy) + { + return apply_visitor(visitor(strategy), geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief \brief_calc2{distance} \brief_strategy +\ingroup distance +\details +\details \details_calc{area}. \brief_strategy. \details_strategy_reasons + +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy \tparam_strategy{Distance} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy \param_strategy{distance} +\return \return_calc{distance} +\note The strategy can be a point-point strategy. In case of distance point-line/point-polygon + it may also be a point-segment strategy. + +\qbk{distinguish,with strategy} + +\qbk{ +[heading Available Strategies] +\* [link geometry.reference.strategies.strategy_distance_pythagoras Pythagoras (cartesian)] +\* [link geometry.reference.strategies.strategy_distance_haversine Haversine (spherical)] +\* [link geometry.reference.strategies.strategy_distance_cross_track Cross track (spherical\, point-to-segment)] +\* [link geometry.reference.strategies.strategy_distance_projected_point Projected point (cartesian\, point-to-segment)] +\* more (currently extensions): Vincenty\, Andoyer (geographic) +} + */ + +/* +Note, in case of a Compilation Error: +if you get: + - "Failed to specialize function template ..." + - "error: no matching function for call to ..." +for distance, it is probably so that there is no specialization +for return_type<...> for your strategy. +*/ +template +inline typename distance_result::type +distance(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + concept::check(); + concept::check(); + + detail::throw_on_empty_input(geometry1); + detail::throw_on_empty_input(geometry2); + + return resolve_variant::distance + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, strategy); +} + + +/*! +\brief \brief_calc2{distance} +\ingroup distance +\details The default strategy is used, corresponding to the coordinate system of the geometries +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_calc{distance} + +\qbk{[include reference/algorithms/distance.qbk]} + */ +template +inline typename default_distance_result::type +distance(Geometry1 const& geometry1, + Geometry2 const& geometry2) +{ + concept::check(); + concept::check(); + + return distance(geometry1, geometry2, default_strategy()); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/distance/is_comparable.hpp b/boost/geometry/algorithms/detail/distance/is_comparable.hpp new file mode 100644 index 0000000000..d13cc6c740 --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/is_comparable.hpp @@ -0,0 +1,45 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHS_DETAIL_DISTANCE_IS_COMPARABLE_HPP +#define BOOST_GEOMETRY_ALGORITHS_DETAIL_DISTANCE_IS_COMPARABLE_HPP + +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +// metafunction to determine is a strategy is comparable or not +template +struct is_comparable + : boost::is_same + < + Strategy, + typename strategy::distance::services::comparable_type + < + Strategy + >::type + > +{}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHS_DETAIL_DISTANCE_IS_COMPARABLE_HPP diff --git a/boost/geometry/algorithms/detail/distance/iterator_selector.hpp b/boost/geometry/algorithms/detail/distance/iterator_selector.hpp new file mode 100644 index 0000000000..363ec465a4 --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/iterator_selector.hpp @@ -0,0 +1,70 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHS_DETAIL_DISTANCE_ITERATOR_SELECTOR_HPP +#define BOOST_GEOMETRY_ALGORITHS_DETAIL_DISTANCE_ITERATOR_SELECTOR_HPP + +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +// class to choose between point_iterator and segment_iterator +template ::type> +struct iterator_selector +{ + typedef geometry::segment_iterator iterator_type; + + static inline iterator_type begin(Geometry& geometry) + { + return segments_begin(geometry); + } + + static inline iterator_type end(Geometry& geometry) + { + return segments_end(geometry); + } +}; + +template +struct iterator_selector +{ + typedef geometry::point_iterator iterator_type; + + static inline iterator_type begin(MultiPoint& multipoint) + { + return points_begin(multipoint); + } + + static inline iterator_type end(MultiPoint& multipoint) + { + return points_end(multipoint); + } +}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHS_DETAIL_DISTANCE_ITERATOR_SELECTOR_HPP diff --git a/boost/geometry/algorithms/detail/distance/linear_or_areal_to_areal.hpp b/boost/geometry/algorithms/detail/distance/linear_or_areal_to_areal.hpp new file mode 100644 index 0000000000..b63104da7d --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/linear_or_areal_to_areal.hpp @@ -0,0 +1,147 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_LINEAR_OR_AREAL_TO_AREAL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_LINEAR_OR_AREAL_TO_AREAL_HPP + +#include + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +template +struct linear_to_areal +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(Linear const& linear, + Areal const& areal, + Strategy const& strategy) + { + if ( geometry::intersects(linear, areal) ) + { + return 0; + } + + return linear_to_linear + < + Linear, Areal, Strategy + >::apply(linear, areal, strategy, false); + } + + + static inline return_type apply(Areal const& areal, + Linear const& linear, + Strategy const& strategy) + { + return apply(linear, areal, strategy); + } +}; + + +template +struct areal_to_areal +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(Areal1 const& areal1, + Areal2 const& areal2, + Strategy const& strategy) + { + if ( geometry::intersects(areal1, areal2) ) + { + return 0; + } + + return linear_to_linear + < + Areal1, Areal2, Strategy + >::apply(areal1, areal2, strategy, false); + } +}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct distance + < + Linear, Areal, Strategy, + linear_tag, areal_tag, + strategy_tag_distance_point_segment, false + > + : detail::distance::linear_to_areal + < + Linear, Areal, Strategy + > +{}; + + +template +struct distance + < + Areal, Linear, Strategy, + areal_tag, linear_tag, + strategy_tag_distance_point_segment, false + > + : detail::distance::linear_to_areal + < + Linear, Areal, Strategy + > +{}; + + +template +struct distance + < + Areal1, Areal2, Strategy, + areal_tag, areal_tag, + strategy_tag_distance_point_segment, false + > + : detail::distance::areal_to_areal + < + Areal1, Areal2, Strategy + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_LINEAR_OR_AREAL_TO_AREAL_HPP diff --git a/boost/geometry/algorithms/detail/distance/linear_to_linear.hpp b/boost/geometry/algorithms/detail/distance/linear_to_linear.hpp new file mode 100644 index 0000000000..e44b372842 --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/linear_to_linear.hpp @@ -0,0 +1,123 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_LINEAR_TO_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_LINEAR_TO_LINEAR_HPP + +#include + +#include + +#include +#include + +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +template +struct linear_to_linear +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(Linear1 const& linear1, + Linear2 const& linear2, + Strategy const& strategy, + bool = false) + { + if (geometry::num_points(linear1) == 1) + { + return dispatch::distance + < + typename point_type::type, + Linear2, + Strategy + >::apply(*points_begin(linear1), linear2, strategy); + } + + if (geometry::num_points(linear2) == 1) + { + return dispatch::distance + < + typename point_type::type, + Linear1, + Strategy + >::apply(*points_begin(linear2), linear1, strategy); + } + + if (geometry::num_segments(linear2) < geometry::num_segments(linear1)) + { + return point_or_segment_range_to_geometry_rtree + < + geometry::segment_iterator, + Linear1, + Strategy + >::apply(geometry::segments_begin(linear2), + geometry::segments_end(linear2), + linear1, + strategy); + + } + + return point_or_segment_range_to_geometry_rtree + < + geometry::segment_iterator, + Linear2, + Strategy + >::apply(geometry::segments_begin(linear1), + geometry::segments_end(linear1), + linear2, + strategy); + } +}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct distance + < + Linear1, Linear2, Strategy, + linear_tag, linear_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::linear_to_linear + < + Linear1, Linear2, Strategy + > +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_LINEAR_TO_LINEAR_HPP diff --git a/boost/geometry/algorithms/detail/distance/multipoint_to_geometry.hpp b/boost/geometry/algorithms/detail/distance/multipoint_to_geometry.hpp new file mode 100644 index 0000000000..5f2c6e34fb --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/multipoint_to_geometry.hpp @@ -0,0 +1,240 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_MULTIPOINT_TO_GEOMETRY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_MULTIPOINT_TO_GEOMETRY_HPP + +#include + +#include +#include + +#include +#include + +#include + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +template +struct multipoint_to_multipoint +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(MultiPoint1 const& multipoint1, + MultiPoint2 const& multipoint2, + Strategy const& strategy) + { + if (boost::size(multipoint2) < boost::size(multipoint1)) + + { + return point_or_segment_range_to_geometry_rtree + < + typename boost::range_iterator::type, + MultiPoint1, + Strategy + >::apply(boost::begin(multipoint2), + boost::end(multipoint2), + multipoint1, + strategy); + } + + return point_or_segment_range_to_geometry_rtree + < + typename boost::range_iterator::type, + MultiPoint2, + Strategy + >::apply(boost::begin(multipoint1), + boost::end(multipoint1), + multipoint2, + strategy); + } +}; + + +template +struct multipoint_to_linear +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(MultiPoint const& multipoint, + Linear const& linear, + Strategy const& strategy) + { + return detail::distance::point_or_segment_range_to_geometry_rtree + < + typename boost::range_iterator::type, + Linear, + Strategy + >::apply(boost::begin(multipoint), + boost::end(multipoint), + linear, + strategy); + } + + static inline return_type apply(Linear const& linear, + MultiPoint const& multipoint, + Strategy const& strategy) + { + return apply(multipoint, linear, strategy); + } +}; + + +template +class multipoint_to_areal +{ +private: + struct within_areal + { + within_areal(Areal const& areal) + : m_areal(areal) + {} + + template + inline bool apply(Point const& point) const + { + return geometry::within(point, m_areal); + } + + Areal const& m_areal; + }; + +public: + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(MultiPoint const& multipoint, + Areal const& areal, + Strategy const& strategy) + { + within_areal predicate(areal); + + if (check_iterator_range + < + within_areal, false + >::apply(boost::begin(multipoint), + boost::end(multipoint), + predicate)) + { + return 0; + } + + return detail::distance::point_or_segment_range_to_geometry_rtree + < + typename boost::range_iterator::type, + Areal, + Strategy + >::apply(boost::begin(multipoint), + boost::end(multipoint), + areal, + strategy); + } + + static inline return_type apply(Areal const& areal, + MultiPoint const& multipoint, + Strategy const& strategy) + { + return apply(multipoint, areal, strategy); + } +}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct distance + < + MultiPoint1, MultiPoint2, Strategy, + multi_point_tag, multi_point_tag, + strategy_tag_distance_point_point, false + > : detail::distance::multipoint_to_multipoint + < + MultiPoint1, MultiPoint2, Strategy + > +{}; + +template +struct distance + < + MultiPoint, Linear, Strategy, multi_point_tag, linear_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::multipoint_to_linear +{}; + + +template +struct distance + < + Linear, MultiPoint, Strategy, linear_tag, multi_point_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::multipoint_to_linear +{}; + + +template +struct distance + < + MultiPoint, Areal, Strategy, multi_point_tag, areal_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::multipoint_to_areal +{}; + + +template +struct distance + < + Areal, MultiPoint, Strategy, areal_tag, multi_point_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::multipoint_to_areal +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_MULTIPOINT_TO_GEOMETRY_HPP diff --git a/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp b/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp new file mode 100644 index 0000000000..ab5de3d9b2 --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp @@ -0,0 +1,518 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_DISTANCE_POINT_TO_GEOMETRY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_POINT_TO_GEOMETRY_HPP + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +template +struct point_to_point +{ + static inline + typename strategy::distance::services::return_type::type + apply(P1 const& p1, P2 const& p2, Strategy const& strategy) + { + boost::ignore_unused(strategy); + return strategy.apply(p1, p2); + } +}; + + +template +< + typename Point, + typename Range, + closure_selector Closure, + typename Strategy +> +class point_to_range +{ +private: + typedef typename strategy::distance::services::comparable_type + < + Strategy + >::type comparable_strategy; + + typedef detail::closest_feature::point_to_point_range + < + Point, Range, Closure, comparable_strategy + > point_to_point_range; + +public: + typedef typename strategy::distance::services::return_type + < + Strategy, + Point, + typename boost::range_value::type + >::type return_type; + + static inline return_type apply(Point const& point, Range const& range, + Strategy const& strategy) + { + return_type const zero = return_type(0); + + if (boost::size(range) == 0) + { + return zero; + } + + namespace sds = strategy::distance::services; + + typename sds::return_type + < + comparable_strategy, + Point, + typename point_type::type + >::type cd_min; + + std::pair + < + typename boost::range_iterator::type, + typename boost::range_iterator::type + > it_pair + = point_to_point_range::apply(point, + boost::begin(range), + boost::end(range), + sds::get_comparable + < + Strategy + >::apply(strategy), + cd_min); + + return + is_comparable::value + ? + cd_min + : + strategy.apply(point, *it_pair.first, *it_pair.second); + } +}; + + +template +< + typename Point, + typename Ring, + closure_selector Closure, + typename Strategy +> +struct point_to_ring +{ + typedef typename strategy::distance::services::return_type + < + Strategy, Point, typename point_type::type + >::type return_type; + + static inline return_type apply(Point const& point, + Ring const& ring, + Strategy const& strategy) + { + if (geometry::within(point, ring)) + { + return return_type(0); + } + + return point_to_range + < + Point, Ring, closure::value, Strategy + >::apply(point, ring, strategy); + } +}; + + +template +< + typename Point, + typename Polygon, + closure_selector Closure, + typename Strategy +> +class point_to_polygon +{ +public: + typedef typename strategy::distance::services::return_type + < + Strategy, Point, typename point_type::type + >::type return_type; + +private: + typedef point_to_range + < + Point, typename ring_type::type, Closure, Strategy + > per_ring; + + struct distance_to_interior_rings + { + template + static inline return_type apply(Point const& point, + InteriorRingIterator first, + InteriorRingIterator last, + Strategy const& strategy) + { + for (InteriorRingIterator it = first; it != last; ++it) + { + if (geometry::within(point, *it)) + { + // the point is inside a polygon hole, so its distance + // to the polygon its distance to the polygon's + // hole boundary + return per_ring::apply(point, *it, strategy); + } + } + return 0; + } + + template + static inline return_type apply(Point const& point, + InteriorRings const& interior_rings, + Strategy const& strategy) + { + return apply(point, + boost::begin(interior_rings), + boost::end(interior_rings), + strategy); + } + }; + + +public: + static inline return_type apply(Point const& point, + Polygon const& polygon, + Strategy const& strategy) + { + if (!geometry::covered_by(point, exterior_ring(polygon))) + { + // the point is outside the exterior ring, so its distance + // to the polygon is its distance to the polygon's exterior ring + return per_ring::apply(point, exterior_ring(polygon), strategy); + } + + // Check interior rings + return distance_to_interior_rings::apply(point, + interior_rings(polygon), + strategy); + } +}; + + +template +< + typename Point, + typename MultiGeometry, + typename Strategy, + bool CheckCoveredBy = boost::is_same + < + typename tag::type, multi_polygon_tag + >::value +> +class point_to_multigeometry +{ +private: + typedef detail::closest_feature::geometry_to_range geometry_to_range; + +public: + typedef typename strategy::distance::services::return_type + < + Strategy, + Point, + typename point_type::type + >::type return_type; + + static inline return_type apply(Point const& point, + MultiGeometry const& multigeometry, + Strategy const& strategy) + { + typedef iterator_selector selector_type; + + namespace sds = strategy::distance::services; + + typename sds::return_type + < + typename sds::comparable_type::type, + Point, + typename point_type::type + >::type cd; + + typename selector_type::iterator_type it_min + = geometry_to_range::apply(point, + selector_type::begin(multigeometry), + selector_type::end(multigeometry), + sds::get_comparable + < + Strategy + >::apply(strategy), + cd); + + return + is_comparable::value + ? + cd + : + dispatch::distance + < + Point, + typename std::iterator_traits + < + typename selector_type::iterator_type + >::value_type, + Strategy + >::apply(point, *it_min, strategy); + } +}; + + +// this is called only for multipolygons, hence the change in the +// template parameter name MultiGeometry to MultiPolygon +template +struct point_to_multigeometry +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + Point, + typename point_type::type + >::type return_type; + + static inline return_type apply(Point const& point, + MultiPolygon const& multipolygon, + Strategy const& strategy) + { + if (geometry::covered_by(point, multipolygon)) + { + return 0; + } + + return point_to_multigeometry + < + Point, MultiPolygon, Strategy, false + >::apply(point, multipolygon, strategy); + } +}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// Point-point +template +struct distance + < + P1, P2, Strategy, point_tag, point_tag, + strategy_tag_distance_point_point, false + > : detail::distance::point_to_point +{}; + + +// Point-line version 2, where point-segment strategy is specified +template +struct distance + < + Point, Linestring, Strategy, point_tag, linestring_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::point_to_range +{}; + + +// Point-ring , where point-segment strategy is specified +template +struct distance + < + Point, Ring, Strategy, point_tag, ring_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::point_to_ring + < + Point, Ring, closure::value, Strategy + > +{}; + + +// Point-polygon , where point-segment strategy is specified +template +struct distance + < + Point, Polygon, Strategy, point_tag, polygon_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::point_to_polygon + < + Point, Polygon, closure::value, Strategy + > +{}; + + +// Point-segment version 2, with point-segment strategy +template +struct distance + < + Point, Segment, Strategy, point_tag, segment_tag, + strategy_tag_distance_point_segment, false + > +{ + static inline typename strategy::distance::services::return_type + < + Strategy, Point, typename point_type::type + >::type apply(Point const& point, + Segment const& segment, + Strategy const& strategy) + { + typename point_type::type p[2]; + geometry::detail::assign_point_from_index<0>(segment, p[0]); + geometry::detail::assign_point_from_index<1>(segment, p[1]); + + boost::ignore_unused(strategy); + return strategy.apply(point, p[0], p[1]); + } +}; + + +template +struct distance + < + Point, Box, Strategy, point_tag, box_tag, + strategy_tag_distance_point_box, false + > +{ + static inline typename strategy::distance::services::return_type + < + Strategy, Point, typename point_type::type + >::type + apply(Point const& point, Box const& box, Strategy const& strategy) + { + boost::ignore_unused(strategy); + return strategy.apply(point, box); + } +}; + + +template +struct distance + < + Point, MultiPoint, Strategy, point_tag, multi_point_tag, + strategy_tag_distance_point_point, false + > : detail::distance::point_to_multigeometry + < + Point, MultiPoint, Strategy + > +{}; + + +template +struct distance + < + Point, MultiLinestring, Strategy, point_tag, multi_linestring_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::point_to_multigeometry + < + Point, MultiLinestring, Strategy + > +{}; + + +template +struct distance + < + Point, MultiPolygon, Strategy, point_tag, multi_polygon_tag, + strategy_tag_distance_point_segment, false + > : detail::distance::point_to_multigeometry + < + Point, MultiPolygon, Strategy + > +{}; + + +template +struct distance + < + Point, Linear, Strategy, point_tag, linear_tag, + strategy_tag_distance_point_segment, false + > : distance + < + Point, Linear, Strategy, + point_tag, typename tag::type, + strategy_tag_distance_point_segment, false + > +{}; + + +template +struct distance + < + Point, Areal, Strategy, point_tag, areal_tag, + strategy_tag_distance_point_segment, false + > : distance + < + Point, Areal, Strategy, + point_tag, typename tag::type, + strategy_tag_distance_point_segment, false + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_POINT_TO_GEOMETRY_HPP diff --git a/boost/geometry/algorithms/detail/distance/range_to_geometry_rtree.hpp b/boost/geometry/algorithms/detail/distance/range_to_geometry_rtree.hpp new file mode 100644 index 0000000000..78189794a1 --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/range_to_geometry_rtree.hpp @@ -0,0 +1,131 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_RANGE_TO_GEOMETRY_RTREE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_RANGE_TO_GEOMETRY_RTREE_HPP + +#include +#include + +#include + +#include + +#include + +#include + +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +template +< + typename PointOrSegmentIterator, + typename Geometry, + typename Strategy +> +class point_or_segment_range_to_geometry_rtree +{ +private: + typedef typename std::iterator_traits + < + PointOrSegmentIterator + >::value_type point_or_segment_type; + + typedef iterator_selector selector_type; + + typedef detail::closest_feature::range_to_range_rtree range_to_range; + +public: + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type apply(PointOrSegmentIterator first, + PointOrSegmentIterator last, + Geometry const& geometry, + Strategy const& strategy) + { + namespace sds = strategy::distance::services; + + BOOST_ASSERT( first != last ); + + if ( geometry::has_one_element(first, last) ) + { + return dispatch::distance + < + point_or_segment_type, Geometry, Strategy + >::apply(*first, geometry, strategy); + } + + typename sds::return_type + < + typename sds::comparable_type::type, + typename point_type::type, + typename point_type::type + >::type cd_min; + + std::pair + < + point_or_segment_type, + typename selector_type::iterator_type + > closest_features + = range_to_range::apply(first, + last, + selector_type::begin(geometry), + selector_type::end(geometry), + sds::get_comparable + < + Strategy + >::apply(strategy), + cd_min); + + return + is_comparable::value + ? + cd_min + : + dispatch::distance + < + point_or_segment_type, + typename std::iterator_traits + < + typename selector_type::iterator_type + >::value_type, + Strategy + >::apply(closest_features.first, + *closest_features.second, + strategy); + } +}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_RANGE_TO_GEOMETRY_RTREE_HPP diff --git a/boost/geometry/algorithms/detail/distance/segment_to_box.hpp b/boost/geometry/algorithms/detail/distance/segment_to_box.hpp new file mode 100644 index 0000000000..f64a3e9fca --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/segment_to_box.hpp @@ -0,0 +1,886 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_BOX_HPP + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + +template +< + typename Segment, + typename Box, + typename Strategy, + bool UsePointBoxStrategy = false +> +class segment_to_box_2D_generic +{ +private: + typedef typename point_type::type segment_point; + typedef typename point_type::type box_point; + + typedef typename strategy::distance::services::comparable_type + < + Strategy + >::type comparable_strategy; + + typedef detail::closest_feature::point_to_point_range + < + segment_point, + std::vector, + open, + comparable_strategy + > point_to_point_range; + + typedef typename strategy::distance::services::return_type + < + comparable_strategy, segment_point, box_point + >::type comparable_return_type; + +public: + typedef typename strategy::distance::services::return_type + < + Strategy, segment_point, box_point + >::type return_type; + + static inline return_type apply(Segment const& segment, + Box const& box, + Strategy const& strategy, + bool check_intersection = true) + { + if (check_intersection && geometry::intersects(segment, box)) + { + return 0; + } + + comparable_strategy cstrategy = + strategy::distance::services::get_comparable + < + Strategy + >::apply(strategy); + + // get segment points + segment_point p[2]; + detail::assign_point_from_index<0>(segment, p[0]); + detail::assign_point_from_index<1>(segment, p[1]); + + // get box points + std::vector box_points(4); + detail::assign_box_corners_oriented(box, box_points); + + comparable_return_type cd[6]; + for (unsigned int i = 0; i < 4; ++i) + { + cd[i] = cstrategy.apply(box_points[i], p[0], p[1]); + } + + std::pair + < + typename std::vector::const_iterator, + typename std::vector::const_iterator + > bit_min[2]; + + bit_min[0] = point_to_point_range::apply(p[0], + box_points.begin(), + box_points.end(), + cstrategy, + cd[4]); + bit_min[1] = point_to_point_range::apply(p[1], + box_points.begin(), + box_points.end(), + cstrategy, + cd[5]); + + unsigned int imin = 0; + for (unsigned int i = 1; i < 6; ++i) + { + if (cd[i] < cd[imin]) + { + imin = i; + } + } + + if (is_comparable::value) + { + return cd[imin]; + } + + if (imin < 4) + { + return strategy.apply(box_points[imin], p[0], p[1]); + } + else + { + unsigned int bimin = imin - 4; + return strategy.apply(p[bimin], + *bit_min[bimin].first, + *bit_min[bimin].second); + } + } +}; + + +template +< + typename Segment, + typename Box, + typename Strategy +> +class segment_to_box_2D_generic +{ +private: + typedef typename point_type::type segment_point; + typedef typename point_type::type box_point; + + typedef typename strategy::distance::services::comparable_type + < + Strategy + >::type comparable_strategy; + + typedef typename strategy::distance::services::return_type + < + comparable_strategy, segment_point, box_point + >::type comparable_return_type; + + typedef typename detail::distance::default_strategy + < + segment_point, Box + >::type point_box_strategy; + + typedef typename strategy::distance::services::comparable_type + < + point_box_strategy + >::type point_box_comparable_strategy; + +public: + typedef typename strategy::distance::services::return_type + < + Strategy, segment_point, box_point + >::type return_type; + + static inline return_type apply(Segment const& segment, + Box const& box, + Strategy const& strategy, + bool check_intersection = true) + { + if (check_intersection && geometry::intersects(segment, box)) + { + return 0; + } + + comparable_strategy cstrategy = + strategy::distance::services::get_comparable + < + Strategy + >::apply(strategy); + boost::ignore_unused(cstrategy); + + // get segment points + segment_point p[2]; + detail::assign_point_from_index<0>(segment, p[0]); + detail::assign_point_from_index<1>(segment, p[1]); + + // get box points + std::vector box_points(4); + detail::assign_box_corners_oriented(box, box_points); + + comparable_return_type cd[6]; + for (unsigned int i = 0; i < 4; ++i) + { + cd[i] = cstrategy.apply(box_points[i], p[0], p[1]); + } + + point_box_comparable_strategy pb_cstrategy; + boost::ignore_unused(pb_cstrategy); + cd[4] = pb_cstrategy.apply(p[0], box); + cd[5] = pb_cstrategy.apply(p[1], box); + + unsigned int imin = 0; + for (unsigned int i = 1; i < 6; ++i) + { + if (cd[i] < cd[imin]) + { + imin = i; + } + } + + if (is_comparable::value) + { + return cd[imin]; + } + + if (imin < 4) + { + strategy.apply(box_points[imin], p[0], p[1]); + } + else + { + return point_box_strategy().apply(p[imin - 4], box); + } + } +}; + + + + +template +< + typename ReturnType, + typename SegmentPoint, + typename BoxPoint, + typename PPStrategy, + typename PSStrategy +> +class segment_to_box_2D +{ +private: + template + struct cast_to_result + { + template + static inline Result apply(T const& t) + { + return boost::numeric_cast(t); + } + }; + + + template + struct compare_less_equal + { + typedef compare_less_equal other; + + template + inline bool operator()(T1 const& t1, T2 const& t2) const + { + return std::less_equal()(cast_to_result::apply(t1), + cast_to_result::apply(t2)); + } + }; + + template + struct compare_less_equal + { + typedef compare_less_equal other; + + template + inline bool operator()(T1 const& t1, T2 const& t2) const + { + return std::greater_equal()(cast_to_result::apply(t1), + cast_to_result::apply(t2)); + } + }; + + + template + struct other_compare + { + typedef typename LessEqual::other type; + }; + + + // it is assumed here that p0 lies to the right of the box (so the + // entire segment lies to the right of the box) + template + struct right_of_box + { + static inline ReturnType apply(SegmentPoint const& p0, + SegmentPoint const& p1, + BoxPoint const& bottom_right, + BoxPoint const& top_right, + PPStrategy const& pp_strategy, + PSStrategy const& ps_strategy) + { + boost::ignore_unused(pp_strategy, ps_strategy); + + // the implementation below is written for non-negative slope + // segments + // + // for negative slope segments swap the roles of bottom_right + // and top_right and use greater_equal instead of less_equal. + + typedef cast_to_result cast; + + LessEqual less_equal; + + if (less_equal(geometry::get<1>(top_right), geometry::get<1>(p0))) + { + // closest box point is the top-right corner + return cast::apply(pp_strategy.apply(p0, top_right)); + } + else if (less_equal(geometry::get<1>(bottom_right), + geometry::get<1>(p0))) + { + // distance is realized between p0 and right-most + // segment of box + ReturnType diff = cast::apply(geometry::get<0>(p0)) + - cast::apply(geometry::get<0>(bottom_right)); + return strategy::distance::services::result_from_distance + < + PSStrategy, BoxPoint, SegmentPoint + >::apply(ps_strategy, math::abs(diff)); + } + else + { + // distance is realized between the bottom-right + // corner of the box and the segment + return cast::apply(ps_strategy.apply(bottom_right, p0, p1)); + } + } + }; + + + // it is assumed here that p0 lies above the box (so the + // entire segment lies above the box) + template + struct above_of_box + { + static inline ReturnType apply(SegmentPoint const& p0, + SegmentPoint const& p1, + BoxPoint const& top_left, + PSStrategy const& ps_strategy) + { + boost::ignore_unused(ps_strategy); + + // the segment lies above the box + + typedef cast_to_result cast; + + LessEqual less_equal; + + // p0 is above the upper segment of the box + // (and inside its band) + if (less_equal(geometry::get<0>(top_left), geometry::get<0>(p0))) + { + ReturnType diff = cast::apply(geometry::get<1>(p0)) + - cast::apply(geometry::get<1>(top_left)); + return strategy::distance::services::result_from_distance + < + PSStrategy, SegmentPoint, BoxPoint + >::apply(ps_strategy, math::abs(diff)); + } + + // p0 is to the left of the box, but p1 is above the box + // in this case the distance is realized between the + // top-left corner of the box and the segment + return cast::apply(ps_strategy.apply(top_left, p0, p1)); + } + }; + + + template + struct check_right_left_of_box + { + static inline bool apply(SegmentPoint const& p0, + SegmentPoint const& p1, + BoxPoint const& top_left, + BoxPoint const& top_right, + BoxPoint const& bottom_left, + BoxPoint const& bottom_right, + PPStrategy const& pp_strategy, + PSStrategy const& ps_strategy, + ReturnType& result) + { + // p0 lies to the right of the box + if (geometry::get<0>(p0) >= geometry::get<0>(top_right)) + { + result = right_of_box + < + LessEqual + >::apply(p0, p1, bottom_right, top_right, + pp_strategy, ps_strategy); + return true; + } + + // p1 lies to the left of the box + if (geometry::get<0>(p1) <= geometry::get<0>(bottom_left)) + { + result = right_of_box + < + typename other_compare::type + >::apply(p1, p0, top_left, bottom_left, + pp_strategy, ps_strategy); + return true; + } + + return false; + } + }; + + + template + struct check_above_below_of_box + { + static inline bool apply(SegmentPoint const& p0, + SegmentPoint const& p1, + BoxPoint const& top_left, + BoxPoint const& top_right, + BoxPoint const& bottom_left, + BoxPoint const& bottom_right, + PSStrategy const& ps_strategy, + ReturnType& result) + { + // the segment lies below the box + if (geometry::get<1>(p1) < geometry::get<1>(bottom_left)) + { + result = above_of_box + < + typename other_compare::type + >::apply(p1, p0, bottom_right, ps_strategy); + return true; + } + + // the segment lies above the box + if (geometry::get<1>(p0) > geometry::get<1>(top_right)) + { + result = above_of_box + < + LessEqual + >::apply(p0, p1, top_left, ps_strategy); + return true; + } + return false; + } + }; + + struct check_generic_position + { + static inline bool apply(SegmentPoint const& p0, + SegmentPoint const& p1, + BoxPoint const& bottom_left0, + BoxPoint const& top_right0, + BoxPoint const& bottom_left1, + BoxPoint const& top_right1, + BoxPoint const& corner1, + BoxPoint const& corner2, + PSStrategy const& ps_strategy, + ReturnType& result) + { + typedef cast_to_result cast; + + ReturnType diff0 = cast::apply(geometry::get<0>(p1)) + - cast::apply(geometry::get<0>(p0)); + ReturnType t_min0 = cast::apply(geometry::get<0>(bottom_left0)) + - cast::apply(geometry::get<0>(p0)); + ReturnType t_max0 = cast::apply(geometry::get<0>(top_right0)) + - cast::apply(geometry::get<0>(p0)); + + ReturnType diff1 = cast::apply(geometry::get<1>(p1)) + - cast::apply(geometry::get<1>(p0)); + ReturnType t_min1 = cast::apply(geometry::get<1>(bottom_left1)) + - cast::apply(geometry::get<1>(p0)); + ReturnType t_max1 = cast::apply(geometry::get<1>(top_right1)) + - cast::apply(geometry::get<1>(p0)); + + if (diff1 < 0) + { + diff1 = -diff1; + t_min1 = -t_min1; + t_max1 = -t_max1; + } + + // t_min0 > t_max1 + if (t_min0 * diff1 > t_max1 * diff0) + { + result = cast::apply(ps_strategy.apply(corner1, p0, p1)); + return true; + } + + // t_max0 < t_min1 + if (t_max0 * diff1 < t_min1 * diff0) + { + result = cast::apply(ps_strategy.apply(corner2, p0, p1)); + return true; + } + return false; + } + }; + + static inline ReturnType + non_negative_slope_segment(SegmentPoint const& p0, + SegmentPoint const& p1, + BoxPoint const& top_left, + BoxPoint const& top_right, + BoxPoint const& bottom_left, + BoxPoint const& bottom_right, + PPStrategy const& pp_strategy, + PSStrategy const& ps_strategy) + { + typedef compare_less_equal less_equal; + + // assert that the segment has non-negative slope + BOOST_ASSERT( ( math::equals(geometry::get<0>(p0), geometry::get<0>(p1)) + && geometry::get<1>(p0) < geometry::get<1>(p1)) + || + ( geometry::get<0>(p0) < geometry::get<0>(p1) + && geometry::get<1>(p0) <= geometry::get<1>(p1) ) + ); + + ReturnType result(0); + + if (check_right_left_of_box + < + less_equal + >::apply(p0, p1, + top_left, top_right, bottom_left, bottom_right, + pp_strategy, ps_strategy, result)) + { + return result; + } + + if (check_above_below_of_box + < + less_equal + >::apply(p0, p1, + top_left, top_right, bottom_left, bottom_right, + ps_strategy, result)) + { + return result; + } + + if (check_generic_position::apply(p0, p1, + bottom_left, top_right, + bottom_left, top_right, + top_left, bottom_right, + ps_strategy, result)) + { + return result; + } + + // in all other cases the box and segment intersect, so return 0 + return result; + } + + + static inline ReturnType + negative_slope_segment(SegmentPoint const& p0, + SegmentPoint const& p1, + BoxPoint const& top_left, + BoxPoint const& top_right, + BoxPoint const& bottom_left, + BoxPoint const& bottom_right, + PPStrategy const& pp_strategy, + PSStrategy const& ps_strategy) + { + typedef compare_less_equal greater_equal; + + // assert that the segment has negative slope + BOOST_ASSERT( geometry::get<0>(p0) < geometry::get<0>(p1) + && geometry::get<1>(p0) > geometry::get<1>(p1) ); + + ReturnType result(0); + + if (check_right_left_of_box + < + greater_equal + >::apply(p0, p1, + bottom_left, bottom_right, top_left, top_right, + pp_strategy, ps_strategy, result)) + { + return result; + } + + if (check_above_below_of_box + < + greater_equal + >::apply(p1, p0, + top_right, top_left, bottom_right, bottom_left, + ps_strategy, result)) + { + return result; + } + + if (check_generic_position::apply(p0, p1, + bottom_left, top_right, + top_right, bottom_left, + bottom_left, top_right, + ps_strategy, result)) + { + return result; + } + + // in all other cases the box and segment intersect, so return 0 + return result; + } + +public: + static inline ReturnType apply(SegmentPoint const& p0, + SegmentPoint const& p1, + BoxPoint const& top_left, + BoxPoint const& top_right, + BoxPoint const& bottom_left, + BoxPoint const& bottom_right, + PPStrategy const& pp_strategy, + PSStrategy const& ps_strategy) + { + BOOST_ASSERT( geometry::less()(p0, p1) ); + + if (geometry::get<0>(p0) < geometry::get<0>(p1) + && geometry::get<1>(p0) > geometry::get<1>(p1)) + { + return negative_slope_segment(p0, p1, + top_left, top_right, + bottom_left, bottom_right, + pp_strategy, ps_strategy); + } + + return non_negative_slope_segment(p0, p1, + top_left, top_right, + bottom_left, bottom_right, + pp_strategy, ps_strategy); + } +}; + + +//========================================================================= + + +template +< + typename Segment, + typename Box, + typename std::size_t Dimension, + typename PPStrategy, + typename PSStrategy +> +class segment_to_box + : not_implemented +{}; + + +template +< + typename Segment, + typename Box, + typename PPStrategy, + typename PSStrategy +> +class segment_to_box +{ +private: + typedef typename point_type::type segment_point; + typedef typename point_type::type box_point; + + typedef typename strategy::distance::services::comparable_type + < + PPStrategy + >::type pp_comparable_strategy; + + typedef typename strategy::distance::services::comparable_type + < + PSStrategy + >::type ps_comparable_strategy; + + typedef typename strategy::distance::services::return_type + < + ps_comparable_strategy, segment_point, box_point + >::type comparable_return_type; +public: + typedef typename strategy::distance::services::return_type + < + PSStrategy, segment_point, box_point + >::type return_type; + + static inline return_type apply(Segment const& segment, + Box const& box, + PPStrategy const& pp_strategy, + PSStrategy const& ps_strategy) + { + segment_point p[2]; + detail::assign_point_from_index<0>(segment, p[0]); + detail::assign_point_from_index<1>(segment, p[1]); + + if (geometry::equals(p[0], p[1])) + { + typedef typename boost::mpl::if_ + < + boost::is_same + < + ps_comparable_strategy, + PSStrategy + >, + typename strategy::distance::services::comparable_type + < + typename detail::distance::default_strategy + < + segment_point, Box + >::type + >::type, + typename detail::distance::default_strategy + < + segment_point, Box + >::type + >::type point_box_strategy_type; + + return dispatch::distance + < + segment_point, + Box, + point_box_strategy_type + >::apply(p[0], box, point_box_strategy_type()); + } + + box_point top_left, top_right, bottom_left, bottom_right; + detail::assign_box_corners(box, bottom_left, bottom_right, + top_left, top_right); + + if (geometry::less()(p[0], p[1])) + { + return segment_to_box_2D + < + return_type, + segment_point, + box_point, + PPStrategy, + PSStrategy + >::apply(p[0], p[1], + top_left, top_right, bottom_left, bottom_right, + pp_strategy, + ps_strategy); + } + else + { + return segment_to_box_2D + < + return_type, + segment_point, + box_point, + PPStrategy, + PSStrategy + >::apply(p[1], p[0], + top_left, top_right, bottom_left, bottom_right, + pp_strategy, + ps_strategy); + } + } +}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct distance + < + Segment, Box, Strategy, segment_tag, box_tag, + strategy_tag_distance_point_segment, false + > +{ + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + + static inline return_type apply(Segment const& segment, + Box const& box, + Strategy const& strategy) + { + assert_dimension_equal(); + + typedef typename boost::mpl::if_ + < + boost::is_same + < + typename strategy::distance::services::comparable_type + < + Strategy + >::type, + Strategy + >, + typename strategy::distance::services::comparable_type + < + typename detail::distance::default_strategy + < + typename point_type::type, + typename point_type::type + >::type + >::type, + typename detail::distance::default_strategy + < + typename point_type::type, + typename point_type::type + >::type + >::type pp_strategy_type; + + + return detail::distance::segment_to_box + < + Segment, + Box, + dimension::value, + pp_strategy_type, + Strategy + >::apply(segment, box, pp_strategy_type(), strategy); + } +}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_BOX_HPP diff --git a/boost/geometry/algorithms/detail/distance/segment_to_segment.hpp b/boost/geometry/algorithms/detail/distance/segment_to_segment.hpp new file mode 100644 index 0000000000..2dcde64946 --- /dev/null +++ b/boost/geometry/algorithms/detail/distance/segment_to_segment.hpp @@ -0,0 +1,150 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_SEGMENT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_SEGMENT_HPP + +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + + + +// compute segment-segment distance +template +class segment_to_segment +{ +private: + typedef typename strategy::distance::services::comparable_type + < + Strategy + >::type comparable_strategy; + + typedef typename strategy::distance::services::return_type + < + comparable_strategy, + typename point_type::type, + typename point_type::type + >::type comparable_return_type; + +public: + typedef typename strategy::distance::services::return_type + < + Strategy, + typename point_type::type, + typename point_type::type + >::type return_type; + + static inline return_type + apply(Segment1 const& segment1, Segment2 const& segment2, + Strategy const& strategy) + { + if (geometry::intersects(segment1, segment2)) + { + return 0; + } + + typename point_type::type p[2]; + detail::assign_point_from_index<0>(segment1, p[0]); + detail::assign_point_from_index<1>(segment1, p[1]); + + typename point_type::type q[2]; + detail::assign_point_from_index<0>(segment2, q[0]); + detail::assign_point_from_index<1>(segment2, q[1]); + + comparable_strategy cstrategy = + strategy::distance::services::get_comparable + < + Strategy + >::apply(strategy); + + comparable_return_type d[4]; + d[0] = cstrategy.apply(q[0], p[0], p[1]); + d[1] = cstrategy.apply(q[1], p[0], p[1]); + d[2] = cstrategy.apply(p[0], q[0], q[1]); + d[3] = cstrategy.apply(p[1], q[0], q[1]); + + std::size_t imin = std::distance(boost::addressof(d[0]), + std::min_element(d, d + 4)); + + if (is_comparable::value) + { + return d[imin]; + } + + switch (imin) + { + case 0: + return strategy.apply(q[0], p[0], p[1]); + case 1: + return strategy.apply(q[1], p[0], p[1]); + case 2: + return strategy.apply(p[0], q[0], q[1]); + default: + return strategy.apply(p[1], q[0], q[1]); + } + } +}; + + + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + + +// segment-segment +template +struct distance + < + Segment1, Segment2, Strategy, segment_tag, segment_tag, + strategy_tag_distance_point_segment, false + > + : detail::distance::segment_to_segment +{}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_SEGMENT_HPP diff --git a/boost/geometry/algorithms/detail/equals/collect_vectors.hpp b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp index 9c2fe28057..5bcb5ffaa0 100644 --- a/boost/geometry/algorithms/detail/equals/collect_vectors.hpp +++ b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp @@ -1,8 +1,9 @@ // 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. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -17,12 +18,13 @@ #include #include -#include -#include +#include #include #include +#include + #include #include @@ -75,9 +77,9 @@ struct collected_vector inline bool same_direction(collected_vector const& other) const { - // For high precision arithmetic, we have to be + // For high precision arithmetic, we have to be // more relaxed then using == - // Because 2/sqrt( (0,0)<->(2,2) ) == 1/sqrt( (0,0)<->(1,1) ) + // Because 2/sqrt( (0,0)<->(2,2) ) == 1/sqrt( (0,0)<->(1,1) ) // is not always true (at least, it is not for ttmath) return math::equals_with_epsilon(dx, other.dx) && math::equals_with_epsilon(dy, other.dy); @@ -111,6 +113,9 @@ struct range_collect_vectors return; } + typedef typename boost::range_size::type collection_size_t; + collection_size_t c_old_size = boost::size(collection); + typedef typename boost::range_iterator::type iterator; bool first = true; @@ -131,7 +136,7 @@ struct range_collect_vectors // Normalize the vector -> this results in points+direction // and is comparible between geometries - calculation_type magnitude = sqrt( + calculation_type magnitude = math::sqrt( boost::numeric_cast(v.dx * v.dx + v.dy * v.dy)); // Avoid non-duplicate points (AND division by zero) @@ -150,10 +155,19 @@ struct range_collect_vectors } // If first one has same direction as last one, remove first one - if (boost::size(collection) > 1 - && collection.front().same_direction(collection.back())) + collection_size_t collected_count = boost::size(collection) - c_old_size; + if ( collected_count > 1 ) { - collection.erase(collection.begin()); + typedef typename boost::range_iterator::type c_iterator; + c_iterator first = collection.begin() + c_old_size; + + if ( first->same_direction(collection.back()) ) + { + //collection.erase(first); + // O(1) instead of O(N) + *first = collection.back(); + collection.pop_back(); + } } } }; @@ -194,9 +208,10 @@ struct polygon_collect_vectors typedef range_collect_vectors per_range; per_range::apply(collection, exterior_ring(polygon)); - typename interior_return_type::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + typename interior_return_type::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) { per_range::apply(collection, *it); } diff --git a/boost/geometry/algorithms/detail/equals/point_point.hpp b/boost/geometry/algorithms/detail/equals/point_point.hpp new file mode 100644 index 0000000000..12daa85e9d --- /dev/null +++ b/boost/geometry/algorithms/detail/equals/point_point.hpp @@ -0,0 +1,52 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2014 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2014 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland + +// This file was modified by Oracle on 2013-2014. +// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// 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_DETAIL_EQUALS_POINT_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_POINT_POINT_HPP + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace equals +{ + +/*! + \brief Internal utility function to detect of points are disjoint + \note To avoid circular references + */ +template +inline bool equals_point_point(Point1 const& point1, Point2 const& point2) +{ + return ! detail::disjoint::disjoint_point_point(point1, point2); +} + + +}} // namespace detail::equals +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_POINT_POINT_HPP diff --git a/boost/geometry/algorithms/detail/extreme_points.hpp b/boost/geometry/algorithms/detail/extreme_points.hpp new file mode 100644 index 0000000000..61839d296a --- /dev/null +++ b/boost/geometry/algorithms/detail/extreme_points.hpp @@ -0,0 +1,520 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2013 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2013 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2013 Mateusz Loskot, London, UK. +// Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXTREME_POINTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXTREME_POINTS_HPP + + +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace extreme_points +{ + +template +struct compare +{ + template + inline bool operator()(Point const& lhs, Point const& rhs) + { + return geometry::get(lhs) < geometry::get(rhs); + } +}; + + +template +inline void move_along_vector(PointType& point, PointType const& extreme, CoordinateType const& base_value) +{ + // Moves a point along the vector (point, extreme) in the direction of the extreme point + // This adapts the possibly uneven legs of the triangle (or trapezium-like shape) + // _____extreme _____ + // / \ / \ . + // /base \ => / \ point . + // \ point . + // + // For so-called intruders, it can be used to adapt both legs to the level of "base" + // For the base, it can be used to adapt both legs to the level of the max-value of the intruders + // If there are 2 or more extreme values, use the one close to 'point' to have a correct vector + + CoordinateType const value = geometry::get(point); + //if (geometry::math::equals(value, base_value)) + if (value >= base_value) + { + return; + } + + PointType vector = point; + subtract_point(vector, extreme); + + CoordinateType const diff = geometry::get(vector); + + // diff should never be zero + // because of the way our triangle/trapezium is build. + // We just return if it would be the case. + if (geometry::math::equals(diff, 0)) + { + return; + } + + CoordinateType const base_diff = base_value - geometry::get(extreme); + + multiply_value(vector, base_diff); + divide_value(vector, diff); + + // The real move: + point = extreme; + add_point(point, vector); +} + + +template +inline void move_along_vector(Range& range, CoordinateType const& base_value) +{ + if (range.size() >= 3) + { + move_along_vector(range.front(), *(range.begin() + 1), base_value); + move_along_vector(range.back(), *(range.rbegin() + 1), base_value); + } +} + + +template +struct extreme_points_on_ring +{ + + typedef typename geometry::coordinate_type::type coordinate_type; + typedef typename boost::range_iterator::type range_iterator; + typedef typename geometry::point_type::type point_type; + + typedef typename geometry::strategy::side::services::default_strategy + < + typename geometry::cs_tag::type + >::type side_strategy; + + + template + static inline bool extend(CirclingIterator& it, + std::size_t n, + coordinate_type max_coordinate_value, + Points& points, int direction) + { + std::size_t safe_index = 0; + do + { + it += direction; + + points.push_back(*it); + + if (safe_index++ >= n) + { + // E.g.: ring is completely horizontal or vertical (= invalid, but we don't want to have an infinite loop) + return false; + } + } while (geometry::math::equals(geometry::get(*it), max_coordinate_value)); + + return true; + } + + // Overload without adding to poinst + template + static inline bool extend(CirclingIterator& it, + std::size_t n, + coordinate_type max_coordinate_value, + int direction) + { + std::size_t safe_index = 0; + do + { + it += direction; + + if (safe_index++ >= n) + { + // E.g.: ring is completely horizontal or vertical (= invalid, but we don't want to have an infinite loop) + return false; + } + } while (geometry::math::equals(geometry::get(*it), max_coordinate_value)); + + return true; + } + + template + static inline bool extent_both_sides(Ring const& ring, + point_type extreme, + CirclingIterator& left, + CirclingIterator& right) + { + std::size_t const n = boost::size(ring); + coordinate_type const max_coordinate_value = geometry::get(extreme); + + if (! extend(left, n, max_coordinate_value, -1)) + { + return false; + } + if (! extend(right, n, max_coordinate_value, +1)) + { + return false; + } + + return true; + } + + template + static inline bool collect(Ring const& ring, + point_type extreme, + Collection& points, + CirclingIterator& left, + CirclingIterator& right) + { + std::size_t const n = boost::size(ring); + coordinate_type const max_coordinate_value = geometry::get(extreme); + + // Collects first left, which is reversed (if more than one point) then adds the top itself, then right + if (! extend(left, n, max_coordinate_value, points, -1)) + { + return false; + } + std::reverse(points.begin(), points.end()); + points.push_back(extreme); + if (! extend(right, n, max_coordinate_value, points, +1)) + { + return false; + } + + return true; + } + + template + static inline void get_intruders(Ring const& ring, CirclingIterator left, CirclingIterator right, + Extremes const& extremes, + Intruders& intruders) + { + if (boost::size(extremes) < 3) + { + return; + } + coordinate_type const min_value = geometry::get(*std::min_element(boost::begin(extremes), boost::end(extremes), compare())); + + // Also select left/right (if Dimension=1) + coordinate_type const other_min = geometry::get<1 - Dimension>(*std::min_element(boost::begin(extremes), boost::end(extremes), compare<1 - Dimension>())); + coordinate_type const other_max = geometry::get<1 - Dimension>(*std::max_element(boost::begin(extremes), boost::end(extremes), compare<1 - Dimension>())); + + std::size_t defensive_check_index = 0; // in case we skip over left/right check, collect modifies right too + std::size_t const n = boost::size(ring); + while (left != right && defensive_check_index < n) + { + coordinate_type const coordinate = geometry::get(*right); + coordinate_type const other_coordinate = geometry::get<1 - Dimension>(*right); + if (coordinate > min_value && other_coordinate > other_min && other_coordinate < other_max) + { + int const factor = geometry::point_order::value == geometry::clockwise ? 1 : -1; + int const first_side = side_strategy::apply(*right, extremes.front(), *(extremes.begin() + 1)) * factor; + int const last_side = side_strategy::apply(*right, *(extremes.rbegin() + 1), extremes.back()) * factor; + + // If not lying left from any of the extemes side + if (first_side != 1 && last_side != 1) + { + //std::cout << "first " << first_side << " last " << last_side << std::endl; + + // we start at this intrusion until it is handled, and don't affect our initial left iterator + CirclingIterator left_intrusion_it = right; + typename boost::range_value::type intruder; + collect(ring, *right, intruder, left_intrusion_it, right); + + // Also moves these to base-level, makes sorting possible which can be done in case of self-tangencies + // (we might postpone this action, it is often not necessary. However it is not time-consuming) + move_along_vector(intruder, min_value); + intruders.push_back(intruder); + --right; + } + } + ++right; + defensive_check_index++; + } + } + + template + static inline void get_intruders(Ring const& ring, + Extremes const& extremes, + Intruders& intruders) + { + std::size_t const n = boost::size(ring); + if (n >= 3) + { + geometry::ever_circling_range_iterator left(ring); + geometry::ever_circling_range_iterator right(ring); + ++right; + + get_intruders(ring, left, right, extremes, intruders); + } + } + + template + static inline bool right_turn(Ring const& ring, Iterator it) + { + typename std::iterator_traits::difference_type const index + = std::distance(boost::begin(ring), it); + geometry::ever_circling_range_iterator left(ring); + geometry::ever_circling_range_iterator right(ring); + left += index; + right += index; + + if (! extent_both_sides(ring, *it, left, right)) + { + return false; + } + + int const factor = geometry::point_order::value == geometry::clockwise ? 1 : -1; + int const first_side = side_strategy::apply(*(right - 1), *right, *left) * factor; + int const last_side = side_strategy::apply(*left, *(left + 1), *right) * factor; + +//std::cout << "Candidate at " << geometry::wkt(*it) << " first=" << first_side << " last=" << last_side << std::endl; + + // Turn should not be left (actually, it should be right because extent removes horizontal/collinear cases) + return first_side != 1 && last_side != 1; + } + + + // Gets the extreme segments (top point plus neighbouring points), plus intruders, if any, on the same ring + template + static inline bool apply(Ring const& ring, Extremes& extremes, Intruders& intruders) + { + std::size_t const n = boost::size(ring); + if (n < 3) + { + return false; + } + + // Get all maxima, usually one. In case of self-tangencies, or self-crossings, + // the max might be is not valid. A valid max should make a right turn + range_iterator max_it = boost::begin(ring); + compare smaller; + for (range_iterator it = max_it + 1; it != boost::end(ring); ++it) + { + if (smaller(*max_it, *it) && right_turn(ring, it)) + { + max_it = it; + } + } + + if (max_it == boost::end(ring)) + { + return false; + } + + typename std::iterator_traits::difference_type const + index = std::distance(boost::begin(ring), max_it); +//std::cout << "Extreme point lies at " << index << " having " << geometry::wkt(*max_it) << std::endl; + + geometry::ever_circling_range_iterator left(ring); + geometry::ever_circling_range_iterator right(ring); + left += index; + right += index; + + // Collect all points (often 3) in a temporary vector + std::vector points; + points.reserve(3); + if (! collect(ring, *max_it, points, left, right)) + { + return false; + } + +//std::cout << "Built vector of " << points.size() << std::endl; + + coordinate_type const front_value = geometry::get(points.front()); + coordinate_type const back_value = geometry::get(points.back()); + coordinate_type const base_value = (std::max)(front_value, back_value); + if (front_value < back_value) + { + move_along_vector(points.front(), *(points.begin() + 1), base_value); + } + else + { + move_along_vector(points.back(), *(points.rbegin() + 1), base_value); + } + + std::copy(points.begin(), points.end(), std::back_inserter(extremes)); + + get_intruders(ring, left, right, extremes, intruders); + + return true; + } +}; + + + + + +}} // namespace detail::extreme_points +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Geometry, + std::size_t Dimension, + typename GeometryTag = typename tag::type +> +struct extreme_points +{}; + + +template +struct extreme_points + : detail::extreme_points::extreme_points_on_ring +{}; + + +template +struct extreme_points +{ + template + static inline bool apply(Polygon const& polygon, Extremes& extremes, Intruders& intruders) + { + typedef typename geometry::ring_type::type ring_type; + typedef detail::extreme_points::extreme_points_on_ring + < + ring_type, Dimension + > ring_implementation; + + if (! ring_implementation::apply(geometry::exterior_ring(polygon), extremes, intruders)) + { + return false; + } + + // For a polygon, its interior rings can contain intruders + typename interior_return_type::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) + { + ring_implementation::get_intruders(*it, extremes, intruders); + } + + return true; + } +}; + +template +struct extreme_points +{ + template + static inline bool apply(Box const& box, Extremes& extremes, Intruders& ) + { + extremes.resize(4); + geometry::detail::assign_box_corners_oriented(box, extremes); + // ll,ul,ur,lr, contains too exactly the right info + return true; + } +}; + +template +struct extreme_points +{ + template + static inline bool apply(Box const& box, Extremes& extremes, Intruders& ) + { + extremes.resize(4); + geometry::detail::assign_box_corners_oriented(box, extremes); + // ll,ul,ur,lr, rotate one to start with UL and end with LL + std::rotate(extremes.begin(), extremes.begin() + 1, extremes.end()); + return true; + } +}; + +template +struct extreme_points +{ + template + static inline bool apply(MultiPolygon const& multi, Extremes& extremes, Intruders& intruders) + { + // Get one for the very first polygon, that is (for the moment) enough. + // It is not guaranteed the "extreme" then, but for the current purpose + // (point_on_surface) it can just be this point. + if (boost::size(multi) >= 1) + { + return extreme_points + < + typename boost::range_value::type, + Dimension, + polygon_tag + >::apply(*boost::begin(multi), extremes, intruders); + } + + return false; + } +}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Returns extreme points (for Edge=1 in dimension 1, so the top, + for Edge=0 in dimension 0, the right side) +\note We could specify a strategy (less/greater) to get bottom/left side too. However, until now we don't need that. + */ +template +inline bool extreme_points(Geometry const& geometry, Extremes& extremes, Intruders& intruders) +{ + concept::check(); + + // Extremes is not required to follow a geometry concept (but it should support an output iterator), + // but its elements should fulfil the point-concept + concept::check::type>(); + + // Intruders should contain collections which value type is point-concept + // Extremes might be anything (supporting an output iterator), but its elements should fulfil the point-concept + concept::check + < + typename boost::range_value + < + typename boost::range_value::type + >::type + const + >(); + + return dispatch::extreme_points::apply(geometry, extremes, intruders); +} + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXTREME_POINTS_HPP diff --git a/boost/geometry/algorithms/detail/for_each_range.hpp b/boost/geometry/algorithms/detail/for_each_range.hpp index 7cb01fa9b4..e8c92160f1 100644 --- a/boost/geometry/algorithms/detail/for_each_range.hpp +++ b/boost/geometry/algorithms/detail/for_each_range.hpp @@ -17,9 +17,13 @@ #include #include +#include +#include +#include #include #include +#include #include #include @@ -34,24 +38,20 @@ namespace detail { namespace for_each { -template +template struct fe_range_range { - static inline void apply( - typename add_const_if_c::type& range, - Actor& actor) + static inline void apply(Range & range, Actor & actor) { actor.apply(range); } }; -template +template struct fe_range_polygon { - static inline void apply( - typename add_const_if_c::type& polygon, - Actor& actor) + static inline void apply(Polygon & polygon, Actor & actor) { actor.apply(exterior_ring(polygon)); @@ -60,17 +60,27 @@ struct fe_range_polygon } }; -template +template struct fe_range_box { - static inline void apply( - typename add_const_if_c::type& box, - Actor& actor) + static inline void apply(Box & box, Actor & actor) { - actor.apply(box_view(box)); + actor.apply(box_view::type>(box)); } }; +template +struct fe_range_multi +{ + static inline void apply(Multi & multi, Actor & actor) + { + for ( typename boost::range_iterator::type + it = boost::begin(multi); it != boost::end(multi); ++it) + { + SinglePolicy::apply(*it, actor); + } + } +}; }} // namespace detail::for_each #endif // DOXYGEN_NO_DETAIL @@ -83,10 +93,9 @@ namespace dispatch template < - typename Tag, typename Geometry, typename Actor, - bool IsConst + typename Tag = typename tag::type > struct for_each_range { @@ -98,26 +107,71 @@ struct for_each_range }; -template -struct for_each_range - : detail::for_each::fe_range_range +template +struct for_each_range + : detail::for_each::fe_range_range +{}; + + +template +struct for_each_range + : detail::for_each::fe_range_range +{}; + + +template +struct for_each_range + : detail::for_each::fe_range_polygon +{}; + + +template +struct for_each_range + : detail::for_each::fe_range_box {}; -template -struct for_each_range - : detail::for_each::fe_range_range +template +struct for_each_range + : detail::for_each::fe_range_range {}; -template -struct for_each_range - : detail::for_each::fe_range_polygon +template +struct for_each_range + : detail::for_each::fe_range_multi + < + Geometry, + Actor, + detail::for_each::fe_range_range + < + typename add_const_if_c + < + boost::is_const::value, + typename boost::range_value::type + >::type, + Actor + > + > {}; -template -struct for_each_range - : detail::for_each::fe_range_box + +template +struct for_each_range + : detail::for_each::fe_range_multi + < + Geometry, + Actor, + detail::for_each::fe_range_polygon + < + typename add_const_if_c + < + boost::is_const::value, + typename boost::range_value::type + >::type, + Actor + > + > {}; @@ -128,14 +182,12 @@ namespace detail { template -inline void for_each_range(Geometry const& geometry, Actor& actor) +inline void for_each_range(Geometry const& geometry, Actor & actor) { dispatch::for_each_range < - typename tag::type, - Geometry, - Actor, - true + Geometry const, + Actor >::apply(geometry, actor); } diff --git a/boost/geometry/algorithms/detail/get_left_turns.hpp b/boost/geometry/algorithms/detail/get_left_turns.hpp index 62f0f7f0f4..0fd243d8e3 100644 --- a/boost/geometry/algorithms/detail/get_left_turns.hpp +++ b/boost/geometry/algorithms/detail/get_left_turns.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,7 +9,12 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP +#include +#include +#include +#include #include +#include namespace boost { namespace geometry { @@ -21,342 +26,286 @@ namespace detail // TODO: move this to /util/ template -static inline std::pair ordered_pair(T const& first, T const& second) +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) +namespace left_turns { -#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) +template +inline int get_quadrant(Vector const& vector) { - 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) - ; + // Return quadrant as layouted in the code below: + // 3 | 0 + // ----- + // 2 | 1 + return geometry::get<1>(vector) >= 0 + ? (geometry::get<0>(vector) < 0 ? 3 : 0) + : (geometry::get<0>(vector) < 0 ? 2 : 1) + ; } -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) +template +inline int squared_length(Vector const& vector) { - 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; + return geometry::get<0>(vector) * geometry::get<0>(vector) + + geometry::get<1>(vector) * geometry::get<1>(vector) + ; } -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) + +template +struct angle_less { - 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; + typedef Point vector_type; + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy_type; - 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); - } + angle_less(Point const& origin) + : m_origin(origin) + {} - 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()) + template + inline bool operator()(Angle const& p, Angle const& q) const { - for (auto sit = it->second.begin(); sit != it->second.end(); ++sit) + // Vector origin -> p and origin -> q + vector_type pv = p.point; + vector_type qv = q.point; + geometry::subtract_point(pv, m_origin); + geometry::subtract_point(qv, m_origin); + + int const quadrant_p = get_quadrant(pv); + int const quadrant_q = get_quadrant(qv); + if (quadrant_p != quadrant_q) { - if (process_include(outgoing_seg_id, incoming_seg_id, *sit, turns[*sit], keep_indices, priority)) - { - result = true; - } + return quadrant_p < quadrant_q; } + // Same quadrant, check if p is located left of q + int const side = side_strategy_type::apply(m_origin, q.point, + p.point); + if (side != 0) + { + return side == 1; + } + // Collinear, check if one is incoming, incoming angles come first + if (p.incoming != q.incoming) + { + return int(p.incoming) < int(q.incoming); + } + // Same quadrant/side/direction, return longest first + // TODO: maybe not necessary, decide this + int const length_p = squared_length(pv); + int const length_q = squared_length(qv); + if (length_p != length_q) + { + return squared_length(pv) > squared_length(qv); + } + // They are still the same. Just compare on seg_id + return p.seg_id < q.seg_id; } - return result; -} -template -inline bool corresponds(Turn const& turn, segment_identifier const& seg_id) +private: + Point m_origin; +}; + +template +struct angle_equal_to { - return turn.operations[Index].operation == detail::overlay::operation_union - && turn.operations[Index].seg_id == seg_id; -} + typedef Point vector_type; + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy_type; + inline angle_equal_to(Point const& origin) + : m_origin(origin) + {} -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) + template + inline bool operator()(Angle const& p, Angle const& q) const { - map[turns[*sit].operations[0].seg_id]++; - map[turns[*sit].operations[1].seg_id]++; - } + // Vector origin -> p and origin -> q + vector_type pv = p.point; + vector_type qv = q.point; + geometry::subtract_point(pv, m_origin); + geometry::subtract_point(qv, m_origin); - std::set segment_occuring_once; - for (auto mit = map.begin(); mit != map.end(); ++mit) - { - if (mit->second == 1) + if (get_quadrant(pv) != get_quadrant(qv)) { - segment_occuring_once.insert(mit->first); + return false; } -#ifdef BOOST_GEOMETRY_DEBUG_BUFFER_PREFER - std::cout << si(mit->first) << " " << mit->second << std::endl; -#endif + // Same quadrant, check if p/q are collinear + int const side = side_strategy_type::apply(m_origin, q.point, + p.point); + return side == 0; } - if (segment_occuring_once.size() == 2) - { - // Try to find within all turns a turn with these two segments +private: + Point m_origin; +}; - std::set::const_iterator soo_it = segment_occuring_once.begin(); - segment_identifier front = *soo_it; - soo_it++; - segment_identifier back = *soo_it; +template +inline void get_left_turns(AngleCollection const& sorted_angles, + Turns& turns) +{ + std::set good_incoming; + std::set good_outgoing; - std::pair pair = ordered_pair(front, back); - auto it = turn_segment_indices.find(pair); - if (it != turn_segment_indices.end()) + for (typename boost::range_iterator::type it = + sorted_angles.begin(); it != sorted_angles.end(); ++it) + { + if (!it->blocked) { - // 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 (it->incoming) { - 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; - } + good_incoming.insert(it->turn_index); } - -#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) + else { - 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; + good_outgoing.insert(it->turn_index); } } } - 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) + if (good_incoming.empty() || good_outgoing.empty()) { return; } - // Only keep indices with high prio - std::set ok_indices; - for (auto sit = indices.begin(); sit != indices.end(); ++sit) + for (typename boost::range_iterator::type it = + sorted_angles.begin(); it != sorted_angles.end(); ++it) { - if (turns[*sit].priority >= max_prio) + if (good_incoming.count(it->turn_index) == 0 + || good_outgoing.count(it->turn_index) == 0) { - ok_indices.insert(*sit); + turns[it->turn_index].remove_on_multi = true; } } - 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); +//! Returns the number of clusters +template +inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin) +{ + // Assign same cluster_index for all turns in same direction + BOOST_ASSERT(boost::size(sorted) >= 4u); - typedef geometry::ever_circling_range_iterator circling_iterator; - circling_iterator cit(angles); + angle_equal_to comparator(origin); + typename boost::range_iterator::type it = sorted.begin(); - debug_left_turn(*cit, *cit); - for(circling_iterator prev = cit++; i < n; prev = cit++, i++) + std::size_t cluster_index = 0; + it->cluster_index = cluster_index; + typename boost::range_iterator::type previous = it++; + for (; it != sorted.end(); ++it) { - debug_left_turn(*cit, *prev); - - bool const include = ! geometry::math::equals(prev->angle, cit->angle) - && ! prev->incoming - && cit->incoming; - - if (include) + if (!comparator(*previous, *it)) { - // 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; - } + cluster_index++; + previous = it; + } + it->cluster_index = cluster_index; + } + return cluster_index + 1; +} - // 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; - } +template +inline void block_turns(AngleCollection& sorted, std::size_t cluster_size) +{ + BOOST_ASSERT(boost::size(sorted) >= 4u && cluster_size > 0); -#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); - } - } - } + std::vector > directions; + for (std::size_t i = 0; i < cluster_size; i++) + { + directions.push_back(std::make_pair(false, false)); } - if (debug_indicate_size) + for (typename boost::range_iterator::type it = sorted.begin(); + it != sorted.end(); ++it) { - std::cout << " size=" << keep_indices.size(); + if (it->incoming) + { + directions[it->cluster_index].first = true; + } + else + { + directions[it->cluster_index].second = true; + } } - if (keep_indices.size() >= 2) + for (typename boost::range_iterator::type it = sorted.begin(); + it != sorted.end(); ++it) { - prefer_by_other(turns, turn_segment_indices, keep_indices); + int cluster_index = it->cluster_index; + int previous_index = cluster_index - 1; + if (previous_index < 0) + { + previous_index = cluster_size - 1; + } + int next_index = cluster_index + 1; + if (next_index >= static_cast(cluster_size)) + { + next_index = 0; + } + + if (directions[cluster_index].first + && directions[cluster_index].second) + { + it->blocked = true; + } + else if (!directions[cluster_index].first + && directions[cluster_index].second + && directions[previous_index].second) + { + // Only outgoing, previous was also outgoing: block this one + it->blocked = true; + } + else if (directions[cluster_index].first + && !directions[cluster_index].second + && !directions[previous_index].first + && directions[previous_index].second) + { + // Only incoming, previous was only outgoing: block this one + it->blocked = true; + } + else if (directions[cluster_index].first + && !directions[cluster_index].second + && directions[next_index].first + && !directions[next_index].second) + { + // Only incoming, next also incoming, block this one + it->blocked = true; + } } - if (keep_indices.size() >= 2) +} + +#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) +template +inline bool has_rounding_issues(AngleCollection const& angles, Point const& origin) +{ + for (typename boost::range_iterator::type it = + angles.begin(); it != angles.end(); ++it) { - prefer_by_priority(turns, keep_indices); + // Vector origin -> p and origin -> q + typedef Point vector_type; + vector_type v = it->point; + geometry::subtract_point(v, origin); + return geometry::math::abs(geometry::get<0>(v)) <= 1 + || geometry::math::abs(geometry::get<1>(v)) <= 1 + ; } + return false; } +#endif + + +} // namespace left_turns } // namespace detail #endif // DOXYGEN_NO_DETAIL diff --git a/boost/geometry/algorithms/detail/get_max_size.hpp b/boost/geometry/algorithms/detail/get_max_size.hpp new file mode 100644 index 0000000000..8ac43e78b8 --- /dev/null +++ b/boost/geometry/algorithms/detail/get_max_size.hpp @@ -0,0 +1,64 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2014 Bruno Lalande, Paris, France. +// Copyright (c) 2014 Mateusz Loskot, London, UK. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_MAX_SIZE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_MAX_SIZE_HPP + + +#include + +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +struct get_max_size_box +{ + static inline typename coordinate_type::type apply(Box const& box) + { + typename coordinate_type::type s + = geometry::math::abs(geometry::get<1, Dimension>(box) - geometry::get<0, Dimension>(box)); + + return (std::max)(s, get_max_size_box::apply(box)); + } +}; + +template +struct get_max_size_box +{ + static inline typename coordinate_type::type apply(Box const& box) + { + return geometry::math::abs(geometry::get<1, 0>(box) - geometry::get<0, 0>(box)); + } +}; + +// This might be implemented later on for other geometries too. +// Not dispatched yet. +template +inline typename coordinate_type::type get_max_size(Box const& box) +{ + return get_max_size_box::value - 1>::apply(box); +} + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_MAX_SIZE_HPP diff --git a/boost/geometry/algorithms/detail/has_self_intersections.hpp b/boost/geometry/algorithms/detail/has_self_intersections.hpp index 1e6215ed93..24746ac627 100644 --- a/boost/geometry/algorithms/detail/has_self_intersections.hpp +++ b/boost/geometry/algorithms/detail/has_self_intersections.hpp @@ -17,7 +17,10 @@ #include #include -#include +#include +#include +#include +#include #ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS # include @@ -56,26 +59,33 @@ namespace detail { namespace overlay { -template -inline bool has_self_intersections(Geometry const& geometry) +template +inline bool has_self_intersections(Geometry const& geometry, + RobustPolicy const& robust_policy, + bool throw_on_self_intersection = true) { typedef typename point_type::type point_type; - typedef detail::overlay::turn_info turn_info; + typedef turn_info + < + point_type, + typename segment_ratio_type::type + > turn_info; std::deque turns; detail::disjoint::disjoint_interrupt_policy policy; - geometry::self_turns(geometry, turns, policy); - + + geometry::self_turns(geometry, robust_policy, turns, policy); + #ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS bool first = true; -#endif - for(typename std::deque::const_iterator it = boost::begin(turns); +#endif + for(typename std::deque::const_iterator it = boost::begin(turns); it != boost::end(turns); ++it) { turn_info const& info = *it; - bool const both_union_turn = + bool const both_union_turn = info.operations[0].operation == detail::overlay::operation_union && info.operations[1].operation == detail::overlay::operation_union; - bool const both_intersection_turn = + bool const both_intersection_turn = info.operations[0].operation == detail::overlay::operation_intersection && info.operations[1].operation == detail::overlay::operation_intersection; @@ -95,19 +105,40 @@ inline bool has_self_intersections(Geometry const& geometry) for (int i = 0; i < 2; i++) { std::cout << " " << operation_char(info.operations[i].operation); + std::cout << " " << info.operations[i].seg_id; } std::cout << " " << geometry::dsv(info.point) << std::endl; #endif #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) - throw overlay_invalid_input_exception(); + if (throw_on_self_intersection) + { + throw overlay_invalid_input_exception(); + } #endif + return true; } } return false; } +// For backward compatibility +template +inline bool has_self_intersections(Geometry const& geometry, + bool throw_on_self_intersection = true) +{ + typedef typename geometry::point_type::type point_type; + typedef typename geometry::rescale_policy_type::type + rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry); + + return has_self_intersections(geometry, robust_policy, + throw_on_self_intersection); +} + }} // namespace detail::overlay #endif // DOXYGEN_NO_DETAIL diff --git a/boost/geometry/algorithms/detail/interior_iterator.hpp b/boost/geometry/algorithms/detail/interior_iterator.hpp new file mode 100644 index 0000000000..3582773c3d --- /dev/null +++ b/boost/geometry/algorithms/detail/interior_iterator.hpp @@ -0,0 +1,71 @@ +// Boost.Geometry + +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERIOR_ITERATOR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERIOR_ITERATOR_HPP + +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +/*! +\brief Structure defining the type of interior rings iterator +\note If the Geometry is const, const iterator is defined. +\tparam Geometry \tparam_geometry + */ +template +struct interior_iterator +{ + typedef typename boost::range_iterator + < + typename geometry::interior_type::type + >::type type; +}; + +template +struct copy_const +{ + typedef T type; +}; + +template +struct copy_const +{ + typedef T const type; +}; + +template +struct interior_ring_iterator +{ + typedef typename boost::range_iterator + < + typename copy_const + < + typename geometry::interior_type::type, + typename boost::range_value + < + typename geometry::interior_type::type + >::type + >::type + >::type type; +}; + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERIOR_ITERATOR_HPP diff --git a/boost/geometry/algorithms/detail/intersection/box_box.hpp b/boost/geometry/algorithms/detail/intersection/box_box.hpp new file mode 100644 index 0000000000..30c31ff1e5 --- /dev/null +++ b/boost/geometry/algorithms/detail/intersection/box_box.hpp @@ -0,0 +1,54 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_BOX_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_BOX_BOX_HPP + + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Box1, typename Box2, bool Reverse +> +struct intersection + < + Box1, Box2, + box_tag, box_tag, + Reverse + > : public detail::intersection::intersection_box_box + < + 0, geometry::dimension::value + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_BOX_BOX_HPP diff --git a/boost/geometry/algorithms/detail/intersection/implementation.hpp b/boost/geometry/algorithms/detail/intersection/implementation.hpp new file mode 100644 index 0000000000..d8fb2ec38c --- /dev/null +++ b/boost/geometry/algorithms/detail/intersection/implementation.hpp @@ -0,0 +1,22 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_IMPLEMENTATION_HPP + + +#include +#include + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/intersection/interface.hpp b/boost/geometry/algorithms/detail/intersection/interface.hpp new file mode 100644 index 0000000000..323ab7c850 --- /dev/null +++ b/boost/geometry/algorithms/detail/intersection/interface.hpp @@ -0,0 +1,309 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_INTERFACE_HPP + + +// TODO: those headers probably may be removed +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// By default, all is forwarded to the intersection_insert-dispatcher +template +< + typename Geometry1, typename Geometry2, + typename Tag1 = typename geometry::tag::type, + typename Tag2 = typename geometry::tag::type, + bool Reverse = reverse_dispatch::type::value +> +struct intersection +{ + template + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + GeometryOut& geometry_out, + Strategy const& strategy) + { + typedef typename boost::range_value::type OneOut; + + intersection_insert + < + Geometry1, Geometry2, OneOut, + overlay_intersection + >::apply(geometry1, geometry2, robust_policy, std::back_inserter(geometry_out), strategy); + + return true; + } + +}; + + +// If reversal is needed, perform it +template +< + typename Geometry1, typename Geometry2, + typename Tag1, typename Tag2 +> +struct intersection +< + Geometry1, Geometry2, + Tag1, Tag2, + true +> + : intersection +{ + template + static inline bool apply( + Geometry1 const& g1, + Geometry2 const& g2, + RobustPolicy const& robust_policy, + GeometryOut& out, + Strategy const& strategy) + { + return intersection< + Geometry2, Geometry1, + Tag2, Tag1, + false + >::apply(g2, g1, robust_policy, out, strategy); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_variant +{ + +template +struct intersection +{ + template + static inline bool + apply( + const Geometry1& geometry1, + const Geometry2& geometry2, + GeometryOut& geometry_out) + { + concept::check(); + concept::check(); + + typedef typename geometry::rescale_overlay_policy_type + < + Geometry1, + Geometry2 + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, geometry2); + + typedef strategy_intersection + < + typename cs_tag::type, + Geometry1, + Geometry2, + typename geometry::point_type::type, + rescale_policy_type + > strategy; + + return dispatch::intersection + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, robust_policy, geometry_out, strategy()); + } +}; + + +template +struct intersection, Geometry2> +{ + template + struct visitor: static_visitor + { + Geometry2 const& m_geometry2; + GeometryOut& m_geometry_out; + + visitor(Geometry2 const& geometry2, + GeometryOut& geometry_out) + : m_geometry2(geometry2), + m_geometry_out(geometry_out) + {} + + template + result_type operator()(Geometry1 const& geometry1) const + { + return intersection + < + Geometry1, + Geometry2 + >::template apply + < + GeometryOut + > + (geometry1, m_geometry2, m_geometry_out); + } + }; + + template + static inline bool + apply(variant const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out) + { + return apply_visitor(visitor(geometry2, geometry_out), geometry1); + } +}; + + +template +struct intersection > +{ + template + struct visitor: static_visitor + { + Geometry1 const& m_geometry1; + GeometryOut& m_geometry_out; + + visitor(Geometry1 const& geometry1, + GeometryOut& geometry_out) + : m_geometry1(geometry1), + m_geometry_out(geometry_out) + {} + + template + result_type operator()(Geometry2 const& geometry2) const + { + return intersection + < + Geometry1, + Geometry2 + >::template apply + < + GeometryOut + > + (m_geometry1, geometry2, m_geometry_out); + } + }; + + template + static inline bool + apply( + Geometry1 const& geometry1, + const variant& geometry2, + GeometryOut& geometry_out) + { + return apply_visitor(visitor(geometry1, geometry_out), geometry2); + } +}; + + +template +struct intersection, variant > +{ + template + struct visitor: static_visitor + { + GeometryOut& m_geometry_out; + + visitor(GeometryOut& geometry_out) + : m_geometry_out(geometry_out) + {} + + template + result_type operator()( + Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + return intersection + < + Geometry1, + Geometry2 + >::template apply + < + GeometryOut + > + (geometry1, geometry2, m_geometry_out); + } + }; + + template + static inline bool + apply( + const variant& geometry1, + const variant& geometry2, + GeometryOut& geometry_out) + { + return apply_visitor(visitor(geometry_out), geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief \brief_calc2{intersection} +\ingroup intersection +\details \details_calc2{intersection, spatial set theoretic intersection}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam GeometryOut Collection of geometries (e.g. std::vector, std::deque, boost::geometry::multi*) of which + the value_type fulfills a \p_l_or_c concept, or it is the output geometry (e.g. for a box) +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param geometry_out The output geometry, either a multi_point, multi_polygon, + multi_linestring, or a box (for intersection of two boxes) + +\qbk{[include reference/algorithms/intersection.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename GeometryOut +> +inline bool intersection(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out) +{ + return resolve_variant::intersection + < + Geometry1, + Geometry2 + >::template apply + < + GeometryOut + > + (geometry1, geometry2, geometry_out); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/intersection/multi.hpp b/boost/geometry/algorithms/detail/intersection/multi.hpp new file mode 100644 index 0000000000..b1f13862fc --- /dev/null +++ b/boost/geometry/algorithms/detail/intersection/multi.hpp @@ -0,0 +1,423 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_MULTI_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_MULTI_HPP + +#include +#include +#include +#include +#include +#include + +// TODO: those headers probably may be removed +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +// TODO: remove this after moving num_point from multi directory +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + + +template +struct intersection_multi_linestring_multi_linestring_point +{ + template + < + typename MultiLinestring1, typename MultiLinestring2, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(MultiLinestring1 const& ml1, + MultiLinestring2 const& ml2, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + // Note, this loop is quadratic w.r.t. number of linestrings per input. + // Future Enhancement: first do the sections of each, then intersect. + for (typename boost::range_iterator + < + MultiLinestring1 const + >::type it1 = boost::begin(ml1); + it1 != boost::end(ml1); + ++it1) + { + for (typename boost::range_iterator + < + MultiLinestring2 const + >::type it2 = boost::begin(ml2); + it2 != boost::end(ml2); + ++it2) + { + out = intersection_linestring_linestring_point + ::apply(*it1, *it2, robust_policy, out, strategy); + } + } + + return out; + } +}; + + +template +struct intersection_linestring_multi_linestring_point +{ + template + < + typename Linestring, typename MultiLinestring, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(Linestring const& linestring, + MultiLinestring const& ml, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + for (typename boost::range_iterator + < + MultiLinestring const + >::type it = boost::begin(ml); + it != boost::end(ml); + ++it) + { + out = intersection_linestring_linestring_point + ::apply(linestring, *it, robust_policy, out, strategy); + } + + return out; + } +}; + + +// This loop is quite similar to the loop above, but beacuse the iterator +// is second (above) or first (below) argument, it is not trivial to merge them. +template +< + bool ReverseAreal, + typename LineStringOut, + overlay_type OverlayType +> +struct intersection_of_multi_linestring_with_areal +{ + template + < + typename MultiLinestring, typename Areal, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(MultiLinestring const& ml, Areal const& areal, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + for (typename boost::range_iterator + < + MultiLinestring const + >::type it = boost::begin(ml); + it != boost::end(ml); + ++it) + { + out = intersection_of_linestring_with_areal + < + ReverseAreal, LineStringOut, OverlayType + >::apply(*it, areal, robust_policy, out, strategy); + } + + return out; + + } +}; + +// This one calls the one above with reversed arguments +template +< + bool ReverseAreal, + typename LineStringOut, + overlay_type OverlayType +> +struct intersection_of_areal_with_multi_linestring +{ + template + < + typename Areal, typename MultiLinestring, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(Areal const& areal, MultiLinestring const& ml, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + return intersection_of_multi_linestring_with_areal + < + ReverseAreal, LineStringOut, OverlayType + >::apply(ml, areal, robust_policy, out, strategy); + } +}; + + + +template +struct clip_multi_linestring +{ + template + < + typename MultiLinestring, typename Box, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(MultiLinestring const& multi_linestring, + Box const& box, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) + { + typedef typename point_type::type point_type; + strategy::intersection::liang_barsky lb_strategy; + for (typename boost::range_iterator::type it + = boost::begin(multi_linestring); + it != boost::end(multi_linestring); ++it) + { + out = detail::intersection::clip_range_with_box + (box, *it, robust_policy, out, lb_strategy); + } + return out; + } +}; + + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// Linear +template +< + typename MultiLinestring1, typename MultiLinestring2, + typename GeometryOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + MultiLinestring1, MultiLinestring2, + GeometryOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + multi_linestring_tag, multi_linestring_tag, point_tag, + false, false, false + > : detail::intersection::intersection_multi_linestring_multi_linestring_point + < + GeometryOut + > +{}; + + +template +< + typename Linestring, typename MultiLinestring, + typename GeometryOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Linestring, MultiLinestring, + GeometryOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + linestring_tag, multi_linestring_tag, point_tag, + false, false, false + > : detail::intersection::intersection_linestring_multi_linestring_point + < + GeometryOut + > +{}; + + +template +< + typename MultiLinestring, typename Box, + typename GeometryOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + MultiLinestring, Box, + GeometryOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + multi_linestring_tag, box_tag, linestring_tag, + false, true, false + > : detail::intersection::clip_multi_linestring + < + GeometryOut + > +{}; + + +template +< + typename Linestring, typename MultiPolygon, + typename GeometryOut, + overlay_type OverlayType, + bool ReverseLinestring, bool ReverseMultiPolygon, bool ReverseOut +> +struct intersection_insert + < + Linestring, MultiPolygon, + GeometryOut, + OverlayType, + ReverseLinestring, ReverseMultiPolygon, ReverseOut, + linestring_tag, multi_polygon_tag, linestring_tag, + false, true, false + > : detail::intersection::intersection_of_linestring_with_areal + < + ReverseMultiPolygon, + GeometryOut, + OverlayType + > +{}; + + +// Derives from areal/mls because runtime arguments are in that order. +// areal/mls reverses it itself to mls/areal +template +< + typename Polygon, typename MultiLinestring, + typename GeometryOut, + overlay_type OverlayType, + bool ReversePolygon, bool ReverseMultiLinestring, bool ReverseOut +> +struct intersection_insert + < + Polygon, MultiLinestring, + GeometryOut, + OverlayType, + ReversePolygon, ReverseMultiLinestring, ReverseOut, + polygon_tag, multi_linestring_tag, linestring_tag, + true, false, false + > : detail::intersection::intersection_of_areal_with_multi_linestring + < + ReversePolygon, + GeometryOut, + OverlayType + > +{}; + + +template +< + typename MultiLinestring, typename Ring, + typename GeometryOut, + overlay_type OverlayType, + bool ReverseMultiLinestring, bool ReverseRing, bool ReverseOut +> +struct intersection_insert + < + MultiLinestring, Ring, + GeometryOut, + OverlayType, + ReverseMultiLinestring, ReverseRing, ReverseOut, + multi_linestring_tag, ring_tag, linestring_tag, + false, true, false + > : detail::intersection::intersection_of_multi_linestring_with_areal + < + ReverseRing, + GeometryOut, + OverlayType + > +{}; + +template +< + typename MultiLinestring, typename Polygon, + typename GeometryOut, + overlay_type OverlayType, + bool ReverseMultiLinestring, bool ReverseRing, bool ReverseOut +> +struct intersection_insert + < + MultiLinestring, Polygon, + GeometryOut, + OverlayType, + ReverseMultiLinestring, ReverseRing, ReverseOut, + multi_linestring_tag, polygon_tag, linestring_tag, + false, true, false + > : detail::intersection::intersection_of_multi_linestring_with_areal + < + ReverseRing, + GeometryOut, + OverlayType + > +{}; + + + +template +< + typename MultiLinestring, typename MultiPolygon, + typename GeometryOut, + overlay_type OverlayType, + bool ReverseMultiLinestring, bool ReverseMultiPolygon, bool ReverseOut +> +struct intersection_insert + < + MultiLinestring, MultiPolygon, + GeometryOut, + OverlayType, + ReverseMultiLinestring, ReverseMultiPolygon, ReverseOut, + multi_linestring_tag, multi_polygon_tag, linestring_tag, + false, true, false + > : detail::intersection::intersection_of_multi_linestring_with_areal + < + ReverseMultiPolygon, + GeometryOut, + OverlayType + > +{}; + + +} // namespace dispatch +#endif + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_MULTI_HPP + diff --git a/boost/geometry/algorithms/detail/is_simple/always_simple.hpp b/boost/geometry/algorithms/detail/is_simple/always_simple.hpp new file mode 100644 index 0000000000..91e2ef76bd --- /dev/null +++ b/boost/geometry/algorithms/detail/is_simple/always_simple.hpp @@ -0,0 +1,83 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_ALWAYS_SIMPLE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_ALWAYS_SIMPLE_HPP + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + + +template +struct always_simple +{ + static inline bool apply(Geometry const&) + { + return true; + } +}; + + +}} // namespace detail::is_simple +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A point is always simple +template +struct is_simple + : detail::is_simple::always_simple +{}; + + +// A valid segment is always simple. +// A segment is a curve. +// A curve is simple if it does not pass through the same point twice, +// with the possible exception of its two endpoints +// +// Reference: OGC 06-103r4 (6.1.6.1) +template +struct is_simple + : detail::is_simple::always_simple +{}; + + +// A valid box is always simple +// A box is a Polygon, and it satisfies the conditions for Polygon validity. +// +// Reference (for polygon validity): OGC 06-103r4 (6.1.11.1) +template +struct is_simple + : detail::is_simple::always_simple +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_ALWAYS_SIMPLE_HPP diff --git a/boost/geometry/algorithms/detail/is_simple/areal.hpp b/boost/geometry/algorithms/detail/is_simple/areal.hpp new file mode 100644 index 0000000000..9a1a16507a --- /dev/null +++ b/boost/geometry/algorithms/detail/is_simple/areal.hpp @@ -0,0 +1,141 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_AREAL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_AREAL_HPP + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + + +template +struct is_simple_ring +{ + static inline bool apply(Ring const& ring) + { + return + !detail::is_valid::has_duplicates + < + Ring, geometry::closure::value + >::apply(ring); + } +}; + + +template +class is_simple_polygon +{ +private: + template + static inline + bool are_simple_interior_rings(InteriorRings const& interior_rings) + { + return + detail::check_iterator_range + < + is_simple_ring + < + typename boost::range_value::type + > + >::apply(boost::begin(interior_rings), + boost::end(interior_rings)); + } + +public: + static inline bool apply(Polygon const& polygon) + { + return + is_simple_ring + < + typename ring_type::type + >::apply(exterior_ring(polygon)) + && + are_simple_interior_rings(geometry::interior_rings(polygon)); + } +}; + + +}} // namespace detail::is_simple +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A Ring is a Polygon. +// A Polygon is always a simple geometric object provided that it is valid. +// +// Reference (for polygon validity): OGC 06-103r4 (6.1.11.1) +template +struct is_simple + : detail::is_simple::is_simple_ring +{}; + + +// A Polygon is always a simple geometric object provided that it is valid. +// +// Reference (for validity of Polygons): OGC 06-103r4 (6.1.11.1) +template +struct is_simple + : detail::is_simple::is_simple_polygon +{}; + + +// Not clear what the definition is. +// Right now we consider a MultiPolygon as simple if it is valid. +// +// Reference (for validity of MultiPolygons): OGC 06-103r4 (6.1.14) +template +struct is_simple +{ + static inline bool apply(MultiPolygon const& multipolygon) + { + return + detail::check_iterator_range + < + detail::is_simple::is_simple_polygon + < + typename boost::range_value::type + >, + false // do not allow empty multi-polygon + >::apply(boost::begin(multipolygon), boost::end(multipolygon)); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_AREAL_HPP diff --git a/boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp b/boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp new file mode 100644 index 0000000000..75c37c68f8 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp @@ -0,0 +1,82 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_DEBUG_PRINT_BOUNDARY_POINTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_DEBUG_PRINT_BOUNDARY_POINTS_HPP + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +#include +#include +#include + +#include + +#include + +#include + +#include + +#include + +#include +#endif + + +namespace boost { namespace geometry +{ + +namespace detail { namespace is_simple +{ + + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +template +inline void debug_print_boundary_points(MultiLinestring const& multilinestring) +{ + typedef typename point_type::type point_type; + typedef std::vector point_vector; + + point_vector boundary_points; + for (typename boost::range_iterator::type it + = boost::begin(multilinestring); + it != boost::end(multilinestring); ++it) + { + if ( boost::size(*it) > 1 + && !geometry::equals(range::front(*it), range::back(*it)) ) + { + boundary_points.push_back( range::front(*it) ); + boundary_points.push_back( range::back(*it) ); + } + } + + std::sort(boundary_points.begin(), boundary_points.end(), + geometry::less()); + + std::cout << "boundary points: "; + for (typename point_vector::const_iterator pit = boundary_points.begin(); + pit != boundary_points.end(); ++pit) + { + std::cout << " " << geometry::dsv(*pit); + } + std::cout << std::endl << std::endl; +} +#else +template +inline void debug_print_boundary_points(MultiLinestring const&) +{ +} +#endif // BOOST_GEOMETRY_TEST_DEBUG + + +}} // namespace detail::is_simple + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_DEBUG_PRINT_BOUNDARY_POINTS_HPP diff --git a/boost/geometry/algorithms/detail/is_simple/implementation.hpp b/boost/geometry/algorithms/detail/is_simple/implementation.hpp new file mode 100644 index 0000000000..e69b273ce3 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_simple/implementation.hpp @@ -0,0 +1,18 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_IMPLEMENTATION_HPP + +#include +#include +#include +#include + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/is_simple/interface.hpp b/boost/geometry/algorithms/detail/is_simple/interface.hpp new file mode 100644 index 0000000000..4239664ed1 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_simple/interface.hpp @@ -0,0 +1,80 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_INTERFACE_HPP + +#include +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +namespace resolve_variant { + +template +struct is_simple +{ + static inline bool apply(Geometry const& geometry) + { + concept::check(); + return dispatch::is_simple::apply(geometry); + } +}; + +template +struct is_simple > +{ + struct visitor : boost::static_visitor + { + template + bool operator()(Geometry const& geometry) const + { + return is_simple::apply(geometry); + } + }; + + static inline bool + apply(boost::variant const& geometry) + { + return boost::apply_visitor(visitor(), geometry); + } +}; + +} // namespace resolve_variant + + + +/*! +\brief \brief_check{is simple} +\ingroup is_simple +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_check{is simple} + +\qbk{[include reference/algorithms/is_simple.qbk]} +*/ +template +inline bool is_simple(Geometry const& geometry) +{ + return resolve_variant::is_simple::apply(geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/is_simple/linear.hpp b/boost/geometry/algorithms/detail/is_simple/linear.hpp new file mode 100644 index 0000000000..f2efcb309d --- /dev/null +++ b/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -0,0 +1,248 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_LINEAR_HPP + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + + +template +struct is_simple_linestring +{ + static inline bool apply(Linestring const& linestring) + { + return !detail::is_valid::has_duplicates + < + Linestring, closed + >::apply(linestring) + && !detail::is_valid::has_spikes + < + Linestring, closed + >::apply(linestring) + && !(CheckSelfIntersections && geometry::intersects(linestring)); + } +}; + + + +template +class is_simple_multilinestring +{ +private: + class is_acceptable_turn + { + private: + template + static inline bool is_boundary_point_of(Point const& point, + Linestring const& linestring) + { + BOOST_ASSERT( boost::size(linestring) > 1 ); + return + !geometry::equals(range::front(linestring), + range::back(linestring)) + && + ( geometry::equals(point, range::front(linestring)) + || geometry::equals(point, range::back(linestring)) ); + } + + template + static inline bool have_same_boundary_points(Linestring1 const& ls1, + Linestring2 const& ls2) + { + return + geometry::equals(range::front(ls1), range::front(ls2)) + ? + geometry::equals(range::back(ls1), range::back(ls2)) + : + (geometry::equals(range::front(ls1), range::back(ls2)) + && + geometry::equals(range::back(ls1), range::front(ls2)) + ) + ; + } + + public: + is_acceptable_turn(MultiLinestring const& multilinestring) + : m_multilinestring(multilinestring) + {} + + template + inline bool apply(Turn const& turn) const + { + typedef typename boost::range_value + < + MultiLinestring + >::type linestring; + + linestring const& ls1 = + range::at(m_multilinestring, + turn.operations[0].seg_id.multi_index); + + linestring const& ls2 = + range::at(m_multilinestring, + turn.operations[1].seg_id.multi_index); + + return + is_boundary_point_of(turn.point, ls1) + && is_boundary_point_of(turn.point, ls2) + && + ( boost::size(ls1) != 2 + || boost::size(ls2) != 2 + || !have_same_boundary_points(ls1, ls2) ); + } + + private: + MultiLinestring const& m_multilinestring; + }; + + +public: + static inline bool apply(MultiLinestring const& multilinestring) + { + typedef typename boost::range_value::type linestring; + typedef typename point_type::type point_type; + typedef point_type point; + + + // check each of the linestrings for simplicity + if ( !detail::check_iterator_range + < + is_simple_linestring, + false // do not allow empty multilinestring + >::apply(boost::begin(multilinestring), + boost::end(multilinestring)) + ) + { + return false; + } + + + // compute self turns + typedef detail::overlay::turn_info + < + point_type, + geometry::segment_ratio + < + typename geometry::coordinate_type::type + > + > turn_info; + + std::deque turns; + + typedef detail::overlay::get_turn_info + < + detail::disjoint::assign_disjoint_policy + > turn_policy; + + is_acceptable_turn predicate(multilinestring); + detail::overlay::predicate_based_interrupt_policy + < + is_acceptable_turn + > interrupt_policy(predicate); + + detail::self_get_turn_points::get_turns + < + turn_policy + >::apply(multilinestring, + detail::no_rescale_policy(), + turns, + interrupt_policy); + + detail::is_valid::debug_print_turns(turns.begin(), turns.end()); + debug_print_boundary_points(multilinestring); + + return !interrupt_policy.has_intersections; + } + +}; + + + +}} // namespace detail::is_simple +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// A linestring is a curve. +// A curve is simple if it does not pass through the same point twice, +// with the possible exception of its two endpoints +// +// Reference: OGC 06-103r4 (6.1.6.1) +template +struct is_simple + : detail::is_simple::is_simple_linestring +{}; + + +// A MultiLinestring is a MultiCurve +// A MultiCurve is simple if all of its elements are simple and the +// only intersections between any two elements occur at Points that +// are on the boundaries of both elements. +// +// Reference: OGC 06-103r4 (6.1.8.1; Fig. 9) +template +struct is_simple + : detail::is_simple::is_simple_multilinestring +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_LINEAR_HPP diff --git a/boost/geometry/algorithms/detail/is_simple/multipoint.hpp b/boost/geometry/algorithms/detail/is_simple/multipoint.hpp new file mode 100644 index 0000000000..d996eb64e9 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_simple/multipoint.hpp @@ -0,0 +1,84 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_MULTIPOINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_MULTIPOINT_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + + +template +struct is_simple_multipoint +{ + static inline bool apply(MultiPoint const& multipoint) + { + if ( boost::size(multipoint) == 0 ) + { + return false; + } + + MultiPoint mp(multipoint); + std::sort(boost::begin(mp), boost::end(mp), + geometry::less::type>()); + + return !detail::is_valid::has_duplicates::apply(mp); + } +}; + + +}} // namespace detail::is_simple +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A MultiPoint is simple if no two Points in the MultiPoint are equal +// (have identical coordinate values in X and Y) +// +// Reference: OGC 06-103r4 (6.1.5) +template +struct is_simple + : detail::is_simple::is_simple_multipoint +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_MULTIPOINT_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/box.hpp b/boost/geometry/algorithms/detail/is_valid/box.hpp new file mode 100644 index 0000000000..f82b3f9bf1 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/box.hpp @@ -0,0 +1,86 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_BOX_HPP + +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + +template +struct has_valid_corners +{ + static inline bool apply(Box const& box) + { + if ( geometry::get(box) + <= + geometry::get(box) ) + { + return false; + } + return has_valid_corners::apply(box); + } +}; + + +template +struct has_valid_corners +{ + static inline bool apply(Box const&) + { + return true; + } +}; + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A box is always simple +// A box is a Polygon, and it satisfies the conditions for Polygon validity. +// +// The only thing we have to check is whether the max corner lies in +// the upper-right quadrant as defined by the min corner +// +// Reference (for polygon validity): OGC 06-103r4 (6.1.11.1) +template +struct is_valid + : detail::is_valid::has_valid_corners::value> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_BOX_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/complement_graph.hpp b/boost/geometry/algorithms/detail/is_valid/complement_graph.hpp new file mode 100644 index 0000000000..c59139a92e --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/complement_graph.hpp @@ -0,0 +1,239 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_COMPLEMENT_GRAPH_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_COMPLEMENT_GRAPH_HPP + +#include + +#include +#include +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +namespace detail { namespace is_valid +{ + + +template +class complement_graph_vertex +{ +public: + complement_graph_vertex(std::size_t id) + : m_id(id) + , m_turn_point(NULL) + {} + + complement_graph_vertex(TurnPoint const* turn_point, + std::size_t expected_id) + : m_id(expected_id) + , m_turn_point(turn_point) + {} + + inline std::size_t id() const { return m_id; } + + inline bool operator<(complement_graph_vertex const& other) const + { + if ( m_turn_point != NULL && other.m_turn_point != NULL ) + { + return geometry::less + < + TurnPoint + >()(*m_turn_point, *other.m_turn_point); + } + if ( m_turn_point == NULL && other.m_turn_point == NULL ) + { + return m_id < other.m_id; + } + return m_turn_point == NULL; + } + +private: + // the value of m_turn_point determines the type of the vertex + // non-NULL: vertex corresponds to an IP + // NULL : vertex corresponds to a hole or outer space, and the id + // is the ring id of the corresponding ring of the polygon + std::size_t m_id; + TurnPoint const* m_turn_point; +}; + + + + +template +class complement_graph +{ +private: + typedef complement_graph_vertex vertex; + typedef std::set vertex_container; + +public: + typedef typename vertex_container::const_iterator vertex_handle; + +private: + struct vertex_handle_less + { + inline bool operator()(vertex_handle v1, vertex_handle v2) const + { + return v1->id() < v2->id(); + } + }; + + typedef std::set neighbor_container; + + class has_cycles_dfs_data + { + public: + has_cycles_dfs_data(std::size_t num_nodes) + : m_visited(num_nodes, false) + , m_parent_id(num_nodes, -1) + {} + + inline signed_index_type parent_id(vertex_handle v) const + { + return m_parent_id[v->id()]; + } + + inline void set_parent_id(vertex_handle v, signed_index_type id) + { + m_parent_id[v->id()] = id; + } + + inline bool visited(vertex_handle v) const + { + return m_visited[v->id()]; + } + + inline void set_visited(vertex_handle v, bool value) + { + m_visited[v->id()] = value; + } + private: + std::vector m_visited; + std::vector m_parent_id; + }; + + + inline bool has_cycles(vertex_handle start_vertex, + has_cycles_dfs_data& data) const + { + std::stack stack; + stack.push(start_vertex); + + while ( !stack.empty() ) + { + vertex_handle v = stack.top(); + stack.pop(); + + data.set_visited(v, true); + for (typename neighbor_container::const_iterator nit + = m_neighbors[v->id()].begin(); + nit != m_neighbors[v->id()].end(); ++nit) + { + if ( static_cast((*nit)->id()) != data.parent_id(v) ) + { + if ( data.visited(*nit) ) + { + return true; + } + else + { + data.set_parent_id(*nit, static_cast(v->id())); + stack.push(*nit); + } + } + } + } + return false; + } + +public: + // num_rings: total number of rings, including the exterior ring + complement_graph(std::size_t num_rings) + : m_num_rings(num_rings) + , m_num_turns(0) + , m_vertices() + , m_neighbors(num_rings) + {} + + // inserts a ring vertex in the graph and returns its handle + // ring id's are zero-based (so the first interior ring has id 1) + inline vertex_handle add_vertex(signed_index_type id) + { + return m_vertices.insert(vertex(static_cast(id))).first; + } + + // inserts an IP in the graph and returns its id + inline vertex_handle add_vertex(TurnPoint const& turn_point) + { + std::pair res + = m_vertices.insert(vertex(boost::addressof(turn_point), + m_num_rings + m_num_turns) + ); + + if ( res.second ) + { + // a new element is inserted + m_neighbors.push_back(neighbor_container()); + ++m_num_turns; + } + return res.first; + } + + inline void add_edge(vertex_handle v1, vertex_handle v2) + { + BOOST_ASSERT( v1 != m_vertices.end() ); + BOOST_ASSERT( v2 != m_vertices.end() ); + m_neighbors[v1->id()].insert(v2); + m_neighbors[v2->id()].insert(v1); + } + + inline bool has_cycles() const + { + // initialize all vertices as non-visited and with no parent set + // this is done by the constructor of has_cycles_dfs_data + has_cycles_dfs_data data(m_num_rings + m_num_turns); + + // for each non-visited vertex, start a DFS from that vertex + for (vertex_handle it = m_vertices.begin(); + it != m_vertices.end(); ++it) + { + if ( !data.visited(it) && has_cycles(it, data) ) + { + return true; + } + } + return false; + } + + template + friend inline + void debug_print_complement_graph(OStream&, complement_graph const&); + +private: + std::size_t m_num_rings, m_num_turns; + vertex_container m_vertices; + std::vector m_neighbors; +}; + + +}} // namespace detail::is_valid + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_COMPLEMENT_GRAPH_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/debug_complement_graph.hpp b/boost/geometry/algorithms/detail/is_valid/debug_complement_graph.hpp new file mode 100644 index 0000000000..60f597e296 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/debug_complement_graph.hpp @@ -0,0 +1,70 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_DEBUG_COMPLEMENT_GRAPH_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_DEBUG_COMPLEMENT_GRAPH_HPP + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +#include +#endif + +namespace boost { namespace geometry +{ + +namespace detail { namespace is_valid +{ + + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +template +inline void +debug_print_complement_graph(OutputStream& os, + complement_graph const& graph) +{ + typedef typename complement_graph::vertex_handle vertex_handle; + + os << "num rings: " << graph.m_num_rings << std::endl; + os << "vertex ids: {"; + for (vertex_handle it = graph.m_vertices.begin(); + it != graph.m_vertices.end(); ++it) + { + os << " " << it->id(); + } + os << " }" << std::endl; + + for (vertex_handle it = graph.m_vertices.begin(); + it != graph.m_vertices.end(); ++it) + { + os << "neighbors of " << it->id() << ": {"; + for (typename complement_graph + < + TurnPoint + >::neighbor_container::const_iterator + nit = graph.m_neighbors[it->id()].begin(); + nit != graph.m_neighbors[it->id()].end(); ++nit) + { + os << " " << (*nit)->id(); + } + os << "}" << std::endl; + } +} +#else +template +inline void debug_print_complement_graph(OutputStream&, + complement_graph const&) +{ +} +#endif + + +}} // namespace detail::is_valid + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_COMPLEMENT_GRAPH_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp b/boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp new file mode 100644 index 0000000000..6824921b63 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp @@ -0,0 +1,64 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_DEBUG_PRINT_TURNS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_DEBUG_PRINT_TURNS_HPP + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +#include + +#include +#include +#endif + + +namespace boost { namespace geometry +{ + +namespace detail { namespace is_valid +{ + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +template +inline void debug_print_turns(TurnIterator first, TurnIterator beyond) +{ + std::cout << "turns:"; + for (TurnIterator tit = first; tit != beyond; ++tit) + { + std::cout << " [" + << geometry::method_char(tit->method) + << "," + << geometry::operation_char(tit->operations[0].operation) + << "/" + << geometry::operation_char(tit->operations[1].operation) + << " {" + << tit->operations[0].seg_id.multi_index + << ", " + << tit->operations[1].seg_id.multi_index + << "} {" + << tit->operations[0].seg_id.ring_index + << ", " + << tit->operations[1].seg_id.ring_index + << "} " + << geometry::dsv(tit->point) + << "]"; + } + std::cout << std::endl << std::endl; +} +#else +template +inline void debug_print_turns(TurnIterator, TurnIterator) +{} +#endif // BOOST_GEOMETRY_TEST_DEBUG + +}} // namespace detail::is_valid + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_DEBUG_PRINT_TURNS_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp b/boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp new file mode 100644 index 0000000000..6f1c263646 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp @@ -0,0 +1,68 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_DEBUG_VALIDITY_PHASE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_DEBUG_VALIDITY_PHASE_HPP + +#ifdef GEOMETRY_TEST_DEBUG +#include + +#include +#include +#endif + + +namespace boost { namespace geometry +{ + +namespace detail { namespace is_valid +{ + +template ::type> +struct debug_validity_phase +{ + static inline void apply(int) + { + } +}; + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +template +struct debug_validity_phase +{ + static inline void apply(int phase) + { + switch (phase) + { + case 1: + std::cout << "checking exterior ring..." << std::endl; + break; + case 2: + std::cout << "checking interior rings..." << std::endl; + break; + case 3: + std::cout << "computing and analyzing turns..." << std::endl; + break; + case 4: + std::cout << "checking if holes are inside the exterior ring..." + << std::endl; + break; + case 5: + std::cout << "checking connectivity of interior..." << std::endl; + break; + } + } +}; +#endif + +}} // namespace detail::is_valid + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_DEBUG_VALIDITY_PHASE_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp b/boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp new file mode 100644 index 0000000000..dd0922bb2b --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp @@ -0,0 +1,70 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_DUPLICATES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_DUPLICATES_HPP + +#include + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + +template +struct has_duplicates +{ + static inline bool apply(Range const& range) + { + typedef typename closeable_view::type view_type; + typedef typename boost::range_iterator::type iterator; + + view_type view(range); + + if ( boost::size(view) < 2 ) + { + return false; + } + + geometry::equal_to::type> equal; + + iterator it = boost::begin(view); + iterator next = ++boost::begin(view); + for (; next != boost::end(view); ++it, ++next) + { + if ( equal(*it, *next) ) + { + return true; + } + } + return false; + } +}; + + + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_DUPLICATES_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp b/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp new file mode 100644 index 0000000000..9b95017482 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp @@ -0,0 +1,139 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_SPIKES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_SPIKES_HPP + +#include + +#include + +#include + +#include + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + +template +struct equal_to +{ + Point const& m_point; + + equal_to(Point const& point) + : m_point(point) + {} + + template + inline bool operator()(OtherPoint const& other) const + { + return geometry::equals(m_point, other); + } +}; + +template +struct not_equal_to +{ + Point const& m_point; + + not_equal_to(Point const& point) + : m_point(point) + {} + + template + inline bool operator()(OtherPoint const& other) const + { + return !geometry::equals(other, m_point); + } +}; + + + +template +struct has_spikes +{ + static inline bool apply(Range const& range) + { + typedef not_equal_to::type> not_equal; + + typedef typename closeable_view::type view_type; + typedef typename boost::range_iterator::type iterator; + + view_type const view(range); + + iterator prev = boost::begin(view); + + iterator cur = std::find_if(prev, boost::end(view), not_equal(*prev)); + if ( cur == boost::end(view) ) + { + // the range has only one distinct point, so it + // cannot have a spike + return false; + } + + iterator next = std::find_if(cur, boost::end(view), not_equal(*cur)); + if ( next == boost::end(view) ) + { + // the range has only two distinct points, so it + // cannot have a spike + return false; + } + + while ( next != boost::end(view) ) + { + if ( geometry::detail::point_is_spike_or_equal(*prev, + *next, + *cur) ) + { + return true; + } + prev = cur; + cur = next; + next = std::find_if(cur, boost::end(view), not_equal(*cur)); + } + + if ( geometry::equals(range::front(view), range::back(view)) ) + { + iterator cur = boost::begin(view); + typename boost::range_reverse_iterator + < + view_type const + >::type prev = std::find_if(boost::rbegin(view), + boost::rend(view), + not_equal(range::back(view))); + iterator next = + std::find_if(cur, boost::end(view), not_equal(*cur)); + return detail::point_is_spike_or_equal(*prev, *next, *cur); + } + + return false; + } +}; + + + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_SPIKES_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp b/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp new file mode 100644 index 0000000000..220a67bcd1 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp @@ -0,0 +1,93 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP + +#include + +#include +#include +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + + +template +< + typename Geometry, + typename IsAcceptableTurn = is_acceptable_turn +> +class has_valid_self_turns +{ +private: + typedef typename point_type::type point_type; + + typedef typename geometry::rescale_policy_type + < + point_type + >::type rescale_policy_type; + + typedef detail::overlay::get_turn_info + < + detail::overlay::assign_null_policy + > turn_policy; + +public: + typedef detail::overlay::turn_info + < + point_type, + typename geometry::segment_ratio_type + < + point_type, + rescale_policy_type + >::type + > turn_type; + + // returns true if all turns are valid + template + static inline bool apply(Geometry const& geometry, Turns& turns) + { + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry); + + detail::overlay::stateless_predicate_based_interrupt_policy + < + IsAcceptableTurn + > interrupt_policy; + + geometry::self_turns(geometry, + robust_policy, + turns, + interrupt_policy); + + return !interrupt_policy.has_intersections; + } +}; + + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/implementation.hpp b/boost/geometry/algorithms/detail/is_valid/implementation.hpp new file mode 100644 index 0000000000..4a515a3073 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/implementation.hpp @@ -0,0 +1,21 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_IMPLEMENTATION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_IMPLEMENTATION_HPP + +#include +#include +#include +#include +#include +#include +#include + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_IMPLEMENTATION_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/interface.hpp b/boost/geometry/algorithms/detail/is_valid/interface.hpp new file mode 100644 index 0000000000..4b232fd436 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/interface.hpp @@ -0,0 +1,78 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_INTERFACE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_INTERFACE_HPP + +#include +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +namespace resolve_variant { + +template +struct is_valid +{ + static inline bool apply(Geometry const& geometry) + { + concept::check(); + return dispatch::is_valid::apply(geometry); + } +}; + +template +struct is_valid > +{ + struct visitor : boost::static_visitor + { + template + bool operator()(Geometry const& geometry) const + { + return is_valid::apply(geometry); + } + }; + + static inline bool + apply(boost::variant const& geometry) + { + return boost::apply_visitor(visitor(), geometry); + } +}; + +} // namespace resolve_variant + + +/*! +\brief \brief_check{is valid (in the OGC sense)} +\ingroup is_valid +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_check{is valid (in the OGC sense)} + +\qbk{[include reference/algorithms/is_valid.qbk]} +*/ +template +inline bool is_valid(Geometry const& geometry) +{ + return resolve_variant::is_valid::apply(geometry); +} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_INTERFACE_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp b/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp new file mode 100644 index 0000000000..f9d926770e --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp @@ -0,0 +1,144 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_IS_ACCEPTABLE_TURN_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_IS_ACCEPTABLE_TURN_HPP + +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + + +template +< + typename Geometry, + order_selector Order = geometry::point_order::value, + typename Tag = typename tag::type +> +struct acceptable_operation +{}; + +template +struct acceptable_operation +{ + static const detail::overlay::operation_type value = + detail::overlay::operation_union; +}; + +template +struct acceptable_operation +{ + static const detail::overlay::operation_type value = + detail::overlay::operation_intersection; +}; + +template +struct acceptable_operation +{ + static const detail::overlay::operation_type value = + detail::overlay::operation_intersection; +}; + +template +struct acceptable_operation +{ + static const detail::overlay::operation_type value = + detail::overlay::operation_union; +}; + + + + +template ::type> +struct is_acceptable_turn +{}; + +template +class is_acceptable_turn +{ +protected: + template + static inline bool check_turn(Turn const& turn, + Method method, + Operation operation) + { + return turn.method == method + && turn.operations[0].operation == operation + && turn.operations[1].operation == operation; + } + + +public: + template + static inline bool apply(Turn const& turn) + { + using namespace detail::overlay; + + if ( turn.operations[0].seg_id.ring_index + == turn.operations[1].seg_id.ring_index ) + { + return false; + } + + operation_type const op = acceptable_operation::value; + + return check_turn(turn, method_touch_interior, op) + || check_turn(turn, method_touch, op) + ; + } +}; + +template +class is_acceptable_turn + : is_acceptable_turn::type> +{ +private: + typedef typename boost::range_value::type polygon; + typedef is_acceptable_turn base; + +public: + template + static inline bool apply(Turn const& turn) + { + using namespace detail::overlay; + + if ( turn.operations[0].seg_id.multi_index + == turn.operations[1].seg_id.multi_index ) + { + return base::apply(turn); + } + + operation_type const op = acceptable_operation::value; + + return base::check_turn(turn, method_touch_interior, op) + || base::check_turn(turn, method_touch, op) + ; + } +}; + + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_IS_ACCEPTABLE_TURN_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/linear.hpp b/boost/geometry/algorithms/detail/is_valid/linear.hpp new file mode 100644 index 0000000000..244df9b035 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/linear.hpp @@ -0,0 +1,125 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_LINEAR_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + + +template +struct is_valid_linestring +{ + static inline bool apply(Linestring const& linestring) + { + std::size_t num_distinct = detail::num_distinct_consecutive_points + < + Linestring, + 3u, + true, + not_equal_to::type> + >::apply(linestring); + + if ( num_distinct < 2u ) + { + return false; + } + + return num_distinct == 2u + || AllowSpikes + || !has_spikes::apply(linestring); + } +}; + + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A linestring is a curve. +// A curve is 1-dimensional so it has to have at least two distinct +// points. +// A curve is simple if it does not pass through the same point twice, +// with the possible exception of its two endpoints +// +// There is an option here as to whether spikes are allowed for linestrings; +// here we pass this as an additional template parameter: allow_spikes +// If allow_spikes is set to true, spikes are allowed, false otherwise. +// By default, spikes are disallowed +// +// Reference: OGC 06-103r4 (6.1.6.1) +template +struct is_valid + : detail::is_valid::is_valid_linestring +{}; + + +// A MultiLinestring is a MultiCurve +// A MultiCurve is simple if all of its elements are simple and the +// only intersections between any two elements occur at Points that +// are on the boundaries of both elements. +// +// Reference: OGC 06-103r4 (6.1.8.1; Fig. 9) +template +struct is_valid +{ + static inline bool apply(MultiLinestring const& multilinestring) + { + return detail::check_iterator_range + < + detail::is_valid::is_valid_linestring + < + typename boost::range_value::type, + AllowSpikes + >, + false // do not allow empty multilinestring + >::apply(boost::begin(multilinestring), + boost::end(multilinestring)); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_LINEAR_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp b/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp new file mode 100644 index 0000000000..3d0ebb5f82 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp @@ -0,0 +1,297 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + + +template +class is_valid_multipolygon + : is_valid_polygon + < + typename boost::range_value::type, + AllowDuplicates, + true // check only the validity of rings + > +{ +private: + typedef is_valid_polygon + < + typename boost::range_value::type, + AllowDuplicates, + true + > base; + + + + template + static inline + bool are_polygon_interiors_disjoint(PolygonIterator polygons_first, + PolygonIterator polygons_beyond, + TurnIterator turns_first, + TurnIterator turns_beyond) + { + // collect all polygons that have turns + std::set multi_indices; + for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit) + { + multi_indices.insert(tit->operations[0].seg_id.multi_index); + multi_indices.insert(tit->operations[1].seg_id.multi_index); + } + + // put polygon iterators without turns in a vector + std::vector polygon_iterators; + signed_index_type multi_index = 0; + for (PolygonIterator it = polygons_first; it != polygons_beyond; + ++it, ++multi_index) + { + if ( multi_indices.find(multi_index) == multi_indices.end() ) + { + polygon_iterators.push_back(it); + } + } + + typename base::item_visitor visitor; + + geometry::partition + < + geometry::model::box::type>, + typename base::expand_box, + typename base::overlaps_box + >::apply(polygon_iterators, visitor); + + return !visitor.items_overlap; + } + + + + class has_multi_index + { + public: + has_multi_index(signed_index_type multi_index) + : m_multi_index(multi_index) + {} + + template + inline bool operator()(Turn const& turn) const + { + return turn.operations[0].seg_id.multi_index == m_multi_index + && turn.operations[1].seg_id.multi_index == m_multi_index; + } + + private: + signed_index_type const m_multi_index; + }; + + + + template + struct has_property_per_polygon + { + template + static inline bool apply(PolygonIterator polygons_first, + PolygonIterator polygons_beyond, + TurnIterator turns_first, + TurnIterator turns_beyond) + { + signed_index_type multi_index = 0; + for (PolygonIterator it = polygons_first; it != polygons_beyond; + ++it, ++multi_index) + { + has_multi_index index_predicate(multi_index); + + typedef boost::filter_iterator + < + has_multi_index, TurnIterator + > filtered_turn_iterator; + + filtered_turn_iterator filtered_turns_first(index_predicate, + turns_first, + turns_beyond); + + filtered_turn_iterator filtered_turns_beyond(index_predicate, + turns_beyond, + turns_beyond); + + if ( !Predicate::apply(*it, + filtered_turns_first, + filtered_turns_beyond) ) + { + return false; + } + } + return true; + } + }; + + + + template + static inline bool have_holes_inside(PolygonIterator polygons_first, + PolygonIterator polygons_beyond, + TurnIterator turns_first, + TurnIterator turns_beyond) + { + return has_property_per_polygon + < + typename base::has_holes_inside + >::apply(polygons_first, polygons_beyond, + turns_first, turns_beyond); + } + + + + template + static inline bool have_connected_interior(PolygonIterator polygons_first, + PolygonIterator polygons_beyond, + TurnIterator turns_first, + TurnIterator turns_beyond) + { + return has_property_per_polygon + < + typename base::has_connected_interior + >::apply(polygons_first, polygons_beyond, + turns_first, turns_beyond); + } + + +public: + static inline bool apply(MultiPolygon const& multipolygon) + { + typedef debug_validity_phase debug_phase; + + // check validity of all polygons ring + debug_phase::apply(1); + + if ( !detail::check_iterator_range + < + base, + false // do not allow empty multi-polygons + >::apply(boost::begin(multipolygon), + boost::end(multipolygon)) ) + { + return false; + } + + + // compute turns and check if all are acceptable + debug_phase::apply(2); + + typedef has_valid_self_turns has_valid_turns; + + std::deque turns; + bool has_invalid_turns = !has_valid_turns::apply(multipolygon, turns); + debug_print_turns(turns.begin(), turns.end()); + + if ( has_invalid_turns ) + { + return false; + } + + + // check if each polygon's interior rings are inside the + // exterior and not one inside the other + debug_phase::apply(3); + + if ( !have_holes_inside(boost::begin(multipolygon), + boost::end(multipolygon), + turns.begin(), + turns.end()) ) + { + return false; + } + + + // check that each polygon's interior is connected + debug_phase::apply(4); + + if ( !have_connected_interior(boost::begin(multipolygon), + boost::end(multipolygon), + turns.begin(), + turns.end()) ) + { + return false; + } + + + // check if polygon interiors are disjoint + debug_phase::apply(5); + return are_polygon_interiors_disjoint(boost::begin(multipolygon), + boost::end(multipolygon), + turns.begin(), + turns.end()); + } +}; + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// Not clear what the definition is. +// Right now we check that each element is simple (in fact valid), and +// that the MultiPolygon is also valid. +// +// Reference (for validity of MultiPolygons): OGC 06-103r4 (6.1.14) +template +struct is_valid + : detail::is_valid::is_valid_multipolygon +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_MULTIPOLYGON_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/pointlike.hpp b/boost/geometry/algorithms/detail/is_valid/pointlike.hpp new file mode 100644 index 0000000000..8a4818ef15 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/pointlike.hpp @@ -0,0 +1,62 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POINTLIKE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POINTLIKE_HPP + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// A point is always simple +template +struct is_valid +{ + static inline bool apply(Point const&) + { + return true; + } +}; + + + +// A MultiPoint is simple if no two Points in the MultiPoint are equal +// (have identical coordinate values in X and Y) +// +// Reference: OGC 06-103r4 (6.1.5) +template +struct is_valid +{ + static inline bool apply(MultiPoint const& multipoint) + { + return boost::size(multipoint) > 0; + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POINTLIKE_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/boost/geometry/algorithms/detail/is_valid/polygon.hpp new file mode 100644 index 0000000000..3a91999208 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -0,0 +1,376 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + + +template +< + typename Polygon, + bool AllowDuplicates, + bool CheckRingValidityOnly = false +> +class is_valid_polygon +{ +protected: + typedef debug_validity_phase debug_phase; + + + + template + static bool has_valid_interior_rings(InteriorRings const& interior_rings) + { + return + detail::check_iterator_range + < + detail::is_valid::is_valid_ring + < + typename boost::range_value::type, + AllowDuplicates, + false, // do not check self-intersections + true // indicate that the ring is interior + > + >::apply(boost::begin(interior_rings), + boost::end(interior_rings)); + } + + struct has_valid_rings + { + static inline bool apply(Polygon const& polygon) + { + typedef typename ring_type::type ring_type; + + // check validity of exterior ring + debug_phase::apply(1); + + if ( !detail::is_valid::is_valid_ring + < + ring_type, + AllowDuplicates, + false // do not check self intersections + >::apply(exterior_ring(polygon)) ) + { + return false; + } + + // check validity of interior rings + debug_phase::apply(2); + + return has_valid_interior_rings(geometry::interior_rings(polygon)); + } + }; + + + // structs from partition -- start + struct expand_box + { + template + static inline void apply(Box& total, Iterator const& it) + { + geometry::expand(total, geometry::return_envelope(*it)); + } + + }; + + struct overlaps_box + { + template + static inline bool apply(Box const& box, Iterator const& it) + { + return !geometry::disjoint(*it, box); + } + }; + + + struct item_visitor + { + bool items_overlap; + + item_visitor() : items_overlap(false) {} + + template + inline void apply(Item1 const& item1, Item2 const& item2) + { + if ( !items_overlap + && (geometry::within(*points_begin(*item1), *item2) + || geometry::within(*points_begin(*item2), *item1)) + ) + { + items_overlap = true; + } + } + }; + // structs for partition -- end + + + template + < + typename RingIterator, + typename ExteriorRing, + typename TurnIterator + > + static inline bool are_holes_inside(RingIterator rings_first, + RingIterator rings_beyond, + ExteriorRing const& exterior_ring, + TurnIterator turns_first, + TurnIterator turns_beyond) + { + // collect the interior ring indices that have turns with the + // exterior ring + std::set ring_indices; + for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit) + { + if ( tit->operations[0].seg_id.ring_index == -1 ) + { + BOOST_ASSERT( tit->operations[1].seg_id.ring_index != -1 ); + ring_indices.insert(tit->operations[1].seg_id.ring_index); + } + else if ( tit->operations[1].seg_id.ring_index == -1 ) + { + BOOST_ASSERT( tit->operations[0].seg_id.ring_index != -1 ); + ring_indices.insert(tit->operations[0].seg_id.ring_index); + } + } + + signed_index_type ring_index = 0; + for (RingIterator it = rings_first; it != rings_beyond; + ++it, ++ring_index) + { + // do not examine interior rings that have turns with the + // exterior ring + if ( ring_indices.find(ring_index) == ring_indices.end() + && !geometry::covered_by(range::front(*it), exterior_ring) ) + { + return false; + } + } + + // collect all rings (exterior and/or interior) that have turns + for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit) + { + ring_indices.insert(tit->operations[0].seg_id.ring_index); + ring_indices.insert(tit->operations[1].seg_id.ring_index); + } + + // put iterators for interior rings without turns in a vector + std::vector ring_iterators; + ring_index = 0; + for (RingIterator it = rings_first; it != rings_beyond; + ++it, ++ring_index) + { + if ( ring_indices.find(ring_index) == ring_indices.end() ) + { + ring_iterators.push_back(it); + } + } + + // call partition to check is interior rings are disjoint from + // each other + item_visitor visitor; + + geometry::partition + < + geometry::model::box::type>, + expand_box, + overlaps_box + >::apply(ring_iterators, visitor); + + return !visitor.items_overlap; + } + + template + < + typename InteriorRings, + typename ExteriorRing, + typename TurnIterator + > + static inline bool are_holes_inside(InteriorRings const& interior_rings, + ExteriorRing const& exterior_ring, + TurnIterator first, + TurnIterator beyond) + { + return are_holes_inside(boost::begin(interior_rings), + boost::end(interior_rings), + exterior_ring, + first, + beyond); + } + + struct has_holes_inside + { + template + static inline bool apply(Polygon const& polygon, + TurnIterator first, + TurnIterator beyond) + { + return are_holes_inside(geometry::interior_rings(polygon), + geometry::exterior_ring(polygon), + first, + beyond); + } + }; + + + + + struct has_connected_interior + { + template + static inline bool apply(Polygon const& polygon, + TurnIterator first, + TurnIterator beyond) + { + typedef typename std::iterator_traits + < + TurnIterator + >::value_type turn_type; + typedef complement_graph graph; + + graph g(geometry::num_interior_rings(polygon) + 1); + for (TurnIterator tit = first; tit != beyond; ++tit) + { + typename graph::vertex_handle v1 = g.add_vertex + ( tit->operations[0].seg_id.ring_index + 1 ); + typename graph::vertex_handle v2 = g.add_vertex + ( tit->operations[1].seg_id.ring_index + 1 ); + typename graph::vertex_handle vip = g.add_vertex(tit->point); + + g.add_edge(v1, vip); + g.add_edge(v2, vip); + } + + debug_print_complement_graph(std::cout, g); + + return !g.has_cycles(); + } + }; + +public: + static inline bool apply(Polygon const& polygon) + { + if ( !has_valid_rings::apply(polygon) ) + { + return false; + } + + if ( CheckRingValidityOnly ) + { + return true; + } + + // compute turns and check if all are acceptable + debug_phase::apply(3); + + typedef has_valid_self_turns has_valid_turns; + + std::deque turns; + bool has_invalid_turns = !has_valid_turns::apply(polygon, turns); + debug_print_turns(turns.begin(), turns.end()); + + if ( has_invalid_turns ) + { + return false; + } + + // check if all interior rings are inside the exterior ring + debug_phase::apply(4); + + if ( !has_holes_inside::apply(polygon, turns.begin(), turns.end()) ) + { + return false; + } + + // check whether the interior of the polygon is a connected set + debug_phase::apply(5); + + return has_connected_interior::apply(polygon, + turns.begin(), + turns.end()); + } +}; + + +}} // namespace detail::is_valid +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A Polygon is always a simple geometric object provided that it is valid. +// +// Reference (for validity of Polygons): OGC 06-103r4 (6.1.11.1) +template +struct is_valid + : detail::is_valid::is_valid_polygon +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYGON_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/ring.hpp b/boost/geometry/algorithms/detail/is_valid/ring.hpp new file mode 100644 index 0000000000..c88df79b05 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/ring.hpp @@ -0,0 +1,173 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_RING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_RING_HPP + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_valid +{ + + +// struct to check whether a ring is topologically closed +template +struct is_topologically_closed +{ + static inline bool apply(Ring const&) + { + return true; + } +}; + +template +struct is_topologically_closed +{ + static inline bool apply(Ring const& ring) + { + return geometry::equals(range::front(ring), range::back(ring)); + } +}; + + + +template +struct ring_area_predicate +{ + typedef std::greater type; +}; + +template +struct ring_area_predicate +{ + typedef std::less type; +}; + + + +template +struct is_properly_oriented +{ + typedef typename point_type::type point_type; + + typedef typename strategy::area::services::default_strategy + < + typename cs_tag::type, + point_type + >::type strategy_type; + + typedef detail::area::ring_area + < + order_as_direction::value>::value, + geometry::closure::value + > ring_area_type; + + typedef typename default_area_result::type area_result_type; + + static inline bool apply(Ring const& ring) + { + typename ring_area_predicate + < + area_result_type, IsInteriorRing + >::type predicate; + + // Check area + area_result_type const zero = area_result_type(); + return predicate(ring_area_type::apply(ring, strategy_type()), zero); + } +}; + + + +template +< + typename Ring, + bool AllowDuplicates, + bool CheckSelfIntersections = true, + bool IsInteriorRing = false +> +struct is_valid_ring +{ + static inline bool apply(Ring const& ring) + { + // return invalid if any of the following condition holds: + // (a) the ring's size is below the minimal one + // (b) the ring is not topologically closed + // (c) the ring has spikes + // (d) the ring has duplicate points (if AllowDuplicates is false) + // (e) the boundary of the ring has self-intersections + // (f) the order of the points is inconsistent with the defined order + // + // Note: no need to check if the area is zero. If this is the + // case, then the ring must have at least two spikes, which is + // checked by condition (c). + + closure_selector const closure = geometry::closure::value; + + return + ( boost::size(ring) + >= core_detail::closure::minimum_ring_size::value ) + && is_topologically_closed::apply(ring) + && (AllowDuplicates || !has_duplicates::apply(ring)) + && !has_spikes::apply(ring) + && !(CheckSelfIntersections && geometry::intersects(ring)) + && is_properly_oriented::apply(ring); + } +}; + + +}} // namespace dispatch +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// A Ring is a Polygon with exterior boundary only. +// The Ring's boundary must be a LinearRing (see OGC 06-103-r4, +// 6.1.7.1, for the definition of LinearRing) +// +// Reference (for polygon validity): OGC 06-103r4 (6.1.11.1) +template +struct is_valid + : detail::is_valid::is_valid_ring +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_RING_HPP diff --git a/boost/geometry/algorithms/detail/is_valid/segment.hpp b/boost/geometry/algorithms/detail/is_valid/segment.hpp new file mode 100644 index 0000000000..486289dabe --- /dev/null +++ b/boost/geometry/algorithms/detail/is_valid/segment.hpp @@ -0,0 +1,61 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_SEGMENT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_SEGMENT_HPP + +#include +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// A segment is a curve. +// A curve is simple if it does not pass through the same point twice, +// with the possible exception of its two endpoints +// A curve is 1-dimensional, hence we have to check is the two +// endpoints of the segment coincide, since in this case it is +// 0-dimensional. +// +// Reference: OGC 06-103r4 (6.1.6.1) +template +struct is_valid +{ + static inline bool apply(Segment const& segment) + { + typename point_type::type p[2]; + detail::assign_point_from_index<0>(segment, p[0]); + detail::assign_point_from_index<1>(segment, p[1]); + + return !geometry::equals(p[0], p[1]); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_SEGMENT_HPP diff --git a/boost/geometry/algorithms/detail/multi_modify.hpp b/boost/geometry/algorithms/detail/multi_modify.hpp new file mode 100644 index 0000000000..f0b9ddd3e6 --- /dev/null +++ b/boost/geometry/algorithms/detail/multi_modify.hpp @@ -0,0 +1,53 @@ +// 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_DETAIL_MULTI_MODIFY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_MULTI_MODIFY_HPP + + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + +template +struct multi_modify +{ + static inline void apply(MultiGeometry& multi) + { + typedef typename boost::range_iterator::type iterator_type; + for (iterator_type it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + Policy::apply(*it); + } + } +}; + + +} // namespace detail +#endif + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_MULTI_MODIFY_HPP diff --git a/boost/geometry/algorithms/detail/multi_modify_with_predicate.hpp b/boost/geometry/algorithms/detail/multi_modify_with_predicate.hpp new file mode 100644 index 0000000000..c3787f9a10 --- /dev/null +++ b/boost/geometry/algorithms/detail/multi_modify_with_predicate.hpp @@ -0,0 +1,52 @@ +// 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_DETAIL_MULTI_MODIFY_WITH_PREDICATE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_MULTI_MODIFY_WITH_PREDICATE_HPP + + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +struct multi_modify_with_predicate +{ + static inline void apply(MultiGeometry& multi, Predicate const& predicate) + { + typedef typename boost::range_iterator::type iterator_type; + for (iterator_type it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + Policy::apply(*it, predicate); + } + } +}; + + +} // namespace detail +#endif + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_MULTI_MODIFY_WITH_PREDICATE_HPP diff --git a/boost/geometry/algorithms/detail/multi_sum.hpp b/boost/geometry/algorithms/detail/multi_sum.hpp new file mode 100644 index 0000000000..af3f425c9c --- /dev/null +++ b/boost/geometry/algorithms/detail/multi_sum.hpp @@ -0,0 +1,52 @@ +// 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_DETAIL_MULTI_SUM_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_MULTI_SUM_HPP + +#include + + +namespace boost { namespace geometry +{ +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +struct multi_sum +{ + template + static inline ReturnType apply(MultiGeometry const& geometry, Strategy const& strategy) + { + ReturnType sum = ReturnType(); + for (typename boost::range_iterator + < + MultiGeometry const + >::type it = boost::begin(geometry); + it != boost::end(geometry); + ++it) + { + sum += Policy::apply(*it, strategy); + } + return sum; + } +}; + + +} // namespace detail +#endif + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_MULTI_SUM_HPP diff --git a/boost/geometry/algorithms/detail/num_distinct_consecutive_points.hpp b/boost/geometry/algorithms/detail/num_distinct_consecutive_points.hpp new file mode 100644 index 0000000000..16fba72fe0 --- /dev/null +++ b/boost/geometry/algorithms/detail/num_distinct_consecutive_points.hpp @@ -0,0 +1,93 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_NUM_DISTINCT_CONSECUTIVE_POINTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_NUM_DISTINCT_CONSECUTIVE_POINTS_HPP + +#include + +#include + +#include + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + +// returns the number of distinct values in the range; +// return values are 0u through MaximumNumber, where MaximumNumber +// corresponds to MaximumNumber or more distinct values +// +// FUTURE: take into account topologically closed ranges; +// add appropriate template parameter(s) to control whether +// the closing point for topologically closed ranges is to be +// accounted for separately or not +template +< + typename Range, + std::size_t MaximumNumber, + bool AllowDuplicates /* true */, + typename NotEqualTo +> +struct num_distinct_consecutive_points +{ + static inline std::size_t apply(Range const& range) + { + typedef typename boost::range_iterator::type iterator; + + std::size_t const size = boost::size(range); + + if ( size < 2u ) + { + return (size < MaximumNumber) ? size : MaximumNumber; + } + + iterator current = boost::begin(range); + std::size_t counter(0); + do + { + ++counter; + iterator next = std::find_if(current, + boost::end(range), + NotEqualTo(*current)); + current = next; + } + while ( current != boost::end(range) && counter <= MaximumNumber ); + + return counter; + } +}; + + +template +struct num_distinct_consecutive_points +{ + static inline std::size_t apply(Range const& range) + { + std::size_t const size = boost::size(range); + return (size < MaximumNumber) ? size : MaximumNumber; + } +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_NUM_DISTINCT_CONSECUTIVE_POINTS_HPP diff --git a/boost/geometry/algorithms/detail/occupation_info.hpp b/boost/geometry/algorithms/detail/occupation_info.hpp index e147ba12d8..d90f3cf7cf 100644 --- a/boost/geometry/algorithms/detail/occupation_info.hpp +++ b/boost/geometry/algorithms/detail/occupation_info.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,17 +9,13 @@ #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 #include @@ -33,291 +29,159 @@ namespace boost { namespace geometry 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 T angle_type; typedef Point point_type; segment_identifier seg_id; int turn_index; int operation_index; + std::size_t cluster_index; Point intersection_point; - Point direction_point; - T angle; + Point point; // either incoming or outgoing point bool incoming; + bool blocked; + bool included; + + inline angle_info() + : blocked(false) + , included(false) + {} }; 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) - {} + typedef std::vector collection_type; - template - inline void add(PointC const& map_point, Point1 const& direction_point, Point2 const& intersection_point, + template + inline void add(RobustPoint const& incoming_point, + RobustPoint const& outgoing_point, + RobustPoint 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; - } + segment_identifier const& seg_id) + { + geometry::equal_to comparator; + if (comparator(incoming_point, intersection_point)) + { + return; + } + if (comparator(outgoing_point, intersection_point)) + { + 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; + { + info.point = incoming_point; + info.incoming = true; + m_angles.push_back(info); + } + { + info.point = outgoing_point; + info.incoming = false; + m_angles.push_back(info); + } + } - if (comparator.equals(intersection_point, *it)) + template + inline void get_left_turns(RobustPoint const& origin, Turns& turns) { - // It should be equal only once. But otherwise we skip it (in "add") - it = advance_circular(it, ring, seg_id, false); + // Sort on angle + std::sort(m_angles.begin(), m_angles.end(), + detail::left_turns::angle_less(origin)); + + // Group same-angled elements + std::size_t cluster_size = detail::left_turns::assign_cluster_indices(m_angles, origin); + // Block all turns on the right side of any turn + detail::left_turns::block_turns(m_angles, cluster_size); + detail::left_turns::get_left_turns(m_angles, turns); } - 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++) +#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) + template + inline bool has_rounding_issues(RobustPoint const& origin) const { - it = advance_circular(it, ring, real_seg_id); + return detail::left_turns::has_rounding_issues(angles, origin); } +#endif - info.add(map_point, *it, intersection_point, turn_index, operation_index, real_seg_id, false); -} - +private : + collection_type m_angles; // each turn splitted in incoming/outgoing vectors +}; -// 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 +template +inline void move_index(Pieces const& pieces, int& index, int& piece_index, int direction) { -public : - typedef std::map > map_type; + BOOST_ASSERT(direction == 1 || direction == -1); + BOOST_ASSERT(piece_index >= 0 && piece_index < static_cast(boost::size(pieces)) ); + BOOST_ASSERT(index >= 0 && index < static_cast(boost::size(pieces[piece_index].robust_ring))); - map_type map; - std::set turn_indices; - - inline OccupationInfo& find_or_insert(Point const& point, Point& mapped_point) + index += direction; + if (direction == -1 && index < 0) { - typename map_type::iterator it = map.find(point); - if (it == boost::end(map)) + piece_index--; + if (piece_index < 0) { - std::pair pair - = map.insert(std::make_pair(point, OccupationInfo())); - it = pair.first; + piece_index = boost::size(pieces) - 1; } - mapped_point = it->first; - return it->second; + index = boost::size(pieces[piece_index].robust_ring) - 1; } - - inline bool contains(Point const& point) const + if (direction == 1 + && index >= static_cast(boost::size(pieces[piece_index].robust_ring))) { - typename map_type::const_iterator it = map.find(point); - return it != boost::end(map); + piece_index++; + if (piece_index >= static_cast(boost::size(pieces))) + { + piece_index = 0; + } + index = 0; } +} - inline bool contains_turn_index(int index) const - { - return turn_indices.count(index) > 0; - } - inline void insert_turn_index(int index) +template +< + typename RobustPoint, + typename Turn, + typename Pieces, + typename Info +> +inline void add_incoming_and_outgoing_angles( + RobustPoint const& intersection_point, // rescaled + Turn const& turn, + Pieces const& pieces, // using rescaled offsets of it + int operation_index, + segment_identifier seg_id, + Info& info) +{ + segment_identifier real_seg_id = seg_id; + geometry::equal_to comparator; + + // Move backward and forward + RobustPoint direction_points[2]; + for (int i = 0; i < 2; i++) { - turn_indices.insert(index); + int index = turn.operations[operation_index].index_in_robust_ring; + int piece_index = turn.operations[operation_index].piece_index; + while(comparator(pieces[piece_index].robust_ring[index], intersection_point)) + { + move_index(pieces, index, piece_index, i == 0 ? -1 : 1); + } + direction_points[i] = pieces[piece_index].robust_ring[index]; } -}; + + info.add(direction_points[0], direction_points[1], intersection_point, + turn.turn_index, operation_index, real_seg_id); +} } // namespace detail diff --git a/boost/geometry/algorithms/detail/overlay/add_rings.hpp b/boost/geometry/algorithms/detail/overlay/add_rings.hpp index 74595fedd0..5ff0b57d6e 100644 --- a/boost/geometry/algorithms/detail/overlay/add_rings.hpp +++ b/boost/geometry/algorithms/detail/overlay/add_rings.hpp @@ -75,15 +75,15 @@ 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 + 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 @@ -117,15 +117,14 @@ inline OutputIterator add_rings(SelectionMap const& map, } } - // 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; - } + // 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) + if (geometry::num_points(result) >= min_num_points + && math::larger(geometry::area(result), zero)) + { + *out++ = result; + } } } return out; diff --git a/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp b/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp index 2c0f88e2aa..0fd1fe4de9 100644 --- a/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp +++ b/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include @@ -29,7 +29,7 @@ template inline void append_no_duplicates(Range& range, Point const& point, bool force = false) { if (boost::size(range) == 0 - || force + || force || ! geometry::detail::equals::equals_point_point(*(boost::end(range)-1), point)) { #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION diff --git a/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp b/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp new file mode 100644 index 0000000000..d44db17ad3 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp @@ -0,0 +1,160 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPS_OR_SPIKES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPS_OR_SPIKES_HPP + +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +// TODO: move this / rename this +template +inline bool points_equal_or_close(Point1 const& point1, + Point2 const& point2, + RobustPolicy const& robust_policy) +{ + if (detail::equals::equals_point_point(point1, point2)) + { + return true; + } + + if (! RobustPolicy::enabled) + { + return false; + } + + // Try using specified robust policy + typedef typename geometry::robust_point_type + < + Point1, + RobustPolicy + >::type robust_point_type; + + robust_point_type point1_rob, point2_rob; + geometry::recalculate(point1_rob, point1, robust_policy); + geometry::recalculate(point2_rob, point2, robust_policy); + + return detail::equals::equals_point_point(point1_rob, point2_rob); +} + + +template +inline void append_no_dups_or_spikes(Range& range, Point const& point, + RobustPolicy const& robust_policy) +{ +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION + std::cout << " add: (" + << geometry::get<0>(point) << ", " << geometry::get<1>(point) << ")" + << std::endl; +#endif + // The code below thies condition checks all spikes/dups + // for geometries >= 3 points. + // So we have to check the first potential duplicate differently + if (boost::size(range) == 1 + && points_equal_or_close(*(boost::begin(range)), point, robust_policy)) + { + return; + } + + traits::push_back::apply(range, point); + + // If a point is equal, or forming a spike, remove the pen-ultimate point + // because this one caused the spike. + // If so, the now-new-pen-ultimate point can again cause a spike + // (possibly at a corner). So keep doing this. + // Besides spikes it will also avoid adding duplicates. + while(boost::size(range) >= 3 + && point_is_spike_or_equal(point, + *(boost::end(range) - 3), + *(boost::end(range) - 2), + robust_policy)) + { + // Use the Concept/traits, so resize and append again + traits::resize::apply(range, boost::size(range) - 2); + traits::push_back::apply(range, point); + } +} + +template +inline void clean_closing_dups_and_spikes(Range& range, + RobustPolicy const& robust_policy) +{ + std::size_t const minsize + = core_detail::closure::minimum_ring_size + < + geometry::closure::value + >::value; + + if (boost::size(range) <= minsize) + { + return; + } + + typedef typename boost::range_iterator::type iterator_type; + static bool const closed = geometry::closure::value == geometry::closed; + +// TODO: the following algorithm could be rewritten to first look for spikes +// and then erase some number of points from the beginning of the Range + + bool found = false; + do + { + found = false; + iterator_type first = boost::begin(range); + iterator_type second = first + 1; + iterator_type ultimate = boost::end(range) - 1; + if (closed) + { + ultimate--; + } + + // Check if closing point is a spike (this is so if the second point is + // considered as a spike w.r.t. the last segment) + if (point_is_spike_or_equal(*second, *ultimate, *first, robust_policy)) + { + range::erase(range, first); + if (closed) + { + // Remove closing last point + range::resize(range, boost::size(range) - 1); + // Add new closing point + range::push_back(range, range::front(range)); + } + found = true; + } + } while(found && boost::size(range) > minsize); +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPS_OR_SPIKES_HPP diff --git a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index 5063f49eb4..67b48cc471 100644 --- a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -18,6 +18,10 @@ #include +#ifdef BOOST_GEOMETRY_TIME_OVERLAY +# include +#endif + namespace boost { namespace geometry { @@ -123,30 +127,30 @@ struct assign_visitor template inline void apply(Item const& outer, Item const& inner, bool first = true) { - if (first && outer.real_area < 0) + if (first && outer.abs_area < inner.abs_area) { - // Reverse arguments + // Apply with reversed arguments apply(inner, outer, false); return; } - if (math::larger(outer.real_area, 0)) + if (m_check_for_orientation + || (math::larger(outer.real_area, 0) + && math::smaller(inner.real_area, 0))) { - if (inner.real_area < 0 || m_check_for_orientation) - { - ring_info_type& inner_in_map = m_ring_map[inner.id]; + ring_info_type& inner_in_map = m_ring_map[inner.id]; - if (geometry::within(inner_in_map.point, outer.envelope) - && within_selected_input(inner_in_map, outer.id, m_geometry1, m_geometry2, m_collection) - ) + if (geometry::within(inner_in_map.point, outer.envelope) + && within_selected_input(inner_in_map, outer.id, m_geometry1, m_geometry2, m_collection) + ) + { + // Assign a parent if there was no earlier parent, or the newly + // found parent is smaller than the previous one + if (inner_in_map.parent.source_index == -1 + || outer.abs_area < inner_in_map.parent_area) { - // Only assign parent if that parent is smaller (or if it is the first) - if (inner_in_map.parent.source_index == -1 - || outer.abs_area < inner_in_map.parent_area) - { - inner_in_map.parent = outer.id; - inner_in_map.parent_area = outer.abs_area; - } + inner_in_map.parent = outer.id; + inner_in_map.parent_area = outer.abs_area; } } } @@ -243,7 +247,7 @@ inline void assign_parents(Geometry1 const& geometry1, // a dramatic improvement (factor 5 for star_comb testcase) ring_identifier id_of_positive = vector[index_positive].id; ring_info_type& outer = ring_map[id_of_positive]; - std::size_t index = 0; + index = 0; for (vector_iterator_type it = boost::begin(vector); it != boost::end(vector); ++it, ++index) { @@ -284,13 +288,21 @@ inline void assign_parents(Geometry1 const& geometry1, { it->second.discarded = true; } - else if (it->second.parent.source_index >= 0 && it->second.get_area() > 0) + else if (it->second.parent.source_index >= 0 + && math::larger(it->second.get_area(), 0)) { - // Discard positive inner ring with parent - it->second.discarded = true; + const ring_info_type& parent = ring_map[it->second.parent]; + + if (math::larger(parent.area, 0)) + { + // Discard positive inner ring with positive parent + it->second.discarded = true; + } + // Remove parent ID from any positive inner ring it->second.parent.source_index = -1; } - else if (it->second.parent.source_index < 0 && it->second.get_area() < 0) + else if (it->second.parent.source_index < 0 + && math::smaller(it->second.get_area(), 0)) { // Reverse negative ring without parent it->second.reversed = true; @@ -309,6 +321,8 @@ inline void assign_parents(Geometry1 const& geometry1, } } + +// Version for one geometry (called by buffer) template < typename Geometry, @@ -320,7 +334,7 @@ inline void assign_parents(Geometry const& geometry, RingMap& ring_map, bool check_for_orientation) { - // Call it with an empty geometry + // Call it with an empty geometry as second geometry (source_id == 1) // (ring_map should be empty for source_id==1) Geometry empty; diff --git a/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp b/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp index 012b3aca30..90901dee70 100644 --- a/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp +++ b/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp @@ -55,8 +55,8 @@ inline void clear_visit_info(Turns& turns) struct backtrack_state { bool m_good; - - inline backtrack_state() : m_good(true) {} + + inline backtrack_state() : m_good(true) {} inline void reset() { m_good = true; } inline bool good() const { return m_good; } }; @@ -79,29 +79,30 @@ class backtrack_check_self_intersections public : typedef state state_type; - template - static inline void apply(std::size_t size_at_start, - Rings& rings, typename boost::range_value::type& ring, + template + static inline void apply(std::size_t size_at_start, + Rings& rings, Ring& ring, Turns& turns, Operation& operation, std::string const& , Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, state_type& state ) { state.m_good = false; - + // Check self-intersections and throw exception if appropriate if (! state.m_checked) { state.m_checked = true; - has_self_intersections(geometry1); - has_self_intersections(geometry2); + has_self_intersections(geometry1, robust_policy); + has_self_intersections(geometry2, robust_policy); } // Make bad output clean rings.resize(size_at_start); - ring.clear(); + geometry::traits::clear::type>::apply(ring); // Reject this as a starting point operation.visited.set_rejected(); @@ -123,7 +124,7 @@ public : typedef backtrack_state state_type; template - static inline void apply(std::size_t size_at_start, + static inline void apply(std::size_t size_at_start, Rings& rings, typename boost::range_value::type& ring, Turns& turns, Operation& operation, std::string const& reason, @@ -133,7 +134,7 @@ public : ) { std::cout << " REJECT " << reason << std::endl; - + state.m_good = false; rings.resize(size_at_start); diff --git a/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp b/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp deleted file mode 100644 index 2003d2350d..0000000000 --- a/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) - -// 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_ALGORITHMS_DETAIL_OVERLAY_CALCULATE_DISTANCE_POLICY_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CALCULATE_DISTANCE_POLICY_HPP - - -#include - - -namespace boost { namespace geometry -{ - - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace overlay -{ - - -/*! - \brief Policy calculating distance - \details get_turn_info has an optional policy to get some - extra information. - This policy calculates the distance (using default distance strategy) - */ -struct calculate_distance_policy -{ - static bool const include_no_turn = false; - static bool const include_degenerate = false; - static bool const include_opposite = false; - - 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); - info.operations[1].enriched.distance - = geometry::comparable_distance(info.point, p2); - } - -}; - - -}} // namespace detail::overlay -#endif //DOXYGEN_NO_DETAIL - - -}} // namespace boost::geometry - - -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CALCULATE_DISTANCE_POLICY_HPP diff --git a/boost/geometry/algorithms/detail/overlay/check_enrich.hpp b/boost/geometry/algorithms/detail/overlay/check_enrich.hpp index b210fd04b1..03be18e07a 100644 --- a/boost/geometry/algorithms/detail/overlay/check_enrich.hpp +++ b/boost/geometry/algorithms/detail/overlay/check_enrich.hpp @@ -137,7 +137,7 @@ inline bool check_graph(TurnPoints& turn_points, operation_type for_operation) it != boost::end(meta_turns); ++it) { - if (! (it->turn->blocked() || it->turn->is_discarded())) + if (! (it->turn->blocked() || it->turn->discarded)) { for (int i = 0 ; i < 2; i++) { diff --git a/boost/geometry/algorithms/detail/overlay/convert_ring.hpp b/boost/geometry/algorithms/detail/overlay/convert_ring.hpp index 05bd721e7f..51955b515d 100644 --- a/boost/geometry/algorithms/detail/overlay/convert_ring.hpp +++ b/boost/geometry/algorithms/detail/overlay/convert_ring.hpp @@ -77,12 +77,23 @@ struct convert_ring } else { - interior_rings(destination).resize( - interior_rings(destination).size() + 1); - geometry::convert(source, interior_rings(destination).back()); - if (reverse) + // Avoid adding interior rings which are invalid + // because of its number of points: + std::size_t const min_num_points + = core_detail::closure::minimum_ring_size + < + geometry::closure::value + >::value; + + if (geometry::num_points(source) >= min_num_points) { - boost::reverse(interior_rings(destination).back()); + interior_rings(destination).resize( + interior_rings(destination).size() + 1); + geometry::convert(source, interior_rings(destination).back()); + if (reverse) + { + boost::reverse(interior_rings(destination).back()); + } } } } diff --git a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp index 5e18d0453a..20a6d7f48d 100644 --- a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp +++ b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp @@ -17,8 +17,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -51,7 +53,7 @@ struct copy_segment_point_range SegmentIdentifier const& seg_id, bool second, PointOut& point) { - int index = seg_id.segment_index; + signed_index_type index = seg_id.segment_index; if (second) { index++; @@ -94,8 +96,8 @@ struct copy_segment_point_polygon >::apply ( seg_id.ring_index < 0 - ? geometry::exterior_ring(polygon) - : geometry::interior_rings(polygon)[seg_id.ring_index], + ? geometry::exterior_ring(polygon) + : range::at(geometry::interior_rings(polygon), seg_id.ring_index), seg_id, second, point ); @@ -110,7 +112,7 @@ struct copy_segment_point_box SegmentIdentifier const& seg_id, bool second, PointOut& point) { - int index = seg_id.segment_index; + signed_index_type index = seg_id.segment_index; if (second) { index++; @@ -124,6 +126,30 @@ struct copy_segment_point_box }; +template +< + typename MultiGeometry, + typename SegmentIdentifier, + typename PointOut, + typename Policy +> +struct copy_segment_point_multi +{ + static inline bool apply(MultiGeometry const& multi, + SegmentIdentifier const& seg_id, bool second, + PointOut& point) + { + + BOOST_ASSERT + ( + seg_id.multi_index >= 0 + && seg_id.multi_index < int(boost::size(multi)) + ); + + // Call the single-version + return Policy::apply(range::at(multi, seg_id.multi_index), seg_id, second, point); + } +}; }} // namespace detail::copy_segments @@ -188,6 +214,66 @@ struct copy_segment_point {}; +template +< + typename MultiGeometry, + bool Reverse, + typename SegmentIdentifier, + typename PointOut +> +struct copy_segment_point + < + multi_polygon_tag, + MultiGeometry, + Reverse, + SegmentIdentifier, + PointOut + > + : detail::copy_segments::copy_segment_point_multi + < + MultiGeometry, + SegmentIdentifier, + PointOut, + detail::copy_segments::copy_segment_point_polygon + < + typename boost::range_value::type, + Reverse, + SegmentIdentifier, + PointOut + > + > +{}; + +template +< + typename MultiGeometry, + bool Reverse, + typename SegmentIdentifier, + typename PointOut +> +struct copy_segment_point + < + multi_linestring_tag, + MultiGeometry, + Reverse, + SegmentIdentifier, + PointOut + > + : detail::copy_segments::copy_segment_point_multi + < + MultiGeometry, + SegmentIdentifier, + PointOut, + detail::copy_segments::copy_segment_point_range + < + typename boost::range_value::type, + Reverse, + SegmentIdentifier, + PointOut + > + > +{}; + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH diff --git a/boost/geometry/algorithms/detail/overlay/copy_segments.hpp b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp index 805f3923e3..ceeb1a3b8b 100644 --- a/boost/geometry/algorithms/detail/overlay/copy_segments.hpp +++ b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp @@ -1,6 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -10,24 +16,32 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP -#include -#include #include +#include #include +#include #include +#include #include #include #include #include +#include +#include +#include #include #include #include #include #include +#include + +#include + namespace boost { namespace geometry { @@ -38,34 +52,38 @@ namespace detail { namespace copy_segments { -template -< - typename Ring, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> +template struct copy_segments_ring { - typedef typename closeable_view + template + < + typename Ring, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > + static inline void apply(Ring const& ring, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, + RangeOut& current_output) + { + typedef typename closeable_view < Ring const, closure::value >::type cview_type; - typedef typename reversible_view + typedef typename reversible_view < cview_type const, Reverse ? iterate_reverse : iterate_forward >::type rview_type; - typedef typename boost::range_iterator::type iterator; - typedef geometry::ever_circling_iterator ec_iterator; + typedef typename boost::range_iterator::type iterator; + typedef geometry::ever_circling_iterator ec_iterator; + - static inline void apply(Ring const& ring, - SegmentIdentifier const& seg_id, int to_index, - RangeOut& current_output) - { cview_type cview(ring); rview_type view(cview); @@ -75,10 +93,10 @@ struct copy_segments_ring // So we use the ever-circling iterator and determine when to step out - int const from_index = seg_id.segment_index + 1; + signed_index_type const from_index = seg_id.segment_index + 1; // Sanity check - BOOST_ASSERT(from_index < int(boost::size(view))); + BOOST_ASSERT(from_index < static_cast(boost::size(view))); ec_iterator it(boost::begin(view), boost::end(view), boost::begin(view) + from_index); @@ -86,103 +104,130 @@ struct copy_segments_ring // [2..4] -> 4 - 2 + 1 = 3 -> {2,3,4} -> OK // [4..2],size=6 -> 6 - 4 + 2 + 1 = 5 -> {4,5,0,1,2} -> OK // [1..1], travel the whole ring round - typedef typename boost::range_difference::type size_type; - size_type const count = from_index <= to_index + signed_index_type const count = from_index <= to_index ? to_index - from_index + 1 - : int(boost::size(view)) - from_index + to_index + 1; + : static_cast(boost::size(view)) + - from_index + to_index + 1; - for (size_type i = 0; i < count; ++i, ++it) + for (signed_index_type i = 0; i < count; ++i, ++it) { - detail::overlay::append_no_duplicates(current_output, *it); + detail::overlay::append_no_dups_or_spikes(current_output, *it, robust_policy); } } }; -template -< - typename LineString, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments_linestring +template +class copy_segments_linestring { +private: + // remove spikes + template + static inline void append_to_output(RangeOut& current_output, + Point const& point, + RobustPolicy const& robust_policy, + boost::true_type const&) + { + detail::overlay::append_no_dups_or_spikes(current_output, point, + robust_policy); + } - typedef typename boost::range_iterator::type iterator; + // keep spikes + template + static inline void append_to_output(RangeOut& current_output, + Point const& point, + RobustPolicy const&, + boost::false_type const&) + { + detail::overlay::append_no_duplicates(current_output, point); + } +public: + template + < + typename LineString, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > static inline void apply(LineString const& ls, - SegmentIdentifier const& seg_id, int to_index, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, RangeOut& current_output) { - int const from_index = seg_id.segment_index + 1; + signed_index_type const from_index = seg_id.segment_index + 1; // Sanity check - if (from_index > to_index || from_index < 0 || to_index >= int(boost::size(ls))) + if ( from_index > to_index + || from_index < 0 + || to_index >= static_cast(boost::size(ls)) ) { return; } - typedef typename boost::range_difference::type size_type; - size_type const count = to_index - from_index + 1; + signed_index_type const count = to_index - from_index + 1; - typename boost::range_iterator::type it = boost::begin(ls) + from_index; + typename boost::range_iterator::type + it = boost::begin(ls) + from_index; - for (size_type i = 0; i < count; ++i, ++it) + for (signed_index_type i = 0; i < count; ++i, ++it) { - detail::overlay::append_no_duplicates(current_output, *it); + append_to_output(current_output, *it, robust_policy, + boost::integral_constant()); } } }; -template -< - typename Polygon, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> +template struct copy_segments_polygon { + template + < + typename Polygon, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > static inline void apply(Polygon const& polygon, - SegmentIdentifier const& seg_id, int to_index, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, RangeOut& current_output) { // Call ring-version with the right ring - copy_segments_ring - < - typename geometry::ring_type::type, - Reverse, - SegmentIdentifier, - RangeOut - >::apply - ( - seg_id.ring_index < 0 + copy_segments_ring::apply + ( + seg_id.ring_index < 0 ? geometry::exterior_ring(polygon) - : geometry::interior_rings(polygon)[seg_id.ring_index], - seg_id, to_index, - current_output - ); + : range::at(geometry::interior_rings(polygon), seg_id.ring_index), + seg_id, to_index, + robust_policy, + current_output + ); } }; -template -< - typename Box, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> +template struct copy_segments_box { + template + < + typename Box, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > static inline void apply(Box const& box, - SegmentIdentifier const& seg_id, int to_index, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, RangeOut& current_output) { - int index = seg_id.segment_index + 1; + signed_index_type index = seg_id.segment_index + 1; BOOST_ASSERT(index < 5); - int const count = index <= to_index + signed_index_type const count = index <= to_index ? to_index - index + 1 : 5 - index + to_index + 1; @@ -193,15 +238,48 @@ struct copy_segments_box // (possibly cyclic) copy to output // (see comments in ring-version) - for (int i = 0; i < count; i++, index++) + for (signed_index_type i = 0; i < count; i++, index++) { - detail::overlay::append_no_duplicates(current_output, bp[index % 5]); + detail::overlay::append_no_dups_or_spikes(current_output, + bp[index % 5], robust_policy); } } }; +template +struct copy_segments_multi +{ + template + < + typename MultiGeometry, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > + static inline void apply(MultiGeometry const& multi_geometry, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, + RangeOut& current_output) + { + + BOOST_ASSERT + ( + seg_id.multi_index >= 0 + && seg_id.multi_index < int(boost::size(multi_geometry)) + ); + + // Call the single-version + Policy::apply(range::at(multi_geometry, seg_id.multi_index), + seg_id, to_index, + robust_policy, + current_output); + } +}; + + }} // namespace detail::copy_segments #endif // DOXYGEN_NO_DETAIL @@ -213,82 +291,44 @@ namespace dispatch template < typename Tag, - typename GeometryIn, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut + bool Reverse > -struct copy_segments -{ - BOOST_MPL_ASSERT_MSG - ( - false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE - , (types) - ); -}; +struct copy_segments : not_implemented +{}; -template -< - typename Ring, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments - : detail::copy_segments::copy_segments_ring - < - Ring, Reverse, SegmentIdentifier, RangeOut - > +template +struct copy_segments + : detail::copy_segments::copy_segments_ring {}; +template +struct copy_segments + : detail::copy_segments::copy_segments_linestring +{}; -template -< - typename LineString, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments - : detail::copy_segments::copy_segments_linestring - < - LineString, Reverse, SegmentIdentifier, RangeOut - > +template +struct copy_segments + : detail::copy_segments::copy_segments_polygon {}; -template -< - typename Polygon, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments - : detail::copy_segments::copy_segments_polygon - < - Polygon, Reverse, SegmentIdentifier, RangeOut - > + +template +struct copy_segments + : detail::copy_segments::copy_segments_box {}; -template -< - typename Box, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments - : detail::copy_segments::copy_segments_box +template +struct copy_segments + : detail::copy_segments::copy_segments_multi < - Box, Reverse, SegmentIdentifier, RangeOut + detail::copy_segments::copy_segments_polygon > {}; - } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH @@ -303,10 +343,13 @@ template bool Reverse, typename Geometry, typename SegmentIdentifier, + typename RobustPolicy, typename RangeOut > inline void copy_segments(Geometry const& geometry, - SegmentIdentifier const& seg_id, int to_index, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, RangeOut& range_out) { concept::check(); @@ -314,11 +357,8 @@ inline void copy_segments(Geometry const& geometry, dispatch::copy_segments < typename tag::type, - Geometry, - Reverse, - SegmentIdentifier, - RangeOut - >::apply(geometry, seg_id, to_index, range_out); + Reverse + >::apply(geometry, seg_id, to_index, robust_policy, range_out); } diff --git a/boost/geometry/algorithms/detail/overlay/do_reverse.hpp b/boost/geometry/algorithms/detail/overlay/do_reverse.hpp new file mode 100644 index 0000000000..15100f8d0b --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/do_reverse.hpp @@ -0,0 +1,47 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DO_REVERSE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DO_REVERSE_HPP + +#include + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +// Metafunction helper for intersection and union +template +struct do_reverse {}; + +template <> +struct do_reverse : boost::false_type {}; + +template <> +struct do_reverse : boost::true_type {}; + +template <> +struct do_reverse : boost::true_type {}; + +template <> +struct do_reverse : boost::false_type {}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DO_REVERSE_HPP diff --git a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index e4842d35f1..9484479b45 100644 --- a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -27,8 +27,9 @@ #include #include -#include #include +#include +#include #ifdef BOOST_GEOMETRY_DEBUG_ENRICH # include #endif @@ -47,17 +48,23 @@ struct indexed_turn_operation { typedef TurnOperation type; - int index; - int operation_index; + std::size_t turn_index; + std::size_t operation_index; bool discarded; - TurnOperation subject; - - inline indexed_turn_operation(int i, int oi, TurnOperation const& s) - : index(i) + // use pointers to avoid copies, const& is not possible because of usage in vector + segment_identifier const* other_seg_id; // segment id of other segment of intersection of two segments + TurnOperation const* subject; + + inline indexed_turn_operation(std::size_t ti, std::size_t oi, + TurnOperation const& s, + segment_identifier const& oid) + : turn_index(ti) , operation_index(oi) , discarded(false) - , subject(s) + , other_seg_id(&oid) + , subject(&s) {} + }; template @@ -75,19 +82,22 @@ template typename TurnPoints, typename Indexed, typename Geometry1, typename Geometry2, + typename RobustPolicy, bool Reverse1, bool Reverse2, typename Strategy > -struct sort_on_segment_and_distance +struct sort_on_segment_and_ratio { - inline sort_on_segment_and_distance(TurnPoints const& turn_points + inline sort_on_segment_and_ratio(TurnPoints const& turn_points , Geometry1 const& geometry1 , Geometry2 const& geometry2 + , RobustPolicy const& robust_policy , Strategy const& strategy , bool* clustered) : m_turn_points(turn_points) , m_geometry1(geometry1) , m_geometry2(geometry2) + , m_robust_policy(robust_policy) , m_strategy(strategy) , m_clustered(clustered) { @@ -98,31 +108,47 @@ private : TurnPoints const& m_turn_points; Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; + RobustPolicy const& m_robust_policy; Strategy const& m_strategy; mutable bool* m_clustered; + typedef typename geometry::point_type::type point_type; + inline bool consider_relative_order(Indexed const& left, Indexed const& right) const { - typedef typename geometry::point_type::type point_type; point_type pi, pj, ri, rj, si, sj; geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.seg_id, + left.subject->seg_id, pi, pj); geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.other_id, + *left.other_seg_id, ri, rj); geometry::copy_segment_points(m_geometry1, m_geometry2, - right.subject.other_id, + *right.other_seg_id, si, sj); - int const order = get_relative_order + typedef typename strategy::side::services::default_strategy < - point_type - >::apply(pi, pj,ri, rj, si, sj); - //debug("r/o", order == -1); - return order == -1; + typename cs_tag::type + >::type strategy; + + int const side_rj_p = strategy::apply(pi, pj, rj); + int const side_sj_p = strategy::apply(pi, pj, sj); + + // Put the one turning left (1; right == -1) as last + if (side_rj_p != side_sj_p) + { + return side_rj_p < side_sj_p; + } + + int const side_sj_r = strategy::apply(ri, rj, sj); + int const side_rj_s = strategy::apply(si, sj, rj); + + // If they both turn left: the most left as last + // If they both turn right: this is not relevant, but take also here most left + return side_rj_s < side_sj_r; } public : @@ -131,33 +157,30 @@ public : // but to the "indexed_turn_operation" inline bool operator()(Indexed const& left, Indexed const& right) const { - segment_identifier const& sl = left.subject.seg_id; - segment_identifier const& sr = right.subject.seg_id; + segment_identifier const& sl = left.subject->seg_id; + segment_identifier const& sr = right.subject->seg_id; - if (sl == sr - && geometry::math::equals(left.subject.enriched.distance - , right.subject.enriched.distance)) + if (sl == sr) { // Both left and right are located on the SAME segment. - - // First check "real" intersection (crosses) - // -> distance zero due to precision, solve it by sorting - if (m_turn_points[left.index].method == method_crosses - && m_turn_points[right.index].method == method_crosses) + if (left.subject->fraction == right.subject->fraction) { - return consider_relative_order(left, right); - } - - // If that is not the case, cluster it later on. - // Indicate that this is necessary. - *m_clustered = true; + // First check "real" intersection (crosses) + // -> distance zero due to precision, solve it by sorting + if (m_turn_points[left.turn_index].method == method_crosses + && m_turn_points[right.turn_index].method == method_crosses) + { + return consider_relative_order(left, right); + } - return left.index < right.index; + // If that is not the case, cluster it later on. + // Indicate that this is necessary. + *m_clustered = true; + } } return sl == sr - ? left.subject.enriched.distance < right.subject.enriched.distance + ? left.subject->fraction < right.subject->fraction : sl < sr; - } }; @@ -171,13 +194,13 @@ inline void update_discarded(Turns& turn_points, Operations& operations) it != boost::end(operations); ++it) { - if (turn_points[it->index].discarded) + if (turn_points[it->turn_index].discarded) { it->discarded = true; } else if (it->discarded) { - turn_points[it->index].discarded = true; + turn_points[it->turn_index].discarded = true; } } } @@ -195,12 +218,14 @@ template typename Container, typename TurnPoints, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Strategy > inline void enrich_sort(Container& operations, TurnPoints& turn_points, operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, Strategy const& strategy) { typedef typename IndexType::type operations_type; @@ -208,14 +233,15 @@ inline void enrich_sort(Container& operations, bool clustered = false; std::sort(boost::begin(operations), boost::end(operations), - sort_on_segment_and_distance + sort_on_segment_and_ratio < TurnPoints, IndexType, Geometry1, Geometry2, + RobustPolicy, Reverse1, Reverse2, Strategy - >(turn_points, geometry1, geometry2, strategy, &clustered)); + >(turn_points, geometry1, geometry2, robust_policy, strategy, &clustered)); // DONT'T discard xx / (for union) ix / ii / (for intersection) ux / uu here // It would give way to "lonely" ui turn points, traveling all @@ -230,16 +256,15 @@ inline void enrich_sort(Container& operations, it != boost::end(operations); prev = it++) { - operations_type& prev_op = turn_points[prev->index] + operations_type& prev_op = turn_points[prev->turn_index] .operations[prev->operation_index]; - operations_type& op = turn_points[it->index] + operations_type& op = turn_points[it->turn_index] .operations[it->operation_index]; if (prev_op.seg_id == op.seg_id - && (turn_points[prev->index].method != method_crosses - || turn_points[it->index].method != method_crosses) - && geometry::math::equals(prev_op.enriched.distance, - op.enriched.distance)) + && (turn_points[prev->turn_index].method != method_crosses + || turn_points[it->turn_index].method != method_crosses) + && prev_op.fraction == op.fraction) { if (begin_cluster == boost::end(operations)) { @@ -249,14 +274,14 @@ inline void enrich_sort(Container& operations, else if (begin_cluster != boost::end(operations)) { handle_cluster(begin_cluster, it, turn_points, - for_operation, geometry1, geometry2, strategy); + for_operation, geometry1, geometry2, robust_policy, strategy); begin_cluster = boost::end(operations); } } if (begin_cluster != boost::end(operations)) { handle_cluster(begin_cluster, it, turn_points, - for_operation, geometry1, geometry2, strategy); + for_operation, geometry1, geometry2, robust_policy, strategy); } } @@ -315,19 +340,19 @@ inline void enrich_assign(Container& operations, prev = it++) { operations_type& prev_op - = turn_points[prev->index].operations[prev->operation_index]; + = turn_points[prev->turn_index].operations[prev->operation_index]; operations_type& op - = turn_points[it->index].operations[it->operation_index]; + = turn_points[it->turn_index].operations[it->operation_index]; prev_op.enriched.travels_to_ip_index - = it->index; + = static_cast(it->turn_index); prev_op.enriched.travels_to_vertex_index - = it->subject.seg_id.segment_index; + = it->subject->seg_id.segment_index; if (! first && prev_op.seg_id.segment_index == op.seg_id.segment_index) { - prev_op.enriched.next_ip_index = it->index; + prev_op.enriched.next_ip_index = static_cast(it->turn_index); } first = false; } @@ -340,16 +365,16 @@ inline void enrich_assign(Container& operations, it != boost::end(operations); ++it) { - operations_type& op = turn_points[it->index] + operations_type& op = turn_points[it->turn_index] .operations[it->operation_index]; - std::cout << it->index - << " meth: " << method_char(turn_points[it->index].method) + std::cout << it->turn_index + << " meth: " << method_char(turn_points[it->turn_index].method) << " seg: " << op.seg_id - << " dst: " << boost::numeric_cast(op.enriched.distance) - << " op: " << operation_char(turn_points[it->index].operations[0].operation) - << operation_char(turn_points[it->index].operations[1].operation) - << " dsc: " << (turn_points[it->index].discarded ? "T" : "F") + << " dst: " << op.fraction // needs define + << " op: " << operation_char(turn_points[it->turn_index].operations[0].operation) + << operation_char(turn_points[it->turn_index].operations[1].operation) + << " dsc: " << (turn_points[it->turn_index].discarded ? "T" : "F") << " ->vtx " << op.enriched.travels_to_vertex_index << " ->ip " << op.enriched.travels_to_ip_index << " ->nxt ip " << op.enriched.next_ip_index @@ -370,7 +395,7 @@ inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vecto typedef typename boost::range_value::type turn_point_type; typedef typename turn_point_type::container_type container_type; - int index = 0; + std::size_t index = 0; for (typename boost::range_iterator::type it = boost::begin(turn_points); it != boost::end(turn_points); @@ -379,7 +404,7 @@ inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vecto // Add operations on this ring, but skip discarded ones if (! it->discarded) { - int op_index = 0; + std::size_t op_index = 0; for (typename boost::range_iterator::type op_it = boost::begin(it->operations); op_it != boost::end(it->operations); @@ -397,7 +422,8 @@ inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vecto ); mapped_vector[ring_id].push_back ( - IndexedType(index, op_index, *op_it) + IndexedType(index, op_index, *op_it, + it->operations[1 - op_index].seg_id) ); } } @@ -422,6 +448,7 @@ inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vecto \param for_operation operation_type (union or intersection) \param geometry1 \param_geometry \param geometry2 \param_geometry +\param robust_policy policy to handle robustness issues \param strategy strategy */ template @@ -429,11 +456,13 @@ template bool Reverse1, bool Reverse2, typename TurnPoints, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Strategy > inline void enrich_intersection_points(TurnPoints& turn_points, detail::overlay::operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, Strategy const& strategy) { typedef typename boost::range_value::type turn_point_type; @@ -462,6 +491,10 @@ inline void enrich_intersection_points(TurnPoints& turn_points, { it->discarded = true; } + if (it->both(detail::overlay::operation_none)) + { + it->discarded = true; + } } @@ -484,7 +517,7 @@ inline void enrich_intersection_points(TurnPoints& turn_points, << mit->first << std::endl; #endif detail::overlay::enrich_sort(mit->second, turn_points, for_operation, - geometry1, geometry2, strategy); + geometry1, geometry2, robust_policy, strategy); } for (typename mapped_vector_type::iterator mit diff --git a/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp index 8c8ed96189..ef32edeefa 100644 --- a/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp @@ -10,9 +10,6 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP -#include - - namespace boost { namespace geometry { @@ -31,37 +28,22 @@ namespace detail { namespace overlay template struct enrichment_info { - typedef typename strategy::distance::services::return_type - < - typename strategy::distance::services::comparable_type - < - typename strategy::distance::services::default_strategy - < - point_tag, - P - >::type - >::type - >::type distance_type; - inline enrichment_info() : travels_to_vertex_index(-1) , travels_to_ip_index(-1) , next_ip_index(-1) - , distance(distance_type()) {} // vertex to which is free travel after this IP, // so from "segment_index+1" to "travels_to_vertex_index", without IP-s, // can be -1 - int travels_to_vertex_index; + signed_index_type travels_to_vertex_index; // same but now IP index, so "next IP index" but not on THIS segment int travels_to_ip_index; // index of next IP on this segment, -1 if there is no one int next_ip_index; - - distance_type distance; // distance-measurement from segment.first to IP }; diff --git a/boost/geometry/algorithms/detail/overlay/follow.hpp b/boost/geometry/algorithms/detail/overlay/follow.hpp index b110cc9602..acf38d09ab 100644 --- a/boost/geometry/algorithms/detail/overlay/follow.hpp +++ b/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -1,6 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -20,6 +25,7 @@ #include #include +#include namespace boost { namespace geometry @@ -32,7 +38,7 @@ namespace detail { namespace overlay namespace following { - + template static inline bool is_entering(Turn const& /* TODO remove this parameter */, Operation const& op) { @@ -44,43 +50,43 @@ static inline bool is_entering(Turn const& /* TODO remove this parameter */, Ope ; } -template +template < - typename Turn, - typename Operation, - typename LineString, + typename Turn, + typename Operation, + typename LineString, typename Polygon > -static inline bool last_covered_by(Turn const& turn, Operation const& op, +static inline bool last_covered_by(Turn const& turn, Operation const& op, LineString const& linestring, Polygon const& polygon) { - // Check any point between the this one and the first IP + // Check any point between the this one and the first IP typedef typename geometry::point_type::type point_type; point_type point_in_between; detail::point_on_border::midpoint_helper < point_type, 0, dimension::value - >::apply(point_in_between, linestring[op.seg_id.segment_index], turn.point); + >::apply(point_in_between, *(::boost::begin(linestring) + op.seg_id.segment_index), turn.point); return geometry::covered_by(point_in_between, polygon); } -template +template < - typename Turn, - typename Operation, - typename LineString, + typename Turn, + typename Operation, + typename LineString, typename Polygon > -static inline bool is_leaving(Turn const& turn, Operation const& op, - bool entered, bool first, +static inline bool is_leaving(Turn const& turn, Operation const& op, + bool entered, bool first, LineString const& linestring, Polygon const& polygon) { if (op.operation == operation_union) { - return entered + return entered || turn.method == method_crosses || (first && last_covered_by(turn, op, linestring, polygon)) ; @@ -89,20 +95,20 @@ static inline bool is_leaving(Turn const& turn, Operation const& op, } -template +template < - typename Turn, - typename Operation, - typename LineString, + typename Turn, + typename Operation, + typename LineString, typename Polygon > -static inline bool is_staying_inside(Turn const& turn, Operation const& op, - bool entered, bool first, +static inline bool is_staying_inside(Turn const& turn, Operation const& op, + bool entered, bool first, LineString const& linestring, Polygon const& polygon) { if (turn.method == method_crosses) { - // The normal case, this is completely covered with entering/leaving + // The normal case, this is completely covered with entering/leaving // so stay out of this time consuming "covered_by" return false; } @@ -115,11 +121,11 @@ static inline bool is_staying_inside(Turn const& turn, Operation const& op, return false; } -template +template < - typename Turn, - typename Operation, - typename Linestring, + typename Turn, + typename Operation, + typename Linestring, typename Polygon > static inline bool was_entered(Turn const& turn, Operation const& op, bool first, @@ -134,7 +140,7 @@ static inline bool was_entered(Turn const& turn, Operation const& op, bool first // Template specialization structure to call the right actions for the right type -template +template struct action_selector { // If you get here the overlay type is not intersection or difference @@ -142,51 +148,86 @@ struct action_selector }; // Specialization for intersection, containing the implementation -template<> -struct action_selector +template +struct action_selector { template < - typename OutputIterator, - typename LineStringOut, - typename LineString, - typename Point, - typename Operation + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation, + typename RobustPolicy > static inline void enter(LineStringOut& current_piece, - LineString const& , + LineString const& , segment_identifier& segment_id, - int , Point const& point, - Operation const& operation, OutputIterator& ) + signed_index_type , Point const& point, + Operation const& operation, + RobustPolicy const& , + OutputIterator& ) { // On enter, append the intersection point and remember starting point + // TODO: we don't check on spikes for linestrings (?). Consider this. detail::overlay::append_no_duplicates(current_piece, point); segment_id = operation.seg_id; } template < - typename OutputIterator, - typename LineStringOut, - typename LineString, - typename Point, - typename Operation + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation, + typename RobustPolicy > static inline void leave(LineStringOut& current_piece, LineString const& linestring, segment_identifier& segment_id, - int index, Point const& point, - Operation const& , OutputIterator& out) + signed_index_type index, Point const& point, + Operation const& , + RobustPolicy const& robust_policy, + OutputIterator& out) { // On leave, copy all segments from starting point, append the intersection point // and add the output piece - geometry::copy_segments(linestring, segment_id, index, current_piece); + detail::copy_segments::copy_segments_linestring + < + false, RemoveSpikes + >::apply(linestring, segment_id, index, robust_policy, current_piece); detail::overlay::append_no_duplicates(current_piece, point); - if (current_piece.size() > 1) + if (::boost::size(current_piece) > 1) { *out++ = current_piece; } - current_piece.clear(); + + geometry::clear(current_piece); + } + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void isolated_point(LineStringOut&, + LineString const&, + segment_identifier&, + signed_index_type, Point const& point, + Operation const& , OutputIterator& out) + { + LineStringOut isolated_point_ls; + geometry::append(isolated_point_ls, point); + +#ifndef BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS + geometry::append(isolated_point_ls, point); +#endif // BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS + + *out++ = isolated_point_ls; } static inline bool is_entered(bool entered) @@ -194,8 +235,15 @@ struct action_selector return entered; } - template - static inline bool included(Point const& point, Geometry const& geometry) + template + < + typename Point, + typename Geometry, + typename RobustPolicy + > + static inline bool included(Point const& point, + Geometry const& geometry, + RobustPolicy const& ) { return geometry::covered_by(point, geometry); } @@ -203,45 +251,67 @@ struct action_selector }; // Specialization for difference, which reverses these actions -template<> -struct action_selector +template +struct action_selector { - typedef action_selector normal_action; + typedef action_selector normal_action; template < - typename OutputIterator, - typename LineStringOut, - typename LineString, - typename Point, - typename Operation + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation, + typename RobustPolicy > - static inline void enter(LineStringOut& current_piece, - LineString const& linestring, - segment_identifier& segment_id, - int index, Point const& point, - Operation const& operation, OutputIterator& out) + static inline void enter(LineStringOut& current_piece, + LineString const& linestring, + segment_identifier& segment_id, + signed_index_type index, Point const& point, + Operation const& operation, + RobustPolicy const& robust_policy, + OutputIterator& out) { - normal_action::leave(current_piece, linestring, segment_id, index, - point, operation, out); + normal_action::leave(current_piece, linestring, segment_id, index, + point, operation, robust_policy, out); } template < - typename OutputIterator, - typename LineStringOut, - typename LineString, - typename Point, - typename Operation + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation, + typename RobustPolicy > static inline void leave(LineStringOut& current_piece, LineString const& linestring, segment_identifier& segment_id, - int index, Point const& point, - Operation const& operation, OutputIterator& out) + signed_index_type index, Point const& point, + Operation const& operation, + RobustPolicy const& robust_policy, + OutputIterator& out) { normal_action::enter(current_piece, linestring, segment_id, index, - point, operation, out); + point, operation, robust_policy, out); + } + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void isolated_point(LineStringOut&, + LineString const&, + segment_identifier&, + signed_index_type, Point const&, + Operation const&, OutputIterator&) + { } static inline bool is_entered(bool entered) @@ -249,10 +319,17 @@ struct action_selector return ! normal_action::is_entered(entered); } - template - static inline bool included(Point const& point, Geometry const& geometry) + template + < + typename Point, + typename Geometry, + typename RobustPolicy + > + static inline bool included(Point const& point, + Geometry const& geometry, + RobustPolicy const& robust_policy) { - return ! normal_action::included(point, geometry); + return ! normal_action::included(point, geometry, robust_policy); } }; @@ -269,12 +346,13 @@ template typename LineStringOut, typename LineString, typename Polygon, - overlay_type OverlayType + overlay_type OverlayType, + bool RemoveSpikes = true > class follow { - template + template struct sort_on_segment { // In case of turn point at the same location, we want to have continue/blocked LAST @@ -296,15 +374,15 @@ class follow inline bool use_operation(Turn const& left, Turn const& right) const { - // If they are the same, OK. + // If they are the same, OK. return operation_order(left) < operation_order(right); } inline bool use_distance(Turn const& left, Turn const& right) const { - return geometry::math::equals(left.operations[0].enriched.distance, right.operations[0].enriched.distance) + return left.operations[0].fraction == right.operations[0].fraction ? use_operation(left, right) - : left.operations[0].enriched.distance < right.operations[0].enriched.distance + : left.operations[0].fraction < right.operations[0].fraction ; } @@ -325,16 +403,33 @@ class follow public : - template - static inline bool included(Point const& point, Geometry const& geometry) + template + < + typename Point, + typename Geometry, + typename RobustPolicy + > + static inline bool included(Point const& point, + Geometry const& geometry, + RobustPolicy const& robust_policy) { - return following::action_selector::included(point, geometry); + return following::action_selector + < + OverlayType, RemoveSpikes + >::included(point, geometry, robust_policy); } - template + template + < + typename Turns, + typename OutputIterator, + typename RobustPolicy + > static inline OutputIterator apply(LineString const& linestring, Polygon const& polygon, detail::overlay::operation_type , // TODO: this parameter might be redundant - Turns& turns, OutputIterator out) + Turns& turns, + RobustPolicy const& robust_policy, + OutputIterator out) { typedef typename boost::range_iterator::type turn_iterator; typedef typename boost::range_value::type turn_type; @@ -343,7 +438,7 @@ public : typename turn_type::container_type >::type turn_operation_iterator_type; - typedef following::action_selector action; + typedef following::action_selector action; // Sort intersection points on segments-along-linestring, and distance // (like in enrich is done for poly/poly) @@ -376,27 +471,38 @@ public : debug_traverse(*it, *iit, "-> Entering"); entered = true; - action::enter(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, out); + action::enter(current_piece, linestring, current_segment_id, + iit->seg_id.segment_index, it->point, *iit, + robust_policy, + out); } else if (following::is_leaving(*it, *iit, entered, first, linestring, polygon)) { debug_traverse(*it, *iit, "-> Leaving"); entered = false; - action::leave(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, out); + action::leave(current_piece, linestring, current_segment_id, + iit->seg_id.segment_index, it->point, *iit, + robust_policy, + out); } first = false; } if (action::is_entered(entered)) { - geometry::copy_segments(linestring, current_segment_id, - boost::size(linestring) - 1, - current_piece); + detail::copy_segments::copy_segments_linestring + < + false, RemoveSpikes + >::apply(linestring, + current_segment_id, + static_cast(boost::size(linestring) - 1), + robust_policy, + current_piece); } // Output the last one, if applicable - if (current_piece.size() > 1) + if (::boost::size(current_piece) > 1) { *out++ = current_piece; } diff --git a/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp b/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp new file mode 100644 index 0000000000..85378e08b0 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp @@ -0,0 +1,536 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +namespace following { namespace linear +{ + + + + +// follower for linear/linear geometries set operations + +template +static inline bool is_entering(Turn const& turn, + Operation const& operation) +{ + if ( turn.method != method_touch && turn.method != method_touch_interior ) + { + return false; + } + return operation.operation == operation_intersection; +} + + + +template +static inline bool is_staying_inside(Turn const& turn, + Operation const& operation, + bool entered) +{ + if ( !entered ) + { + return false; + } + + if ( turn.method != method_equal && turn.method != method_collinear ) + { + return false; + } + return operation.operation == operation_continue; +} + + + +template +static inline bool is_leaving(Turn const& turn, + Operation const& operation, + bool entered) +{ + if ( !entered ) + { + return false; + } + + if ( turn.method != method_touch + && turn.method != method_touch_interior + && turn.method != method_equal + && turn.method != method_collinear ) + { + return false; + } + + if ( operation.operation == operation_blocked ) + { + return true; + } + + if ( operation.operation != operation_union ) + { + return false; + } + + return operation.is_collinear; +} + + + +template +static inline bool is_isolated_point(Turn const& turn, + Operation const& operation, + bool entered) +{ + if ( entered ) + { + return false; + } + + if ( turn.method == method_none ) + { + BOOST_ASSERT( operation.operation == operation_continue ); + return true; + } + + if ( turn.method == method_crosses ) + { + return true; + } + + if ( turn.method != method_touch && turn.method != method_touch_interior ) + { + return false; + } + + if ( operation.operation == operation_blocked ) + { + return true; + } + + if ( operation.operation != operation_union ) + { + return false; + } + + return !operation.is_collinear; +} + + + + + + + + + +template +< + typename LinestringOut, + typename Linestring, + typename Linear, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns +> +class follow_linestring_linear_linestring +{ +protected: + // allow spikes (false indicates: do not remove spikes) + typedef following::action_selector action; + + template + < + typename TurnIterator, + typename TurnOperationIterator, + typename SegmentIdentifier, + typename OutputIterator + > + static inline OutputIterator + process_turn(TurnIterator it, + TurnOperationIterator op_it, + bool& entered, + std::size_t& enter_count, + Linestring const& linestring, + LinestringOut& current_piece, + SegmentIdentifier& current_segment_id, + OutputIterator oit) + { + // We don't rescale linear/linear + detail::no_rescale_policy robust_policy; + + if ( is_entering(*it, *op_it) ) + { + detail::turns::debug_turn(*it, *op_it, "-> Entering"); + + entered = true; + if ( enter_count == 0 ) + { + action::enter(current_piece, linestring, + current_segment_id, + op_it->seg_id.segment_index, + it->point, *op_it, robust_policy, oit); + } + ++enter_count; + } + else if ( is_leaving(*it, *op_it, entered) ) + { + detail::turns::debug_turn(*it, *op_it, "-> Leaving"); + + --enter_count; + if ( enter_count == 0 ) + { + entered = false; + action::leave(current_piece, linestring, + current_segment_id, + op_it->seg_id.segment_index, + it->point, *op_it, robust_policy, oit); + } + } + else if ( FollowIsolatedPoints + && is_isolated_point(*it, *op_it, entered) ) + { + detail::turns::debug_turn(*it, *op_it, "-> Isolated point"); + + action::isolated_point(current_piece, linestring, + current_segment_id, + op_it->seg_id.segment_index, + it->point, *op_it, oit); + } + else if ( FollowContinueTurns + && is_staying_inside(*it, *op_it, entered) ) + { + detail::turns::debug_turn(*it, *op_it, "-> Staying inside"); + + entered = true; + } + return oit; + } + + template + < + typename SegmentIdentifier, + typename OutputIterator + > + static inline OutputIterator + process_end(bool entered, + Linestring const& linestring, + SegmentIdentifier const& current_segment_id, + LinestringOut& current_piece, + OutputIterator oit) + { + if ( action::is_entered(entered) ) + { + // We don't rescale linear/linear + detail::no_rescale_policy robust_policy; + + detail::copy_segments::copy_segments_linestring + < + false, false // do not reverse; do not remove spikes + >::apply(linestring, + current_segment_id, + static_cast(boost::size(linestring) - 1), + robust_policy, + current_piece); + } + + // Output the last one, if applicable + if (::boost::size(current_piece) > 1) + { + *oit++ = current_piece; + } + + return oit; + } + +public: + template + static inline OutputIterator + apply(Linestring const& linestring, Linear const&, + TurnIterator first, TurnIterator beyond, + OutputIterator oit) + { + // Iterate through all intersection points (they are + // ordered along the each line) + + LinestringOut current_piece; + geometry::segment_identifier current_segment_id(0, -1, -1, -1); + + bool entered = false; + std::size_t enter_count = 0; + + for (TurnIterator it = first; it != beyond; ++it) + { + oit = process_turn(it, boost::begin(it->operations), + entered, enter_count, + linestring, + current_piece, current_segment_id, + oit); + } + + BOOST_ASSERT( enter_count == 0 ); + + return process_end(entered, linestring, + current_segment_id, current_piece, + oit); + } +}; + + + + +template +< + typename LinestringOut, + typename MultiLinestring, + typename Linear, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns +> +class follow_multilinestring_linear_linestring + : follow_linestring_linear_linestring + < + LinestringOut, + typename boost::range_value::type, + Linear, + OverlayType, + FollowIsolatedPoints, + FollowContinueTurns + > +{ +protected: + typedef typename boost::range_value::type Linestring; + + typedef follow_linestring_linear_linestring + < + LinestringOut, Linestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns + > Base; + + typedef following::action_selector action; + + typedef typename boost::range_iterator + < + MultiLinestring const + >::type linestring_iterator; + + + template + struct copy_linestrings_in_range + { + static inline OutputIt + apply(linestring_iterator, linestring_iterator, OutputIt oit) + { + return oit; + } + }; + + template + struct copy_linestrings_in_range + { + static inline OutputIt + apply(linestring_iterator first, linestring_iterator beyond, + OutputIt oit) + { + for (linestring_iterator ls_it = first; ls_it != beyond; ++ls_it) + { + LinestringOut line_out; + geometry::convert(*ls_it, line_out); + *oit++ = line_out; + } + return oit; + } + }; + + template + static inline signed_index_type get_multi_index(TurnIterator it) + { + return boost::begin(it->operations)->seg_id.multi_index; + } + + class has_other_multi_id + { + private: + signed_index_type m_multi_id; + + public: + has_other_multi_id(signed_index_type multi_id) + : m_multi_id(multi_id) {} + + template + bool operator()(Turn const& turn) const + { + return boost::begin(turn.operations)->seg_id.multi_index + != m_multi_id; + } + }; + +public: + template + static inline OutputIterator + apply(MultiLinestring const& multilinestring, Linear const& linear, + TurnIterator first, TurnIterator beyond, + OutputIterator oit) + { + BOOST_ASSERT( first != beyond ); + + typedef copy_linestrings_in_range + < + OutputIterator, OverlayType + > copy_linestrings; + + linestring_iterator ls_first = boost::begin(multilinestring); + linestring_iterator ls_beyond = boost::end(multilinestring); + + // Iterate through all intersection points (they are + // ordered along the each linestring) + + signed_index_type current_multi_id = get_multi_index(first); + + oit = copy_linestrings::apply(ls_first, + ls_first + current_multi_id, + oit); + + TurnIterator per_ls_next = first; + do { + TurnIterator per_ls_current = per_ls_next; + + // find turn with different multi-index + per_ls_next = std::find_if(per_ls_current, beyond, + has_other_multi_id(current_multi_id)); + + oit = Base::apply(*(ls_first + current_multi_id), + linear, per_ls_current, per_ls_next, oit); + + signed_index_type next_multi_id(-1); + linestring_iterator ls_next = ls_beyond; + if ( per_ls_next != beyond ) + { + next_multi_id = get_multi_index(per_ls_next); + ls_next = ls_first + next_multi_id; + } + oit = copy_linestrings::apply(ls_first + current_multi_id + 1, + ls_next, + oit); + + current_multi_id = next_multi_id; + } + while ( per_ls_next != beyond ); + + return oit; + } +}; + + + + + + +template +< + typename LinestringOut, + typename Geometry1, + typename Geometry2, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns, + typename TagOut = typename tag::type, + typename TagIn1 = typename tag::type +> +struct follow + : not_implemented +{}; + + + +template +< + typename LinestringOut, + typename Linestring, + typename Linear, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns +> +struct follow + < + LinestringOut, Linestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns, + linestring_tag, linestring_tag + > : follow_linestring_linear_linestring + < + LinestringOut, Linestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns + > +{}; + + +template +< + typename LinestringOut, + typename MultiLinestring, + typename Linear, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns +> +struct follow + < + LinestringOut, MultiLinestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns, + linestring_tag, multi_linestring_tag + > : follow_multilinestring_linear_linestring + < + LinestringOut, MultiLinestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns + > +{}; + + + +}} // namespace following::linear + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp index 019c3ba3f9..63011c7d48 100644 --- a/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp @@ -17,6 +17,7 @@ #include +#include namespace boost { namespace geometry { @@ -35,32 +36,45 @@ template > struct get_turn_without_info { - typedef strategy_intersection - < - typename cs_tag::type, - Point1, - Point2, - typename TurnInfo::point_type - > si; - - typedef typename si::segment_intersection_strategy_type strategy; - - - - template + template static inline OutputIterator apply( - Point1 const& pi, Point1 const& pj, Point1 const& pk, - Point2 const& qi, Point2 const& qj, Point2 const& qk, + Point1 const& pi, Point1 const& pj, Point1 const& /*pk*/, + Point2 const& qi, Point2 const& qj, Point2 const& /*qk*/, + bool /*is_p_first*/, bool /*is_p_last*/, + bool /*is_q_first*/, bool /*is_q_last*/, TurnInfo const& , + RobustPolicy const& robust_policy, OutputIterator out) { + typedef strategy_intersection + < + typename cs_tag::type, + Point1, + Point2, + typename TurnInfo::point_type, + RobustPolicy + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + typedef model::referring_segment segment_type1; typedef model::referring_segment segment_type2; - segment_type1 p1(pi, pj), p2(pj, pk); - segment_type2 q1(qi, qj), q2(qj, qk); + segment_type1 p1(pi, pj); + segment_type2 q1(qi, qj); - // - typename strategy::return_type result = strategy::apply(p1, q1); + typedef typename geometry::robust_point_type + < + Point1, RobustPolicy + >::type robust_point_type; + + robust_point_type pi_rob, pj_rob, qi_rob, qj_rob; + geometry::recalculate(pi_rob, pi, robust_policy); + geometry::recalculate(pj_rob, pj, robust_policy); + geometry::recalculate(qi_rob, qi, robust_policy); + geometry::recalculate(qj_rob, qj, robust_policy); + typename strategy::return_type result + = strategy::apply(p1, q1, robust_policy, + pi_rob, pj_rob, qi_rob, qj_rob); for (std::size_t i = 0; i < result.template get<0>().count; i++) { @@ -84,10 +98,12 @@ template < typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Turns > inline void get_intersection_points(Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, Turns& turns) { concept::check_concepts_and_equal_dimensions(); @@ -99,14 +115,6 @@ inline void get_intersection_points(Geometry1 const& geometry1, typename boost::range_value::type > TurnPolicy; - typedef typename strategy_intersection - < - typename cs_tag::type, - Geometry1, - Geometry2, - typename boost::range_value::type - >::segment_intersection_strategy_type segment_intersection_strategy_type; - detail::get_turns::no_interrupt_policy interrupt_policy; boost::mpl::if_c @@ -118,9 +126,7 @@ inline void get_intersection_points(Geometry1 const& geometry1, typename tag::type, Geometry1, Geometry2, false, false, - Turns, TurnPolicy, - //segment_intersection_strategy_type, - detail::get_turns::no_interrupt_policy + TurnPolicy >, dispatch::get_turns < @@ -128,13 +134,12 @@ inline void get_intersection_points(Geometry1 const& geometry1, typename tag::type, Geometry1, Geometry2, false, false, - Turns, TurnPolicy, - //segment_intersection_strategy_type, - detail::get_turns::no_interrupt_policy + TurnPolicy > >::type::apply( 0, geometry1, 1, geometry2, + robust_policy, turns, interrupt_policy); } diff --git a/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp index 522ef68382..d71f4ad51f 100644 --- a/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp @@ -10,8 +10,6 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP -#include - #include @@ -36,15 +34,10 @@ namespace detail { namespace overlay template struct get_relative_order { - typedef strategy_intersection + typedef typename strategy::side::services::default_strategy < - typename cs_tag::type, - Point1, - Point1, - Point1 - > si; - - typedef typename si::side_strategy_type strategy; + typename cs_tag::type + >::type strategy; template static inline int value_via_product(Point const& ti, Point const& tj, diff --git a/boost/geometry/algorithms/detail/overlay/get_ring.hpp b/boost/geometry/algorithms/detail/overlay/get_ring.hpp index c2c6980577..131d58d582 100644 --- a/boost/geometry/algorithms/detail/overlay/get_ring.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_ring.hpp @@ -13,11 +13,13 @@ #include #include - -#include #include #include +#include +#include #include +#include +#include namespace boost { namespace geometry @@ -33,16 +35,16 @@ template struct get_ring {}; -// A container of rings (multi-ring but that does not exist) +// A range of rings (multi-ring but that does not exist) // gets the "void" tag and is dispatched here. template<> struct get_ring { - template - static inline typename boost::range_value::type const& - apply(ring_identifier const& id, Container const& container) + template + static inline typename boost::range_value::type const& + apply(ring_identifier const& id, Range const& container) { - return container[id.multi_index]; + return range::at(container, id.multi_index); } }; @@ -87,7 +89,26 @@ struct get_ring ); return id.ring_index < 0 ? exterior_ring(polygon) - : interior_rings(polygon)[id.ring_index]; + : range::at(interior_rings(polygon), id.ring_index); + } +}; + + +template<> +struct get_ring +{ + template + static inline typename ring_type::type const& apply( + ring_identifier const& id, + MultiPolygon const& multi_polygon) + { + BOOST_ASSERT + ( + id.multi_index >= 0 + && id.multi_index < int(boost::size(multi_polygon)) + ); + return get_ring::apply(id, + range::at(multi_polygon, id.multi_index)); } }; diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index b8320d9b7b..240b6de036 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -16,9 +16,19 @@ #include #include +#include #include +#include +#include + +// Silence warning C4127: conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + namespace boost { namespace geometry { @@ -30,7 +40,7 @@ class turn_info_exception : public geometry::exception public: // NOTE: "char" will be replaced by enum in future version - inline turn_info_exception(char const method) + inline turn_info_exception(char const method) { message = "Boost.Geometry Turn exception: "; message += method; @@ -50,7 +60,6 @@ public: namespace detail { namespace overlay { - struct base_turn_handler { // Returns true if both sides are opposite @@ -91,13 +100,32 @@ struct base_turn_handler { both(ti, condition ? operation_union : operation_intersection); } + + template + static inline void assign_point(TurnInfo& ti, + method_type method, + IntersectionInfo const& info, int index) + { + ti.method = method; + BOOST_ASSERT(index >= 0 && unsigned(index) < info.count); // TODO remove this + geometry::convert(info.intersections[index], ti.point); + ti.operations[0].fraction = info.fractions[index].robust_ra; + ti.operations[1].fraction = info.fractions[index].robust_rb; + } + + template + static inline int non_opposite_to_index(IntersectionInfo const& info) + { + return info.fractions[0].robust_rb < info.fractions[1].robust_rb + ? 1 : 0; + } + }; template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct touch_interior : public base_turn_handler { @@ -108,17 +136,18 @@ struct touch_interior : public base_turn_handler typename Point1, typename Point2, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( - Point1 const& pi, Point1 const& pj, Point1 const& , - Point2 const& qi, Point2 const& qj, Point2 const& qk, + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , TurnInfo& ti, IntersectionInfo const& intersection_info, - DirInfo const& dir_info) + DirInfo const& dir_info, + SidePolicy const& side) { - ti.method = method_touch_interior; - geometry::convert(intersection_info.intersections[0], ti.point); + assign_point(ti, method_touch_interior, intersection_info, 0); // Both segments of q touch segment p somewhere in its interior // 1) We know: if q comes from LEFT or RIGHT @@ -130,7 +159,7 @@ struct touch_interior : public base_turn_handler static int const index_q = 1 - Index; int const side_qi_p = dir_info.sides.template get(); - int const side_qk_p = SideStrategy::apply(pi, pj, qk); + int const side_qk_p = side.qk_wrt_p1(); if (side_qi_p == -side_qk_p) { @@ -143,7 +172,7 @@ struct touch_interior : public base_turn_handler return; } - int const side_qk_q = SideStrategy::apply(qi, qj, qk); + int const side_qk_q = side.qk_wrt_q1(); if (side_qi_p == -1 && side_qk_p == -1 && side_qk_q == 1) { @@ -203,8 +232,7 @@ struct touch_interior : public base_turn_handler template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct touch : public base_turn_handler { @@ -227,37 +255,34 @@ struct touch : public base_turn_handler typename Point1, typename Point2, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( - Point1 const& pi, Point1 const& pj, Point1 const& pk, - Point2 const& qi, Point2 const& qj, Point2 const& qk, + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , TurnInfo& ti, IntersectionInfo const& intersection_info, - DirInfo const& dir_info) + DirInfo const& dir_info, + SidePolicy const& side) { - ti.method = method_touch; - geometry::convert(intersection_info.intersections[0], ti.point); + assign_point(ti, method_touch, intersection_info, 0); int const side_qi_p1 = dir_info.sides.template get<1, 0>(); - int const side_qk_p1 = SideStrategy::apply(pi, pj, qk); + int const side_qk_p1 = side.qk_wrt_p1(); // If Qi and Qk are both at same side of Pi-Pj, // or collinear (so: not opposite sides) if (! opposite(side_qi_p1, side_qk_p1)) { - 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; + int const side_pk_q2 = side.pk_wrt_q2(); + int const side_pk_p = side.pk_wrt_p1(); + int const side_qk_q = side.qk_wrt_q1(); bool const q_turns_left = side_qk_q == 1; bool const block_q = side_qk_p1 == 0 && ! same(side_qi_p1, side_qk_q) - && ! robustness_issue_in_continue ; // If Pk at same side as Qi/Qk @@ -276,7 +301,7 @@ struct touch : public base_turn_handler return; } - int const side_pk_q1 = SideStrategy::apply(qi, qj, pk); + int const side_pk_q1 = side.pk_wrt_q1(); // Collinear opposite case -> block P @@ -329,9 +354,6 @@ struct touch : public base_turn_handler else { // Pk at other side than Qi/Pk - int const side_qk_q = SideStrategy::apply(qi, qj, qk); - bool const q_turns_left = side_qk_q == 1; - ti.operations[0].operation = q_turns_left ? operation_intersection : operation_union; @@ -347,13 +369,13 @@ struct touch : public base_turn_handler else { // From left to right or from right to left - int const side_pk_p = SideStrategy::apply(pi, pj, pk); + int const side_pk_p = side.pk_wrt_p1(); bool const right_to_left = side_qk_p1 == 1; // If p turns into direction of qi (1,2) if (side_pk_p == side_qi_p1) { - int const side_pk_q1 = SideStrategy::apply(qi, qj, pk); + int const side_pk_q1 = side.pk_wrt_q1(); // Collinear opposite case -> block P if (side_pk_q1 == 0) @@ -374,7 +396,7 @@ struct touch : public base_turn_handler // If p turns into direction of qk (4,5) if (side_pk_p == side_qk_p1) { - int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); + int const side_pk_q2 = side.pk_wrt_q2(); // Collinear case -> lines join, continue if (side_pk_q2 == 0) @@ -413,8 +435,7 @@ struct touch : public base_turn_handler template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct equal : public base_turn_handler { @@ -423,22 +444,24 @@ struct equal : public base_turn_handler typename Point1, typename Point2, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( - Point1 const& pi, Point1 const& pj, Point1 const& pk, - Point2 const& , Point2 const& qj, Point2 const& qk, + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , TurnInfo& ti, - IntersectionInfo const& intersection_info, - DirInfo const& ) + IntersectionInfo const& info, + DirInfo const& , + SidePolicy const& side) { - ti.method = method_equal; - // Copy the SECOND intersection point - geometry::convert(intersection_info.intersections[1], ti.point); + // Copy the intersection point in TO direction + assign_point(ti, method_equal, info, non_opposite_to_index(info)); + + int const side_pk_q2 = side.pk_wrt_q2(); + int const side_pk_p = side.pk_wrt_p1(); + int const side_qk_p = side.qk_wrt_p1(); - int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); - int const side_pk_p = SideStrategy::apply(pi, pj, pk); - int const side_qk_p = SideStrategy::apply(pi, pj, qk); // If pk is collinear with qj-qk, they continue collinearly. // This can be on either side of p1 (== q1), or collinear @@ -447,6 +470,7 @@ struct equal : public base_turn_handler if (side_pk_q2 == 0 && side_pk_p == side_qk_p) { both(ti, operation_continue); + return; } @@ -454,8 +478,6 @@ struct equal : public base_turn_handler // If they turn to same side (not opposite sides) if (! opposite(side_pk_p, side_qk_p)) { - int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); - // If pk is left of q2 or collinear: p: union, q: intersection ui_else_iu(side_pk_q2 != -1, ti); } @@ -485,33 +507,32 @@ struct equal_opposite : public base_turn_handler typename DirInfo > static inline void apply(Point1 const& pi, Point2 const& qi, - /* by value: */ TurnInfo tp, + /* 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; - } - } + 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++) + { + assign_point(tp, method_none, intersection_info, i); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + *out++ = tp; + } + } } }; template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct collinear : public base_turn_handler { @@ -543,7 +564,7 @@ struct collinear : public base_turn_handler 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) + If p arrives, q is further, so the angle qk//p1 is (normally) more precise than pk//p1 */ @@ -552,24 +573,26 @@ struct collinear : public base_turn_handler typename Point1, typename Point2, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( - Point1 const& pi, Point1 const& pj, Point1 const& pk, - Point2 const& qi, Point2 const& qj, Point2 const& qk, + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , TurnInfo& ti, - IntersectionInfo const& intersection_info, - DirInfo const& dir_info) + IntersectionInfo const& info, + DirInfo const& dir_info, + SidePolicy const& side) { - ti.method = method_collinear; - geometry::convert(intersection_info.intersections[1], ti.point); + // Copy the intersection point in TO direction + assign_point(ti, method_collinear, info, non_opposite_to_index(info)); int const arrival = dir_info.arrival[0]; // 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); + int const side_p = side.pk_wrt_p1(); + int const side_q = side.qk_wrt_q1(); // If p arrives, use p, else use q int const side_p_or_q = arrival == 1 @@ -577,9 +600,6 @@ struct collinear : public base_turn_handler : 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 @@ -587,15 +607,7 @@ struct collinear : public base_turn_handler int const product = arrival * side_p_or_q; - // 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) + if(product == 0) { both(ti, operation_continue); } @@ -605,43 +617,11 @@ struct collinear : public base_turn_handler } } - 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 < typename TurnInfo, - typename SideStrategy, typename AssignPolicy > struct collinear_opposite : public base_turn_handler @@ -674,14 +654,19 @@ private : template < int Index, - typename Point, + typename Point1, + typename Point2, typename IntersectionInfo > - static inline bool set_tp(Point const& ri, Point const& rj, Point const& rk, + static inline bool set_tp(Point1 const& , Point1 const& , Point1 const& , int side_rk_r, + bool const handle_robustness, + Point2 const& , Point2 const& , int side_rk_s, TurnInfo& tp, IntersectionInfo const& intersection_info) { - int const side_rk_r = SideStrategy::apply(ri, rj, rk); - operation_type blocked = operation_blocked; + boost::ignore_unused_variable_warning(handle_robustness); + boost::ignore_unused_variable_warning(side_rk_s); + + operation_type blocked = operation_blocked; switch(side_rk_r) { @@ -699,16 +684,16 @@ private : // two operations blocked, so the whole point does not need // to be generated. // So return false to indicate nothing is to be done. - if (AssignPolicy::include_opposite) - { - tp.operations[Index].operation = operation_opposite; - blocked = operation_opposite; - } - else - { - return false; - } - break; + 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 @@ -717,18 +702,21 @@ private : // 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 // (see e.g. figure CLO1) - geometry::convert(intersection_info.intersections[1 - Index], tp.point); + assign_point(tp, method_collinear, intersection_info, 1 - Index); return true; } public: + static inline void empty_transformer(TurnInfo &) {} + template < typename Point1, typename Point2, typename OutputIterator, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( Point1 const& pi, Point1 const& pj, Point1 const& pk, @@ -739,46 +727,79 @@ public: OutputIterator& out, IntersectionInfo const& intersection_info, - DirInfo const& dir_info) + DirInfo const& dir_info, + SidePolicy const& side) { - TurnInfo tp = tp_model; + apply(pi, pj, pk, qi, qj, qk, tp_model, out, intersection_info, dir_info, side, empty_transformer); + } - tp.method = method_collinear; +public: + template + < + typename Point1, + typename Point2, + typename OutputIterator, + typename IntersectionInfo, + typename DirInfo, + typename SidePolicy, + typename TurnTransformer + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + + // Opposite collinear can deliver 2 intersection points, + TurnInfo const& tp_model, + OutputIterator& out, + + IntersectionInfo const& intersection_info, + DirInfo const& dir_info, + SidePolicy const& side, + TurnTransformer turn_transformer, + bool const is_pk_valid = true, bool const is_qk_valid = true) + { + TurnInfo tp = tp_model; // 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)) + if ( dir_info.arrival[0] == 1 + && is_pk_valid + && set_tp<0>(pi, pj, pk, side.pk_wrt_p1(), true, qi, qj, side.pk_wrt_q1(), tp, intersection_info) ) { + turn_transformer(tp); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); *out++ = tp; } // 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)) + if ( dir_info.arrival[1] == 1 + && is_qk_valid + && set_tp<1>(qi, qj, qk, side.qk_wrt_q1(), false, pi, pj, side.qk_wrt_p1(), tp, intersection_info) ) { + turn_transformer(tp); + 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; - } - } - } + 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++) + { + assign_point(tp, method_collinear, intersection_info, i); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + *out++ = tp; + } + } + } } }; @@ -786,8 +807,7 @@ public: template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct crosses : public base_turn_handler { @@ -805,8 +825,7 @@ struct crosses : public base_turn_handler IntersectionInfo const& intersection_info, DirInfo const& dir_info) { - ti.method = method_crosses; - geometry::convert(intersection_info.intersections[0], ti.point); + assign_point(ti, method_crosses, intersection_info, 0); // In all casees: // If Q crosses P from left to right @@ -820,14 +839,12 @@ struct crosses : public base_turn_handler } }; -template -struct only_convert +struct only_convert : public base_turn_handler { - template + template static inline void apply(TurnInfo& ti, IntersectionInfo const& intersection_info) { - ti.method = method_collinear; - geometry::convert(intersection_info.intersections[0], ti.point); + assign_point(ti, method_none, intersection_info, 0); // was collinear ti.operations[0].operation = operation_continue; ti.operations[1].operation = operation_continue; } @@ -845,14 +862,14 @@ struct assign_null_policy static bool const include_degenerate = false; static bool const include_opposite = false; - template - < - typename Info, - typename Point1, - typename Point2, - typename IntersectionInfo, - typename DirInfo - > + template + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > static inline void apply(Info& , Point1 const& , Point2 const&, IntersectionInfo const&, DirInfo const&) {} @@ -873,48 +890,39 @@ struct assign_null_policy It also defines if a certain class of points (degenerate, non-turns) should be included. */ -template -< - typename Point1, - typename Point2, - typename TurnInfo, - typename AssignPolicy -> +template struct get_turn_info { - typedef strategy_intersection - < - typename cs_tag::type, - Point1, - Point2, - typename TurnInfo::point_type - > si; - - 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 + // The points pk and qk are used do determine more information + // about the turn (turn left/right) + template + < + typename Point1, + typename Point2, + typename TurnInfo, + typename RobustPolicy, + typename OutputIterator + > static inline OutputIterator apply( Point1 const& pi, Point1 const& pj, Point1 const& pk, Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool /*is_p_first*/, bool /*is_p_last*/, + bool /*is_q_first*/, bool /*is_q_last*/, TurnInfo const& tp_model, + RobustPolicy const& robust_policy, OutputIterator out) { - typedef model::referring_segment segment_type1; - typedef model::referring_segment segment_type2; - segment_type1 p1(pi, pj), p2(pj, pk); - segment_type2 q1(qi, qj), q2(qj, qk); + typedef intersection_info + inters_info; - typename strategy::return_type result = strategy::apply(p1, q1); + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); - char const method = result.template get<1>().how; + char const method = inters.d_info().how; // Copy, to copy possibly extended fields TurnInfo tp = tp_model; - // Select method and apply switch(method) { @@ -922,11 +930,10 @@ struct get_turn_info case 'f' : // collinear, "from" case 's' : // starts from the middle if (AssignPolicy::include_no_turn - && result.template get<0>().count > 0) + && inters.i_info().count > 0) { - only_convert::apply(tp, - result.template get<0>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + only_convert::apply(tp, inters.i_info()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; @@ -938,113 +945,94 @@ struct get_turn_info { typedef touch_interior < - TurnInfo, - typename si::side_strategy_type + TurnInfo > policy; // If Q (1) arrives (1) - if (result.template get<1>().arrival[1] == 1) + if ( inters.d_info().arrival[1] == 1 ) { policy::template apply<0>(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); + tp, inters.i_info(), inters.d_info(), + inters.sides()); } else { // Swap p/q + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); policy::template apply<1>(qi, qj, qk, pi, pj, pk, - tp, result.template get<0>(), result.template get<1>()); + tp, inters.i_info(), inters.d_info(), + swapped_side_calc); } - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; case 'i' : { - typedef crosses - < - TurnInfo, - typename si::side_strategy_type - > policy; - - policy::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + crosses::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; case 't' : { // Both touch (both arrive there) - typedef touch - < - TurnInfo, - typename si::side_strategy_type - > policy; - - policy::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + touch::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; case 'e': { - if (! result.template get<1>().opposite) + if ( ! inters.d_info().opposite ) { // Both equal // or collinear-and-ending at intersection point - typedef equal - < - TurnInfo, - typename si::side_strategy_type - > policy; - - policy::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } - else - { + else + { equal_opposite < TurnInfo, AssignPolicy >::apply(pi, qi, - tp, out, result.template get<0>(), result.template get<1>()); - } + tp, out, inters.i_info(), inters.d_info()); + } } break; case 'c' : { // Collinear - if (! result.template get<1>().opposite) + if ( ! inters.d_info().opposite ) { - if (result.template get<1>().arrival[0] == 0) + if ( inters.d_info().arrival[0] == 0 ) { // Collinear, but similar thus handled as equal - equal - < - TurnInfo, - typename si::side_strategy_type - >::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); // override assigned method tp.method = method_collinear; } else { - collinear - < - TurnInfo, - typename si::side_strategy_type - >::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); + collinear::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); } - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } else @@ -1052,10 +1040,9 @@ struct get_turn_info collinear_opposite < TurnInfo, - typename si::side_strategy_type, AssignPolicy >::apply(pi, pj, pk, qi, qj, qk, - tp, out, result.template get<0>(), result.template get<1>()); + tp, out, inters.i_info(), inters.d_info(), inters.sides()); } } break; @@ -1064,14 +1051,17 @@ struct get_turn_info // degenerate points if (AssignPolicy::include_degenerate) { - only_convert::apply(tp, result.template get<0>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + only_convert::apply(tp, inters.i_info()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } } break; default : { +#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) + std::cout << "TURN: Unknown method: " << method << std::endl; +#endif #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) throw turn_info_exception(method); #endif @@ -1091,4 +1081,8 @@ struct get_turn_info }} // namespace boost::geometry +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp new file mode 100644 index 0000000000..ca1b9d9d0c --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp @@ -0,0 +1,657 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP + +#include +#include + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +// SEGMENT_INTERSECTION RESULT + +// C H0 H1 A0 A1 O IP1 IP2 + +// D0 and D1 == 0 + +// |--------> 2 0 0 0 0 F i/i x/x +// |--------> +// +// |--------> 2 0 0 0 0 T i/x x/i +// <--------| +// +// |-----> 1 0 0 0 0 T x/x +// <-----| +// + +// |---------> 2 0 0 0 1 T i/x x/i +// <----| +// +// |---------> 2 0 0 0 0 F i/i x/x +// |----> +// +// |---------> 2 0 0 -1 1 F i/i u/x +// |----> +// +// |---------> 2 0 0 -1 0 T i/x u/i +// <----| + +// |-------> 2 0 0 1 -1 F and i/i x/u +// |-------> 2 0 0 -1 1 F symetric i/i u/x +// |-------> +// +// |-------> 2 0 0 -1 -1 T i/u u/i +// <-------| +// +// |-------> 2 0 0 1 1 T i/x x/i +// <-------| +// +// |--------> 2 0 0 -1 1 F i/i u/x +// |----> +// +// |--------> 2 0 0 -1 1 T i/x u/i +// <----| + +// |-----> 1 -1 -1 -1 -1 T u/u +// <-----| +// +// |-----> 1 -1 0 -1 0 F and u/x +// |-----> 1 0 -1 0 -1 F symetric x/u +// |-----> + +// D0 or D1 != 0 + +// ^ +// | +// + 1 -1 1 -1 1 F and u/x (P is vertical) +// |--------> 1 1 -1 1 -1 F symetric x/u (P is horizontal) +// ^ +// | +// + +// +// + +// | +// v +// |--------> 1 1 1 1 1 F x/x (P is vertical) +// +// ^ +// | +// + +// |--------> 1 -1 -1 -1 -1 F u/u (P is vertical) +// +// ^ +// | +// + +// |--------> 1 0 -1 0 -1 F u/u (P is vertical) +// +// + +// | +// v +// |--------> 1 0 1 0 1 F u/x (P is vertical) +// + +class linear_intersections +{ +public: + template + linear_intersections(Point1 const& pi, + Point2 const& qi, + IntersectionResult const& result, + bool is_p_last, bool is_q_last) + { + int arrival_a = result.template get<1>().arrival[0]; + int arrival_b = result.template get<1>().arrival[1]; + bool same_dirs = result.template get<1>().dir_a == 0 + && result.template get<1>().dir_b == 0; + + if ( same_dirs ) + { + if ( result.template get<0>().count == 2 ) + { + if ( ! result.template get<1>().opposite ) + { + ips[0].p_operation = operation_intersection; + ips[0].q_operation = operation_intersection; + ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last); + ips[1].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last); + + ips[0].is_pi + = equals::equals_point_point( + pi, result.template get<0>().intersections[0]); + ips[0].is_qi + = equals::equals_point_point( + qi, result.template get<0>().intersections[0]); + ips[1].is_pj = arrival_a != -1; + ips[1].is_qj = arrival_b != -1; + } + else + { + ips[0].p_operation = operation_intersection; + ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last); + ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last); + ips[1].q_operation = operation_intersection; + + ips[0].is_pi = arrival_b != 1; + ips[0].is_qj = arrival_b != -1; + ips[1].is_pj = arrival_a != -1; + ips[1].is_qi = arrival_a != 1; + } + } + else + { + BOOST_ASSERT(result.template get<0>().count == 1); + ips[0].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last); + ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last); + + ips[0].is_pi = arrival_a == -1; + ips[0].is_qi = arrival_b == -1; + ips[0].is_pj = arrival_a == 0; + ips[0].is_qj = arrival_b == 0; + } + } + else + { + ips[0].p_operation = union_or_blocked_different_dirs(arrival_a, is_p_last); + ips[0].q_operation = union_or_blocked_different_dirs(arrival_b, is_q_last); + + ips[0].is_pi = arrival_a == -1; + ips[0].is_qi = arrival_b == -1; + ips[0].is_pj = arrival_a == 1; + ips[0].is_qj = arrival_b == 1; + } + } + + struct ip_info + { + inline ip_info() + : p_operation(operation_none), q_operation(operation_none) + , is_pi(false), is_pj(false), is_qi(false), is_qj(false) + {} + + operation_type p_operation, q_operation; + bool is_pi, is_pj, is_qi, is_qj; + }; + + template + ip_info const& get() const + { + BOOST_STATIC_ASSERT(I < 2); + return ips[I]; + } + +private: + + // only if collinear (same_dirs) + static inline operation_type union_or_blocked_same_dirs(int arrival, bool is_last) + { + if ( arrival == 1 ) + return operation_blocked; + else if ( arrival == -1 ) + return operation_union; + else + return is_last ? operation_blocked : operation_union; + //return operation_blocked; + } + + // only if not collinear (!same_dirs) + static inline operation_type union_or_blocked_different_dirs(int arrival, bool is_last) + { + if ( arrival == 1 ) + //return operation_blocked; + return is_last ? operation_blocked : operation_union; + else + return operation_union; + } + + ip_info ips[2]; +}; + +template +struct get_turn_info_for_endpoint +{ + BOOST_STATIC_ASSERT(EnableFirst || EnableLast); + + template + static inline bool apply(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + TurnInfo const& tp_model, + IntersectionInfo const& inters, + method_type /*method*/, + OutputIterator out) + { + std::size_t ip_count = inters.i_info().count; + // no intersection points + if ( ip_count == 0 ) + return false; + + if ( !is_p_first && !is_p_last && !is_q_first && !is_q_last ) + return false; + + linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last); + + bool append0_last + = analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + intersections.template get<0>(), + tp_model, inters, 0, out); + + // NOTE: opposite && ip_count == 1 may be true! + bool opposite = inters.d_info().opposite; + + // don't ignore only for collinear opposite + bool result_ignore_ip0 = append0_last && ( ip_count == 1 || !opposite ); + + if ( intersections.template get<1>().p_operation == operation_none ) + return result_ignore_ip0; + + bool append1_last + = analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + intersections.template get<1>(), + tp_model, inters, 1, out); + + // don't ignore only for collinear opposite + bool result_ignore_ip1 = append1_last && !opposite /*&& ip_count == 2*/; + + return result_ignore_ip0 || result_ignore_ip1; + } + + template + static inline + bool analyse_segment_and_assign_ip(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + linear_intersections::ip_info const& ip_info, + TurnInfo const& tp_model, + IntersectionInfo const& inters, + int ip_index, + OutputIterator out) + { +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR + // may this give false positives for INTs? + typename IntersectionResult::point_type const& + inters_pt = result.template get<0>().intersections[ip_index]; + BOOST_ASSERT(ip_info.is_pi == equals::equals_point_point(pi, inters_pt)); + BOOST_ASSERT(ip_info.is_qi == equals::equals_point_point(qi, inters_pt)); + BOOST_ASSERT(ip_info.is_pj == equals::equals_point_point(pj, inters_pt)); + BOOST_ASSERT(ip_info.is_qj == equals::equals_point_point(qj, inters_pt)); +#endif + + // TODO - calculate first/last only if needed + bool is_p_first_ip = is_p_first && ip_info.is_pi; + bool is_p_last_ip = is_p_last && ip_info.is_pj; + bool is_q_first_ip = is_q_first && ip_info.is_qi; + bool is_q_last_ip = is_q_last && ip_info.is_qj; + bool append_first = EnableFirst && (is_p_first_ip || is_q_first_ip); + bool append_last = EnableLast && (is_p_last_ip || is_q_last_ip); + + operation_type p_operation = ip_info.p_operation; + operation_type q_operation = ip_info.q_operation; + + if ( append_first || append_last ) + { + bool handled = handle_internal<0>(pi, pj, pk, qi, qj, qk, + inters.rpi(), inters.rpj(), inters.rpk(), + inters.rqi(), inters.rqj(), inters.rqk(), + is_p_first_ip, is_p_last_ip, + is_q_first_ip, is_q_last_ip, + ip_info.is_qi, ip_info.is_qj, + tp_model, inters, ip_index, + p_operation, q_operation); + if ( !handled ) + { + handle_internal<1>(qi, qj, qk, pi, pj, pk, + inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk(), + is_q_first_ip, is_q_last_ip, + is_p_first_ip, is_p_last_ip, + ip_info.is_pi, ip_info.is_pj, + tp_model, inters, ip_index, + q_operation, p_operation); + } + + if ( p_operation != operation_none ) + { + method_type method = endpoint_ip_method(ip_info.is_pi, ip_info.is_pj, + ip_info.is_qi, ip_info.is_qj); + turn_position p_pos = ip_position(is_p_first_ip, is_p_last_ip); + turn_position q_pos = ip_position(is_q_first_ip, is_q_last_ip); + + // handle spikes + + // P is spike and should be handled + if ( !is_p_last + && ip_info.is_pj // this check is redundant (also in is_spike_p) but faster + && inters.i_info().count == 2 + && inters.is_spike_p() ) + { + assign(pi, qi, inters.result(), ip_index, method, operation_blocked, q_operation, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out); + assign(pi, qi, inters.result(), ip_index, method, operation_intersection, q_operation, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out); + } + // Q is spike and should be handled + else if ( !is_q_last + && ip_info.is_qj // this check is redundant (also in is_spike_q) but faster + && inters.i_info().count == 2 + && inters.is_spike_q() ) + { + assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_blocked, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out); + assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_intersection, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out); + } + // no spikes + else + { + assign(pi, qi, inters.result(), ip_index, method, p_operation, q_operation, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, false, tp_model, out); + } + } + } + + return append_last; + } + + // TODO: IT'S ALSO PROBABLE THAT ALL THIS FUNCTION COULD BE INTEGRATED WITH handle_segment + // however now it's lazily calculated and then it would be always calculated + + template + static inline bool handle_internal(Point1 const& /*i1*/, Point1 const& /*j1*/, Point1 const& /*k1*/, + Point2 const& i2, Point2 const& j2, Point2 const& /*k2*/, + RobustPoint1 const& ri1, RobustPoint1 const& rj1, RobustPoint1 const& /*rk1*/, + RobustPoint2 const& ri2, RobustPoint2 const& rj2, RobustPoint2 const& rk2, + bool first1, bool last1, bool first2, bool last2, + bool ip_i2, bool ip_j2, TurnInfo const& tp_model, + IntersectionInfo const& inters, int ip_index, + operation_type & op1, operation_type & op2) + { + boost::ignore_unused_variable_warning(i2); + boost::ignore_unused_variable_warning(j2); + boost::ignore_unused_variable_warning(ip_index); + boost::ignore_unused_variable_warning(tp_model); + + if ( !first2 && !last2 ) + { + if ( first1 ) + { +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR + // may this give false positives for INTs? + typename IntersectionResult::point_type const& + inters_pt = inters.i_info().intersections[ip_index]; + BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt)); + BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt)); +#endif + if ( ip_i2 ) + { + // don't output this IP - for the first point of other geometry segment + op1 = operation_none; + op2 = operation_none; + return true; + } + else if ( ip_j2 ) + { + side_calculator + side_calc(ri2, ri1, rj1, ri2, rj2, rk2); + + std::pair + operations = operations_of_equal(side_calc); + +// TODO: must the above be calculated? +// wouldn't it be enough to check if segments are collinear? + + if ( operations_both(operations, operation_continue) ) + { + if ( op1 != operation_union + || op2 != operation_union + || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) ) + { + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! + bool opposite = inters.d_info().opposite; + + op1 = operation_intersection; + op2 = opposite ? operation_union : operation_intersection; + } + } + else + { + BOOST_ASSERT(operations_combination(operations, operation_intersection, operation_union)); + //op1 = operation_union; + //op2 = operation_union; + } + + return true; + } + // else do nothing - shouldn't be handled this way + } + else if ( last1 ) + { +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR + // may this give false positives for INTs? + typename IntersectionResult::point_type const& + inters_pt = inters.i_info().intersections[ip_index]; + BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt)); + BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt)); +#endif + if ( ip_i2 ) + { + // don't output this IP - for the first point of other geometry segment + op1 = operation_none; + op2 = operation_none; + return true; + } + else if ( ip_j2 ) + { + side_calculator + side_calc(ri2, rj1, ri1, ri2, rj2, rk2); + + std::pair + operations = operations_of_equal(side_calc); + +// TODO: must the above be calculated? +// wouldn't it be enough to check if segments are collinear? + + if ( operations_both(operations, operation_continue) ) + { + if ( op1 != operation_blocked + || op2 != operation_union + || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) ) + { + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! + bool second_going_out = inters.i_info().count > 1; + + op1 = operation_blocked; + op2 = second_going_out ? operation_union : operation_intersection; + } + } + else + { + BOOST_ASSERT(operations_combination(operations, operation_intersection, operation_union)); + //op1 = operation_blocked; + //op2 = operation_union; + } + + return true; + } + // else do nothing - shouldn't be handled this way + } + // else do nothing - shouldn't be handled this way + } + + return false; + } + + static inline method_type endpoint_ip_method(bool ip_pi, bool ip_pj, bool ip_qi, bool ip_qj) + { + if ( (ip_pi || ip_pj) && (ip_qi || ip_qj) ) + return method_touch; + else + return method_touch_interior; + } + + static inline turn_position ip_position(bool is_ip_first_i, bool is_ip_last_j) + { + return is_ip_first_i ? position_front : + ( is_ip_last_j ? position_back : position_middle ); + } + + template + static inline void assign(Point1 const& pi, Point2 const& qi, + IntersectionResult const& result, + int ip_index, + method_type method, + operation_type op0, operation_type op1, + turn_position pos0, turn_position pos1, + bool is_p_first_ip, bool is_q_first_ip, + bool is_p_spike, bool is_q_spike, + TurnInfo const& tp_model, + OutputIterator out) + { + TurnInfo tp = tp_model; + + //geometry::convert(ip, tp.point); + //tp.method = method; + base_turn_handler::assign_point(tp, method, result.template get<0>(), ip_index); + + tp.operations[0].operation = op0; + tp.operations[1].operation = op1; + tp.operations[0].position = pos0; + tp.operations[1].position = pos1; + + if ( result.template get<0>().count > 1 ) + { + // NOTE: is_collinear is NOT set for the first endpoint + // for which there is no preceding segment + + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); + if ( ! is_p_first_ip ) + { + tp.operations[0].is_collinear = op0 != operation_intersection + || is_p_spike; + } + + if ( ! is_q_first_ip ) + { + tp.operations[1].is_collinear = op1 != operation_intersection + || is_q_spike; + } + } + else //if ( result.template get<0>().count == 1 ) + { + if ( op0 == operation_blocked && op1 == operation_intersection ) + { + tp.operations[0].is_collinear = true; + } + else if ( op0 == operation_intersection && op1 == operation_blocked ) + { + tp.operations[1].is_collinear = true; + } + } + + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + *out++ = tp; + } + + template + static inline std::pair operations_of_equal(SidePolicy const& side) + { + int const side_pk_q2 = side.pk_wrt_q2(); + int const side_pk_p = side.pk_wrt_p1(); + int const side_qk_p = side.qk_wrt_p1(); + + // If pk is collinear with qj-qk, they continue collinearly. + // This can be on either side of p1 (== q1), or collinear + // The second condition checks if they do not continue + // oppositely + if ( side_pk_q2 == 0 && side_pk_p == side_qk_p ) + { + return std::make_pair(operation_continue, operation_continue); + } + + // If they turn to same side (not opposite sides) + if ( ! base_turn_handler::opposite(side_pk_p, side_qk_p) ) + { + // If pk is left of q2 or collinear: p: union, q: intersection + if ( side_pk_q2 != -1 ) + { + return std::make_pair(operation_union, operation_intersection); + } + else + { + return std::make_pair(operation_intersection, operation_union); + } + } + else + { + // They turn opposite sides. If p turns left (or collinear), + // p: union, q: intersection + if ( side_pk_p != -1 ) + { + return std::make_pair(operation_union, operation_intersection); + } + else + { + return std::make_pair(operation_intersection, operation_union); + } + } + } + + static inline bool operations_both( + std::pair const& operations, + operation_type const op) + { + return operations.first == op && operations.second == op; + } + + static inline bool operations_combination( + std::pair const& operations, + operation_type const op1, operation_type const op2) + { + return ( operations.first == op1 && operations.second == op2 ) + || ( operations.first == op2 && operations.second == op1 ); + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp new file mode 100644 index 0000000000..eead0d719b --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -0,0 +1,329 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP + +#include + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +enum turn_position { position_middle, position_front, position_back }; + +template +struct turn_operation_linear + : public turn_operation +{ + turn_operation_linear() + : position(position_middle) + , is_collinear(false) + {} + + turn_position position; + bool is_collinear; // valid only for Linear geometry +}; + +template +struct side_calculator +{ + typedef boost::geometry::strategy::side::side_by_triangle<> side; // todo: get from coordinate system + + inline side_calculator(Pi const& pi, Pj const& pj, Pk const& pk, + Qi const& qi, Qj const& qj, Qk const& qk) + : m_pi(pi), m_pj(pj), m_pk(pk) + , m_qi(qi), m_qj(qj), m_qk(qk) + {} + + inline int pk_wrt_p1() const { return side::apply(m_pi, m_pj, m_pk); } + inline int pk_wrt_q1() const { return side::apply(m_qi, m_qj, m_pk); } + inline int qk_wrt_p1() const { return side::apply(m_pi, m_pj, m_qk); } + inline int qk_wrt_q1() const { return side::apply(m_qi, m_qj, m_qk); } + + inline int pk_wrt_q2() const { return side::apply(m_qj, m_qk, m_pk); } + inline int qk_wrt_p2() const { return side::apply(m_pj, m_pk, m_qk); } + + Pi const& m_pi; + Pj const& m_pj; + Pk const& m_pk; + Qi const& m_qi; + Qj const& m_qj; + Qk const& m_qk; +}; + +template +struct robust_points +{ + typedef typename geometry::robust_point_type + < + Point1, RobustPolicy + >::type robust_point1_type; + // TODO: define robust_point2_type using Point2? + typedef robust_point1_type robust_point2_type; + + inline robust_points(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + { + geometry::recalculate(m_rpi, pi, robust_policy); + geometry::recalculate(m_rpj, pj, robust_policy); + geometry::recalculate(m_rpk, pk, robust_policy); + geometry::recalculate(m_rqi, qi, robust_policy); + geometry::recalculate(m_rqj, qj, robust_policy); + geometry::recalculate(m_rqk, qk, robust_policy); + } + + robust_point1_type m_rpi, m_rpj, m_rpk; + robust_point2_type m_rqi, m_rqj, m_rqk; +}; + +template +class intersection_info_base + : private robust_points +{ + typedef robust_points base_t; + +public: + typedef Point1 point1_type; + typedef Point2 point2_type; + + typedef typename base_t::robust_point1_type robust_point1_type; + typedef typename base_t::robust_point2_type robust_point2_type; + + typedef side_calculator side_calculator_type; + + intersection_info_base(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + : base_t(pi, pj, pk, qi, qj, qk, robust_policy) + , m_side_calc(base_t::m_rpi, base_t::m_rpj, base_t::m_rpk, + base_t::m_rqi, base_t::m_rqj, base_t::m_rqk) + , m_pi(pi), m_pj(pj), m_pk(pk) + , m_qi(qi), m_qj(qj), m_qk(qk) + {} + + inline Point1 const& pi() const { return m_pi; } + inline Point1 const& pj() const { return m_pj; } + inline Point1 const& pk() const { return m_pk; } + + inline Point2 const& qi() const { return m_qi; } + inline Point2 const& qj() const { return m_qj; } + inline Point2 const& qk() const { return m_qk; } + + inline robust_point1_type const& rpi() const { return base_t::m_rpi; } + inline robust_point1_type const& rpj() const { return base_t::m_rpj; } + inline robust_point1_type const& rpk() const { return base_t::m_rpk; } + + inline robust_point2_type const& rqi() const { return base_t::m_rqi; } + inline robust_point2_type const& rqj() const { return base_t::m_rqj; } + inline robust_point2_type const& rqk() const { return base_t::m_rqk; } + + inline side_calculator_type const& sides() const { return m_side_calc; } + +private: + side_calculator_type m_side_calc; + + point1_type const& m_pi; + point1_type const& m_pj; + point1_type const& m_pk; + point2_type const& m_qi; + point2_type const& m_qj; + point2_type const& m_qk; +}; + +template +class intersection_info_base +{ +public: + typedef Point1 point1_type; + typedef Point2 point2_type; + + typedef Point1 robust_point1_type; + typedef Point2 robust_point2_type; + + typedef side_calculator side_calculator_type; + + intersection_info_base(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + no_rescale_policy const& /*robust_policy*/) + : m_side_calc(pi, pj, pk, qi, qj, qk) + {} + + inline Point1 const& pi() const { return m_side_calc.m_pi; } + inline Point1 const& pj() const { return m_side_calc.m_pj; } + inline Point1 const& pk() const { return m_side_calc.m_pk; } + + inline Point2 const& qi() const { return m_side_calc.m_qi; } + inline Point2 const& qj() const { return m_side_calc.m_qj; } + inline Point2 const& qk() const { return m_side_calc.m_qk; } + + inline Point1 const& rpi() const { return pi(); } + inline Point1 const& rpj() const { return pj(); } + inline Point1 const& rpk() const { return pk(); } + + inline Point2 const& rqi() const { return qi(); } + inline Point2 const& rqj() const { return qj(); } + inline Point2 const& rqk() const { return qk(); } + + inline side_calculator_type const& sides() const { return m_side_calc; } + +private: + side_calculator_type m_side_calc; +}; + + +template +class intersection_info + : public intersection_info_base +{ + typedef intersection_info_base base_t; + + typedef typename strategy_intersection + < + typename cs_tag::type, + Point1, + Point2, + TurnPoint, + RobustPolicy + >::segment_intersection_strategy_type strategy; + +public: + typedef model::referring_segment segment_type1; + typedef model::referring_segment segment_type2; + typedef typename base_t::side_calculator_type side_calculator_type; + + typedef typename strategy::return_type result_type; + typedef typename boost::tuples::element<0, result_type>::type i_info_type; // intersection_info + typedef typename boost::tuples::element<1, result_type>::type d_info_type; // dir_info + + intersection_info(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + : base_t(pi, pj, pk, qi, qj, qk, robust_policy) + , m_result(strategy::apply(segment_type1(pi, pj), + segment_type2(qi, qj), + robust_policy)) + , m_robust_policy(robust_policy) + {} + + inline result_type const& result() const { return m_result; } + inline i_info_type const& i_info() const { return m_result.template get<0>(); } + inline d_info_type const& d_info() const { return m_result.template get<1>(); } + + // TODO: it's more like is_spike_ip_p + inline bool is_spike_p() const + { + if ( base_t::sides().pk_wrt_p1() == 0 ) + { + if ( ! is_ip_j<0>() ) + return false; + + int const qk_p1 = base_t::sides().qk_wrt_p1(); + int const qk_p2 = base_t::sides().qk_wrt_p2(); + + if ( qk_p1 == -qk_p2 ) + { + if ( qk_p1 == 0 ) + { + return is_spike_of_collinear(base_t::pi(), base_t::pj(), base_t::pk()); + } + + return true; + } + } + + return false; + } + + // TODO: it's more like is_spike_ip_q + inline bool is_spike_q() const + { + if ( base_t::sides().qk_wrt_q1() == 0 ) + { + if ( ! is_ip_j<1>() ) + return false; + + int const pk_q1 = base_t::sides().pk_wrt_q1(); + int const pk_q2 = base_t::sides().pk_wrt_q2(); + + if ( pk_q1 == -pk_q2 ) + { + if ( pk_q1 == 0 ) + { + return is_spike_of_collinear(base_t::qi(), base_t::qj(), base_t::qk()); + } + + return true; + } + } + + return false; + } + +private: + template + inline bool is_spike_of_collinear(Point const& i, Point const& j, Point const& k) const + { + typedef model::referring_segment seg_t; + + typedef strategy_intersection + < + typename cs_tag::type, Point, Point, Point, RobustPolicy + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + + typename strategy::return_type result + = strategy::apply(seg_t(i, j), seg_t(j, k), m_robust_policy); + + return result.template get<0>().count == 2; + } + + template + bool is_ip_j() const + { + int arrival = d_info().arrival[OpId]; + bool same_dirs = d_info().dir_a == 0 && d_info().dir_b == 0; + + if ( same_dirs ) + { + if ( i_info().count == 2 ) + { + return arrival != -1; + } + else + { + return arrival == 0; + } + } + else + { + return arrival == 1; + } + } + + result_type m_result; + RobustPolicy const& m_robust_policy; +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp new file mode 100644 index 0000000000..873567bbf5 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp @@ -0,0 +1,805 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP + +#include +#include + +// TEMP, for spikes detector +//#include + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +template +struct get_turn_info_linear_areal +{ + static const bool handle_spikes = true; + + template + < + typename Point1, + typename Point2, + typename TurnInfo, + typename RobustPolicy, + typename OutputIterator + > + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + TurnInfo const& tp_model, + RobustPolicy const& robust_policy, + OutputIterator out) + { + typedef intersection_info + inters_info; + + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); + + char const method = inters.d_info().how; + + // Copy, to copy possibly extended fields + TurnInfo tp = tp_model; + + // Select method and apply + switch(method) + { + case 'a' : // collinear, "at" + case 'f' : // collinear, "from" + case 's' : // starts from the middle + get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_none, out); + break; + + case 'd' : // disjoint: never do anything + break; + + case 'm' : + { + if ( get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch_interior, out) ) + { + // do nothing + } + else + { + typedef touch_interior + < + TurnInfo + > policy; + + // If Q (1) arrives (1) + if ( inters.d_info().arrival[1] == 1 ) + { + policy::template apply<0>(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), + inters.sides()); + } + else + { + // Swap p/q + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); + policy::template apply<1>(qi, qj, qk, pi, pj, pk, + tp, inters.i_info(), inters.d_info(), + swapped_side_calc); + } + + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + + // this function assumes that 'u' must be set for a spike + calculate_spike_operation(tp.operations[0].operation, + inters, is_p_last); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + *out++ = tp; + } + } + break; + case 'i' : + { + crosses::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info()); + + replace_operations_i(tp.operations[0].operation, tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + break; + case 't' : + { + // Both touch (both arrive there) + if ( get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch, out) ) + { + // do nothing + } + else + { + touch::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + + // workarounds for touch<> not taking spikes into account starts here + // those was discovered empirically + // touch<> is not symmetrical! + // P spikes and Q spikes may produce various operations! + // Only P spikes are valid for L/A + // TODO: this is not optimal solution - think about rewriting touch<> + + if ( tp.operations[0].operation == operation_blocked ) + { + // a spike on P on the same line with Q1 + if ( inters.is_spike_p() ) + { + if ( inters.sides().qk_wrt_p1() == 0 ) + { + tp.operations[0].is_collinear = true; + } + else + { + tp.operations[0].operation = operation_union; + } + } + } + else if ( tp.operations[0].operation == operation_continue + && tp.operations[1].operation == operation_continue ) + { + // P spike on the same line with Q2 (opposite) + if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1() + && inters.is_spike_p() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + } + else if ( tp.operations[0].operation == operation_none + && tp.operations[1].operation == operation_none ) + { + // spike not handled by touch<> + if ( inters.is_spike_p() ) + { + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_union; + + if ( inters.sides().pk_wrt_q2() == 0 ) + { + tp.operations[0].operation = operation_continue; // will be converted to i + tp.operations[0].is_collinear = true; + } + } + } + + // workarounds for touch<> not taking spikes into account ends here + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + + bool ignore_spike + = calculate_spike_operation(tp.operations[0].operation, + inters, is_p_last); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + if ( ! handle_spikes + || ignore_spike + || ! append_opposite_spikes( // for 'i' or 'c' i??? + tp, inters, is_p_last, is_q_last, out) ) + { + *out++ = tp; + } + } + } + break; + case 'e': + { + if ( get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_equal, out) ) + { + // do nothing + } + else + { + tp.operations[0].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + // Both equal + // or collinear-and-ending at intersection point + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + turn_transformer_ec transformer(method_touch); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, + method_touch, append_equal, out) ) + { + *out++ = tp; // no spikes + } + } + else + { + equal_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, qi, + tp, out, inters.i_info(), inters.d_info()); + } + } + } + break; + case 'c' : + { + // Collinear + if ( get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_collinear, out) ) + { + // do nothing + } + else + { + tp.operations[0].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + method_type method_replace = method_touch_interior; + append_version_c version = append_collinear; + + if ( inters.d_info().arrival[0] == 0 ) + { + // Collinear, but similar thus handled as equal + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + method_replace = method_touch; + version = append_equal; + } + else + { + collinear::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + //method_replace = method_touch_interior; + //version = append_collinear; + } + + turn_transformer_ec transformer(method_replace); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, + method_replace, version, out) ) + { + // no spikes + *out++ = tp; + } + } + else + { + // Is this always 'm' ? + turn_transformer_ec transformer(method_touch_interior); + + // conditionally handle spikes + if ( handle_spikes ) + { + append_opposite_spikes( + tp, inters, is_p_last, is_q_last, out); + } + + // TODO: ignore for spikes? + // E.g. pass is_p_valid = !is_p_last && !is_pj_spike, + // the same with is_q_valid + + collinear_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, pj, pk, qi, qj, qk, + tp, out, inters.i_info(), inters.d_info(), + inters.sides(), transformer); + } + } + } + break; + case '0' : + { + // degenerate points + if (AssignPolicy::include_degenerate) + { + only_convert::apply(tp, inters.i_info()); + + if ( is_p_first + && equals::equals_point_point(pi, tp.point) ) + { + tp.operations[0].position = position_front; + } + else if ( is_p_last + && equals::equals_point_point(pj, tp.point) ) + { + tp.operations[0].position = position_back; + } + // tp.operations[1].position = position_middle; + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + } + break; + default : + { +#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) + std::cout << "TURN: Unknown method: " << method << std::endl; +#endif +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw turn_info_exception(method); +#endif + } + break; + } + + return out; + } + + template + static inline bool calculate_spike_operation(Operation & op, + IntersectionInfo const& inters, + bool is_p_last) + { + bool is_p_spike = ( op == operation_union || op == operation_intersection ) + && ! is_p_last + && inters.is_spike_p(); + + if ( is_p_spike ) + { + bool going_in = false, going_out = false; + + int const pk_q1 = inters.sides().pk_wrt_q1(); + int const pk_q2 = inters.sides().pk_wrt_q2(); + + if ( inters.sides().qk_wrt_q1() <= 0 ) // Q turning R or C + { + going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both + going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them + } + else + { + going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them + going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both + } + + if ( going_in ) + { + op = operation_intersection; + return true; + } + else if ( going_out ) + { + op = operation_union; + return true; + } + } + + return false; + } + + enum append_version_c { append_equal, append_collinear }; + + template + static inline bool append_collinear_spikes(TurnInfo & tp, + IntersectionInfo const& inters, + bool is_p_last, bool /*is_q_last*/, + method_type method, append_version_c version, + OutIt out) + { + // method == touch || touch_interior + // both position == middle + + bool is_p_spike = ( version == append_equal ? + ( tp.operations[0].operation == operation_union + || tp.operations[0].operation == operation_intersection ) : + tp.operations[0].operation == operation_continue ) + && ! is_p_last + && inters.is_spike_p(); + + // TODO: throw an exception for spike in Areal? + /*bool is_q_spike = tp.operations[1].operation == spike_op + && ! is_q_last + && inters.is_spike_q();*/ + + if ( is_p_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_union; + *out++ = tp; + tp.operations[0].operation = operation_continue; // boundary + //tp.operations[1].operation = operation_union; + *out++ = tp; + + return true; + } + + return false; + } + + enum append_version_o { append_touches, append_collinear_opposite }; + + template + static inline bool append_opposite_spikes(TurnInfo & tp, + IntersectionInfo const& inters, + bool is_p_last, bool /*is_q_last*/, + OutIt out) + { + bool is_p_spike = ( Version == append_touches ? + ( tp.operations[0].operation == operation_continue + || tp.operations[0].operation == operation_intersection ) : // i ??? + true ) + && ! is_p_last + && inters.is_spike_p(); + // TODO: throw an exception for spike in Areal? + /*bool is_q_spike = ( Version == append_touches ? + ( tp.operations[1].operation == operation_continue + || tp.operations[1].operation == operation_intersection ) : + true ) + && ! is_q_last + && inters.is_spike_q();*/ + + if ( is_p_spike && ( Version == append_touches || inters.d_info().arrival[0] == 1 ) ) + { + if ( Version == append_touches ) + { + tp.operations[0].is_collinear = true; + //tp.operations[1].is_collinear = false; + tp.method = method_touch; + } + else + { + tp.operations[0].is_collinear = true; + //tp.operations[1].is_collinear = false; + + BOOST_ASSERT(inters.i_info().count > 1); + base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1); + + AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters.i_info(), inters.d_info()); + } + + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_continue; // boundary + *out++ = tp; + tp.operations[0].operation = operation_continue; // boundary + //tp.operations[1].operation = operation_continue; // boundary + *out++ = tp; + + return true; + } + + return false; + } + + static inline void replace_method_and_operations_tm(method_type & method, + operation_type & op0, + operation_type & op1) + { + if ( op0 == operation_blocked && op1 == operation_blocked ) + { + // NOTE: probably only if methods are WRT IPs, not segments! + method = (method == method_touch ? method_equal : method_collinear); + } + + // Assuming G1 is always Linear + if ( op0 == operation_blocked ) + { + op0 = operation_continue; + } + + if ( op1 == operation_blocked ) + { + op1 = operation_continue; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + + // spikes in 'm' + if ( method == method_error ) + { + method = method_touch_interior; + op0 = operation_union; + op1 = operation_union; + } + } + + template + class turn_transformer_ec + { + public: + explicit turn_transformer_ec(method_type method_t_or_m) + : m_method(method_t_or_m) + {} + + template + void operator()(Turn & turn) const + { + operation_type & op0 = turn.operations[0].operation; + operation_type & op1 = turn.operations[1].operation; + + // NOTE: probably only if methods are WRT IPs, not segments! + if ( IsFront + || op0 == operation_intersection || op0 == operation_union + || op1 == operation_intersection || op1 == operation_union ) + { + turn.method = m_method; + } + + turn.operations[0].is_collinear = op0 != operation_blocked; + + // Assuming G1 is always Linear + if ( op0 == operation_blocked ) + { + op0 = operation_continue; + } + + if ( op1 == operation_blocked ) + { + op1 = operation_continue; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + } + + private: + method_type m_method; + }; + + static inline void replace_operations_i(operation_type & /*op0*/, operation_type & op1) + { + // assuming Linear is always the first one + op1 = operation_union; + } + + // NOTE: Spikes may NOT be handled for Linear endpoints because it's not + // possible to define a spike on an endpoint. Areal geometries must + // NOT have spikes at all. One thing that could be done is to throw + // an exception when spike is detected in Areal geometry. + + template + static inline bool get_turn_info_for_endpoint( + Point1 const& pi, Point1 const& /*pj*/, Point1 const& /*pk*/, + Point2 const& qi, Point2 const& /*qj*/, Point2 const& /*qk*/, + bool is_p_first, bool is_p_last, + bool /*is_q_first*/, bool is_q_last, + TurnInfo const& tp_model, + IntersectionInfo const& inters, + method_type /*method*/, + OutputIterator out) + { + namespace ov = overlay; + typedef ov::get_turn_info_for_endpoint get_info_e; + + const std::size_t ip_count = inters.i_info().count; + // no intersection points + if ( ip_count == 0 ) + return false; + + if ( !is_p_first && !is_p_last ) + return false; + +// TODO: is_q_last could probably be replaced by false and removed from parameters + + linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last); + linear_intersections::ip_info const& ip0 = intersections.template get<0>(); + linear_intersections::ip_info const& ip1 = intersections.template get<1>(); + + const bool opposite = inters.d_info().opposite; + + // ANALYSE AND ASSIGN FIRST + + // IP on the first point of Linear Geometry + if ( EnableFirst && is_p_first && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication + { + TurnInfo tp = tp_model; + tp.operations[0].position = position_front; + tp.operations[1].position = position_middle; + + if ( opposite ) // opposite -> collinear + { + tp.operations[0].operation = operation_continue; + tp.operations[1].operation = operation_union; + tp.method = ip0.is_qj ? method_touch : method_touch_interior; + } + else + { + method_type replaced_method = method_touch_interior; + + if ( ip0.is_qj ) + { + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), + inters.rqi(), inters.rqj(), inters.rqk()); + + std::pair + operations = get_info_e::operations_of_equal(side_calc); + + tp.operations[0].operation = operations.first; + tp.operations[1].operation = operations.second; + + replaced_method = method_touch; + } + else + { + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), + inters.rqi(), inters.rpi(), inters.rqj()); + + std::pair + operations = get_info_e::operations_of_equal(side_calc); + + tp.operations[0].operation = operations.first; + tp.operations[1].operation = operations.second; + } + + turn_transformer_ec transformer(replaced_method); + transformer(tp); + } + + // equals<> or collinear<> will assign the second point, + // we'd like to assign the first one + base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0); + + // NOTE: is_collinear is not set for the first endpoint of L + // for which there is no preceding segment + // here is_p_first_ip == true + tp.operations[0].is_collinear = false; + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + + // ANALYSE AND ASSIGN LAST + + // IP on the last point of Linear Geometry + if ( EnableLast + && is_p_last + && ( ip_count > 1 ? (ip1.is_pj && !ip1.is_qi) : (ip0.is_pj && !ip0.is_qi) ) ) // prevents duplication + { + TurnInfo tp = tp_model; + + if ( inters.i_info().count > 1 ) + { + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); + tp.operations[0].is_collinear = true; + tp.operations[1].operation = opposite ? operation_continue : operation_union; + } + else //if ( result.template get<0>().count == 1 ) + { + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpj(), inters.rpi(), + inters.rqi(), inters.rqj(), inters.rqk()); + + std::pair + operations = get_info_e::operations_of_equal(side_calc); + + tp.operations[0].operation = operations.first; + tp.operations[1].operation = operations.second; + + turn_transformer_ec transformer(method_none); + transformer(tp); + + tp.operations[0].is_collinear = tp.both(operation_continue); + } + + tp.method = ( ip_count > 1 ? ip1.is_qj : ip0.is_qj ) ? method_touch : method_touch_interior; + tp.operations[0].operation = operation_blocked; + tp.operations[0].position = position_back; + tp.operations[1].position = position_middle; + + // equals<> or collinear<> will assign the second point, + // we'd like to assign the first one + int ip_index = ip_count > 1 ? 1 : 0; + base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + + return true; + } + + // don't ignore anything for now + return false; + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp new file mode 100644 index 0000000000..4f0384877f --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp @@ -0,0 +1,720 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP + +#include +#include + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +template +struct get_turn_info_linear_linear +{ + static const bool handle_spikes = true; + + template + < + typename Point1, + typename Point2, + typename TurnInfo, + typename RobustPolicy, + typename OutputIterator + > + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + TurnInfo const& tp_model, + RobustPolicy const& robust_policy, + OutputIterator out) + { + typedef intersection_info + inters_info; + + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); + + char const method = inters.d_info().how; + + // Copy, to copy possibly extended fields + TurnInfo tp = tp_model; + + // Select method and apply + switch(method) + { + case 'a' : // collinear, "at" + case 'f' : // collinear, "from" + case 's' : // starts from the middle + get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_none, out); + break; + + case 'd' : // disjoint: never do anything + break; + + case 'm' : + { + if ( get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch_interior, out) ) + { + // do nothing + } + else + { + typedef touch_interior + < + TurnInfo + > policy; + + // If Q (1) arrives (1) + if ( inters.d_info().arrival[1] == 1) + { + policy::template apply<0>(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), + inters.sides()); + } + else + { + // Swap p/q + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); + + policy::template apply<1>(qi, qj, qk, pi, pj, pk, + tp, inters.i_info(), inters.d_info(), + swapped_side_calc); + } + + if ( tp.operations[0].operation == operation_blocked ) + { + tp.operations[1].is_collinear = true; + } + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + } + break; + case 'i' : + { + crosses::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info()); + + replace_operations_i(tp.operations[0].operation, tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + break; + case 't' : + { + // Both touch (both arrive there) + if ( get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch, out) ) + { + // do nothing + } + else + { + touch::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + // workarounds for touch<> not taking spikes into account starts here + // those was discovered empirically + // touch<> is not symmetrical! + // P spikes and Q spikes may produce various operations! + // TODO: this is not optimal solution - think about rewriting touch<> + + if ( tp.operations[0].operation == operation_blocked + && tp.operations[1].operation == operation_blocked ) + { + // two touching spikes on the same line + if ( inters.is_spike_p() && inters.is_spike_q() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + else + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + } + } + else if ( tp.operations[0].operation == operation_blocked ) + { + // a spike on P on the same line with Q1 + if ( inters.is_spike_p() ) + { + if ( inters.sides().qk_wrt_p1() == 0 ) + { + tp.operations[0].is_collinear = true; + } + else + { + tp.operations[0].operation = operation_union; + } + } + else + { + tp.operations[1].is_collinear = true; + } + } + else if ( tp.operations[1].operation == operation_blocked ) + { + // a spike on Q on the same line with P1 + if ( inters.is_spike_q() ) + { + if ( inters.sides().pk_wrt_q1() == 0 ) + { + tp.operations[1].is_collinear = true; + } + else + { + tp.operations[1].operation = operation_union; + } + } + else + { + tp.operations[0].is_collinear = true; + } + } + else if ( tp.operations[0].operation == operation_continue + && tp.operations[1].operation == operation_continue ) + { + // P spike on the same line with Q2 (opposite) + if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1() + && inters.is_spike_p() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + } + else if ( tp.operations[0].operation == operation_none + && tp.operations[1].operation == operation_none ) + { + // spike not handled by touch<> + bool const is_p = inters.is_spike_p(); + bool const is_q = inters.is_spike_q(); + + if ( is_p || is_q ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + + if ( inters.sides().pk_wrt_q2() == 0 ) + { + tp.operations[0].operation = operation_continue; // will be converted to i + if ( is_p ) + { + tp.operations[0].is_collinear = true; + } + } + + if ( inters.sides().qk_wrt_p2() == 0 ) + { + tp.operations[1].operation = operation_continue; // will be converted to i + if ( is_q ) + { + tp.operations[1].is_collinear = true; + } + } + } + } + + // workarounds for touch<> not taking spikes into account ends here + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + if ( ! handle_spikes + || ! append_opposite_spikes(tp, inters, + is_p_last, is_q_last, + out) ) + { + *out++ = tp; + } + } + } + break; + case 'e': + { + if ( get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_equal, out) ) + { + // do nothing + } + else + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + // Both equal + // or collinear-and-ending at intersection point + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + // transform turn + turn_transformer_ec transformer(method_touch); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, + is_p_last, is_q_last, + method_touch, operation_union, + out) ) + { + *out++ = tp; // no spikes + } + } + else + { + // TODO: ignore for spikes or generate something else than opposite? + + equal_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, qi, tp, out, inters.i_info(), inters.d_info()); + } + } + } + break; + case 'c' : + { + // Collinear + if ( get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_collinear, out) ) + { + // do nothing + } + else + { + // NOTE: this is for spikes since those are set in the turn_transformer_ec + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + method_type method_replace = method_touch_interior; + operation_type spike_op = operation_continue; + + if ( inters.d_info().arrival[0] == 0 ) + { + // Collinear, but similar thus handled as equal + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + method_replace = method_touch; + spike_op = operation_union; + } + else + { + collinear::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + //method_replace = method_touch_interior; + //spike_op = operation_continue; + } + + // transform turn + turn_transformer_ec transformer(method_replace); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, + is_p_last, is_q_last, + method_replace, spike_op, + out) ) + { + // no spikes + *out++ = tp; + } + } + else + { + // If this always 'm' ? + turn_transformer_ec transformer(method_touch_interior); + + // conditionally handle spikes + if ( handle_spikes ) + { + append_opposite_spikes(tp, inters, + is_p_last, is_q_last, + out); + } + + // TODO: ignore for spikes? + // E.g. pass is_p_valid = !is_p_last && !is_pj_spike, + // the same with is_q_valid + + collinear_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, pj, pk, qi, qj, qk, + tp, out, inters.i_info(), inters.d_info(), inters.sides(), + transformer, !is_p_last, !is_q_last); + } + } + } + break; + case '0' : + { + // degenerate points + if (AssignPolicy::include_degenerate) + { + only_convert::apply(tp, inters.i_info()); + + // if any, only one of those should be true + if ( is_p_first + && equals::equals_point_point(pi, tp.point) ) + { + tp.operations[0].position = position_front; + } + else if ( is_p_last + && equals::equals_point_point(pj, tp.point) ) + { + tp.operations[0].position = position_back; + } + else if ( is_q_first + && equals::equals_point_point(qi, tp.point) ) + { + tp.operations[1].position = position_front; + } + else if ( is_q_last + && equals::equals_point_point(qj, tp.point) ) + { + tp.operations[1].position = position_back; + } + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + } + break; + default : + { +#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) + std::cout << "TURN: Unknown method: " << method << std::endl; +#endif +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw turn_info_exception(method); +#endif + } + break; + } + + return out; + } + + template + static inline bool append_collinear_spikes(TurnInfo & tp, + IntersectionInfo const& inters_info, + bool is_p_last, bool is_q_last, + method_type method, operation_type spike_op, + OutIt out) + { + // method == touch || touch_interior + // both position == middle + + bool is_p_spike = tp.operations[0].operation == spike_op + && ! is_p_last + && inters_info.is_spike_p(); + bool is_q_spike = tp.operations[1].operation == spike_op + && ! is_q_last + && inters_info.is_spike_q(); + + if ( is_p_spike && is_q_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + return true; + } + else if ( is_p_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_union; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + //tp.operations[1].operation = operation_union; + *out++ = tp; + + return true; + } + else if ( is_q_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + //tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + return true; + } + + return false; + } + + enum append_version { append_touches, append_collinear_opposite }; + + template + static inline bool append_opposite_spikes(TurnInfo & tp, + IntersectionInfo const& inters, + bool is_p_last, bool is_q_last, + OutIt out) + { + bool is_p_spike = ( Version == append_touches ? + ( tp.operations[0].operation == operation_continue + || tp.operations[0].operation == operation_intersection ) : + true ) + && ! is_p_last + && inters.is_spike_p(); + bool is_q_spike = ( Version == append_touches ? + ( tp.operations[1].operation == operation_continue + || tp.operations[1].operation == operation_intersection ) : + true ) + && ! is_q_last + && inters.is_spike_q(); + + bool res = false; + + if ( is_p_spike && ( Version == append_touches || inters.d_info().arrival[0] == 1 ) ) + { + if ( Version == append_touches ) + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = false; + tp.method = method_touch; + } + else // Version == append_collinear_opposite + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = false; + + BOOST_ASSERT(inters.i_info().count > 1); + + base_turn_handler::assign_point(tp, method_touch_interior, + inters.i_info(), 1); + + AssignPolicy::apply(tp, inters.pi(), inters.qi(), + inters.i_info(), inters.d_info()); + } + + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + //tp.operations[1].operation = operation_intersection; + *out++ = tp; + + res = true; + } + + if ( is_q_spike && ( Version == append_touches || inters.d_info().arrival[1] == 1 ) ) + { + if ( Version == append_touches ) + { + tp.operations[0].is_collinear = false; + tp.operations[1].is_collinear = true; + tp.method = method_touch; + } + else // Version == append_collinear_opposite + { + tp.operations[0].is_collinear = false; + tp.operations[1].is_collinear = true; + + BOOST_ASSERT(inters.i_info().count > 0); + + base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 0); + + AssignPolicy::apply(tp, inters.pi(), inters.qi(), + inters.i_info(), inters.d_info()); + } + + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + //tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + res = true; + } + + return res; + } + + static inline void replace_method_and_operations_tm(method_type & method, + operation_type & op0, + operation_type & op1) + { + if ( op0 == operation_blocked && op1 == operation_blocked ) + { + // NOTE: probably only if methods are WRT IPs, not segments! + method = (method == method_touch ? method_equal : method_collinear); + op0 = operation_continue; + op1 = operation_continue; + } + else + { + if ( op0 == operation_continue || op0 == operation_blocked ) + { + op0 = operation_intersection; + } + else if ( op0 == operation_intersection ) + { + op0 = operation_union; + } + + if ( op1 == operation_continue || op1 == operation_blocked ) + { + op1 = operation_intersection; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + + // spikes in 'm' + if ( method == method_error ) + { + method = method_touch_interior; + op0 = operation_union; + op1 = operation_union; + } + } + } + + class turn_transformer_ec + { + public: + explicit turn_transformer_ec(method_type method_t_or_m) + : m_method(method_t_or_m) + {} + + template + void operator()(Turn & turn) const + { + operation_type & op0 = turn.operations[0].operation; + operation_type & op1 = turn.operations[1].operation; + + BOOST_ASSERT(op0 != operation_blocked || op1 != operation_blocked ); + + if ( op0 == operation_blocked ) + { + op0 = operation_intersection; + } + else if ( op0 == operation_intersection ) + { + op0 = operation_union; + } + + if ( op1 == operation_blocked ) + { + op1 = operation_intersection; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + + if ( op0 == operation_intersection || op0 == operation_union + || op1 == operation_intersection || op1 == operation_union ) + { + turn.method = m_method; + } + +// TODO: is this correct? +// it's equivalent to comparing to operation_blocked at the beginning of the function + turn.operations[0].is_collinear = op0 != operation_intersection; + turn.operations[1].is_collinear = op1 != operation_intersection; + } + + private: + method_type m_method; + }; + + static inline void replace_operations_i(operation_type & op0, operation_type & op1) + { + if ( op0 == operation_intersection ) + { + op0 = operation_union; + } + + if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 26629043cb..a96538c43a 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -1,11 +1,17 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. // 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) +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP @@ -14,19 +20,17 @@ #include #include +#include #include #include -#include - -#include #include #include -#include - #include #include +#include #include +#include #include @@ -44,17 +48,22 @@ #include #include -#include +#include +#include + +#include #include -#include +#include +#include +#include +#include #include - #include +#include #include -#include #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION # include @@ -65,6 +74,12 @@ namespace boost { namespace geometry { +// Silence warning C4127: conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace get_turns @@ -88,9 +103,7 @@ template typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, typename Section1, typename Section2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > class get_turns_in_sections { @@ -138,16 +151,21 @@ class get_turns_in_sections // About first condition: will be optimized by compiler (static) // It checks if it is areal (box,ring,(multi)polygon int const n = int(section.range_count); + + boost::ignore_unused_variable_warning(n); + boost::ignore_unused_variable_warning(index1); + boost::ignore_unused_variable_warning(index2); + return boost::is_same < typename tag_cast < - typename geometry::point_type::type, + typename geometry::tag::type, areal_tag - >::type, + >::type, areal_tag >::value - && index1 == 0 + && index1 == 0 && index2 >= n - 2 ; } @@ -155,13 +173,27 @@ class get_turns_in_sections public : // Returns true if terminated, false if interrupted + template static inline bool apply( int source_id1, Geometry1 const& geometry1, Section1 const& sec1, int source_id2, Geometry2 const& geometry2, Section2 const& sec2, bool skip_larger, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { + boost::ignore_unused_variable_warning(interrupt_policy); + + if ((sec1.duplicate && (sec1.count + 1) < sec1.range_count) + || (sec2.duplicate && (sec2.count + 1) < sec2.range_count)) + { + // Skip sections containig only duplicates. + // They are still important (can indicate non-disjointness) + // but they will be found processing adjacent sections. + // Do NOT skip if they are the ONLY section + return true; + } + cview_type1 cview1(range_by_section(geometry1, sec1)); cview_type2 cview2(range_by_section(geometry2, sec2)); view_type1 view1(cview1); @@ -186,7 +218,7 @@ public : range1_iterator prev1, it1, end1; get_start_point_iterator(sec1, view1, prev1, it1, end1, - index1, ndi1, dir1, sec2.bounding_box); + index1, ndi1, dir1, sec2.bounding_box, robust_policy); // We need a circular iterator because it might run through the closing point. // One circle is actually enough but this one is just convenient. @@ -197,12 +229,12 @@ public : // section 2: [--------------] // section 1: |----|---|---|---|---| for (prev1 = it1++, next1++; - it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box); + it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); ++prev1, ++it1, ++index1, ++next1, ++ndi1) { ever_circling_iterator nd_next1( begin_range_1, end_range_1, next1, true); - advance_to_non_duplicate_next(nd_next1, it1, sec1); + advance_to_non_duplicate_next(nd_next1, it1, sec1, robust_policy); int index2 = sec2.begin_index; int ndi2 = sec2.non_duplicate_index; @@ -210,12 +242,12 @@ public : range2_iterator prev2, it2, end2; get_start_point_iterator(sec2, view2, prev2, it2, end2, - index2, ndi2, dir2, sec1.bounding_box); + index2, ndi2, dir2, sec1.bounding_box, robust_policy); ever_circling_iterator next2(begin_range_2, end_range_2, it2, true); next2++; for (prev2 = it2++, next2++; - it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box); + it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { bool skip = same_source; @@ -241,24 +273,28 @@ public : // Move to the "non duplicate next" ever_circling_iterator nd_next2( begin_range_2, end_range_2, next2, true); - advance_to_non_duplicate_next(nd_next2, it2, sec2); + advance_to_non_duplicate_next(nd_next2, it2, sec2, robust_policy); typedef typename boost::range_value::type turn_info; - typedef typename turn_info::point_type ip; turn_info ti; - ti.operations[0].seg_id = segment_identifier(source_id1, - sec1.ring_id.multi_index, sec1.ring_id.ring_index, index1), - ti.operations[1].seg_id = segment_identifier(source_id2, - sec2.ring_id.multi_index, sec2.ring_id.ring_index, index2), - - ti.operations[0].other_id = ti.operations[1].seg_id; - ti.operations[1].other_id = ti.operations[0].seg_id; + ti.operations[0].seg_id + = segment_identifier(source_id1, sec1.ring_id.multi_index, + sec1.ring_id.ring_index, index1); + ti.operations[1].seg_id + = segment_identifier(source_id2, sec2.ring_id.multi_index, + sec2.ring_id.ring_index, index2); std::size_t const size_before = boost::size(turns); + bool const is_1_first = sec1.is_non_duplicate_first && index1 == sec1.begin_index; + bool const is_1_last = sec1.is_non_duplicate_last && index1+1 >= sec1.end_index; + bool const is_2_first = sec2.is_non_duplicate_first && index2 == sec2.begin_index; + bool const is_2_last = sec2.is_non_duplicate_last && index2+1 >= sec2.end_index; + TurnPolicy::apply(*prev1, *it1, *nd_next1, *prev2, *it2, *nd_next2, - ti, std::back_inserter(turns)); + is_1_first, is_1_last, is_2_first, is_2_last, + ti, robust_policy, std::back_inserter(turns)); if (InterruptPolicy::enabled) { @@ -283,24 +319,34 @@ private : typedef typename model::referring_segment segment2_type; - template - static inline bool preceding(int dir, Point const& point, Box const& box) + template + static inline bool preceding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) { - return (dir == 1 && get(point) < get(box)) - || (dir == -1 && get(point) > get(box)); + typename robust_point_type::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get(robust_point) < get(box)) + || (dir == -1 && get(robust_point) > get(box)); } - template - static inline bool exceeding(int dir, Point const& point, Box const& box) + template + static inline bool exceeding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) { - return (dir == 1 && get(point) > get(box)) - || (dir == -1 && get(point) < get(box)); + typename robust_point_type::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get(robust_point) > get(box)) + || (dir == -1 && get(robust_point) < get(box)); } - template + template static inline void advance_to_non_duplicate_next(Iterator& next, - RangeIterator const& it, Section const& section) + RangeIterator const& it, Section const& section, RobustPolicy const& robust_policy) { + typedef typename robust_point_type::type robust_point_type; + robust_point_type robust_point_from_it; + robust_point_type robust_point_from_next; + geometry::recalculate(robust_point_from_it, *it, robust_policy); + geometry::recalculate(robust_point_from_next, *next, robust_policy); + // To see where the next segments bend to, in case of touch/intersections // on end points, we need (in case of degenerate/duplicate points) an extra // iterator which moves to the REAL next point, so non duplicate. @@ -311,10 +357,14 @@ private : // So advance to the "non duplicate next" // (the check is defensive, to avoid endless loops) std::size_t check = 0; - while(! detail::disjoint::disjoint_point_point(*it, *next) + while(! detail::disjoint::disjoint_point_point + ( + robust_point_from_it, robust_point_from_next + ) && check++ < section.range_count) { next++; + geometry::recalculate(robust_point_from_next, *next, robust_policy); } } @@ -322,14 +372,14 @@ private : // because of the logistics of "index" (the section-iterator automatically // skips to the begin-point, we loose the index or have to recalculate it) // So we mimic it here - template + template static inline void get_start_point_iterator(Section & section, Range const& range, typename boost::range_iterator::type& it, typename boost::range_iterator::type& prev, typename boost::range_iterator::type& end, int& index, int& ndi, - int dir, Box const& other_bounding_box) + int dir, Box const& other_bounding_box, RobustPolicy const& robust_policy) { it = boost::begin(range) + section.begin_index; end = boost::begin(range) + section.end_index + 1; @@ -337,7 +387,7 @@ private : // Mimic section-iterator: // Skip to point such that section interects other box prev = it++; - for(; it != end && preceding<0>(dir, *it, other_bounding_box); + for(; it != end && preceding<0>(dir, *it, other_bounding_box, robust_policy); prev = it++, index++, ndi++) {} // Go back one step because we want to start completely preceding @@ -369,6 +419,7 @@ template bool Reverse1, bool Reverse2, typename Turns, typename TurnPolicy, + typename RobustPolicy, typename InterruptPolicy > struct section_visitor @@ -377,14 +428,17 @@ struct section_visitor Geometry1 const& m_geometry1; int m_source_id2; Geometry2 const& m_geometry2; + RobustPolicy const& m_rescale_policy; Turns& m_turns; InterruptPolicy& m_interrupt_policy; section_visitor(int id1, Geometry1 const& g1, int id2, Geometry2 const& g2, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& ip) : m_source_id1(id1), m_geometry1(g1) , m_source_id2(id2), m_geometry2(g2) + , m_rescale_policy(robust_policy) , m_turns(turns) , m_interrupt_policy(ip) {} @@ -400,13 +454,12 @@ struct section_visitor Geometry2, Reverse1, Reverse2, Section, Section, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy >::apply( m_source_id1, m_geometry1, sec1, m_source_id2, m_geometry2, sec2, false, + m_rescale_policy, m_turns, m_interrupt_policy); } return true; @@ -418,37 +471,45 @@ template < typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > class get_turns_generic { public: + template static inline void apply( int source_id1, Geometry1 const& geometry1, int source_id2, Geometry2 const& geometry2, - Turns& turns, InterruptPolicy& interrupt_policy) + RobustPolicy const& robust_policy, + Turns& turns, + InterruptPolicy& interrupt_policy) { // First create monotonic sections... typedef typename boost::range_value::type ip_type; typedef typename ip_type::point_type point_type; - typedef model::box box_type; + + typedef model::box + < + typename geometry::robust_point_type + < + point_type, RobustPolicy + >::type + > box_type; typedef typename geometry::sections sections_type; sections_type sec1, sec2; - geometry::sectionalize(geometry1, sec1, 0); - geometry::sectionalize(geometry2, sec2, 1); + geometry::sectionalize(geometry1, robust_policy, true, sec1, 0); + geometry::sectionalize(geometry2, robust_policy, true, sec2, 1); // ... and then partition them, intersecting overlapping sections in visitor method section_visitor < Geometry1, Geometry2, Reverse1, Reverse2, - Turns, TurnPolicy, InterruptPolicy - > visitor(source_id1, geometry1, source_id2, geometry2, turns, interrupt_policy); + Turns, TurnPolicy, RobustPolicy, InterruptPolicy + > visitor(source_id1, geometry1, source_id2, geometry2, robust_policy, turns, interrupt_policy); geometry::partition < @@ -463,13 +524,10 @@ template < typename Range, typename Box, bool ReverseRange, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns_cs { - typedef typename boost::range_value::type turn_info; typedef typename geometry::point_type::type point_type; typedef typename geometry::point_type::type box_point_type; @@ -491,14 +549,16 @@ struct get_turns_cs >::type iterator_type; + template static inline void apply( int source_id1, Range const& range, int source_id2, Box const& box, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, int multi_index = -1, int ring_index = -1) { - if (boost::size(range) <= 1) + if ( boost::size(range) <= 1) { return; } @@ -509,6 +569,8 @@ struct get_turns_cs cview_type cview(range); view_type view(cview); + typename boost::range_size::type segments_count1 = boost::size(view) - 1; + iterator_type it = boost::begin(view); ever_circling_iterator next( @@ -557,9 +619,13 @@ struct get_turns_cs get_turns_with_box(seg_id, source_id2, *prev, *it, *next, bp[0], bp[1], bp[2], bp[3], + // NOTE: some dummy values could be passed below since this would be called only for Polygons and Boxes + index == 0, + unsigned(index) == segments_count1, + robust_policy, turns, interrupt_policy); - // Future performance enhancement: - // return if told by the interrupt policy + // Future performance enhancement: + // return if told by the interrupt policy } } } @@ -585,6 +651,7 @@ private: else return 0; } + template static inline void get_turns_with_box(segment_identifier const& seg_id, int source_id2, // Points from a range: point_type const& rp0, @@ -595,34 +662,45 @@ private: box_point_type const& bp1, box_point_type const& bp2, box_point_type const& bp3, + bool const is_range_first, + bool const is_range_last, + RobustPolicy const& robust_policy, // Output Turns& turns, InterruptPolicy& interrupt_policy) { + boost::ignore_unused_variable_warning(interrupt_policy); + // Depending on code some relations can be left out typedef typename boost::range_value::type turn_info; turn_info ti; ti.operations[0].seg_id = seg_id; - ti.operations[0].other_id = ti.operations[1].seg_id; - ti.operations[1].other_id = seg_id; ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 0); TurnPolicy::apply(rp0, rp1, rp2, bp0, bp1, bp2, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + true, false, + ti, robust_policy, std::back_inserter(turns)); ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 1); TurnPolicy::apply(rp0, rp1, rp2, bp1, bp2, bp3, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + false, false, + ti, robust_policy, std::back_inserter(turns)); ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 2); TurnPolicy::apply(rp0, rp1, rp2, bp2, bp3, bp0, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + false, false, + ti, robust_policy, std::back_inserter(turns)); ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 3); TurnPolicy::apply(rp0, rp1, rp2, bp3, bp0, bp1, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + false, true, + ti, robust_policy, std::back_inserter(turns)); if (InterruptPolicy::enabled) { @@ -638,15 +716,15 @@ template < typename Polygon, typename Box, bool Reverse, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns_polygon_cs { + template static inline void apply( int source_id1, Polygon const& polygon, int source_id2, Box const& box, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, int multi_index = -1) { @@ -656,32 +734,118 @@ struct get_turns_polygon_cs < ring_type, Box, Reverse, ReverseBox, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > intersector_type; intersector_type::apply( source_id1, geometry::exterior_ring(polygon), - source_id2, box, turns, interrupt_policy, + source_id2, box, + robust_policy, + turns, interrupt_policy, multi_index, -1); int i = 0; - typename interior_return_type::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); - ++it, ++i) + typename interior_return_type::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it, ++i) { intersector_type::apply( source_id1, *it, - source_id2, box, turns, interrupt_policy, + source_id2, box, + robust_policy, + turns, interrupt_policy, multi_index, i); } } }; + +template +< + typename Multi, typename Box, + bool Reverse, bool ReverseBox, + typename TurnPolicy +> +struct get_turns_multi_polygon_cs +{ + template + static inline void apply( + int source_id1, Multi const& multi, + int source_id2, Box const& box, + RobustPolicy const& robust_policy, + Turns& turns, InterruptPolicy& interrupt_policy) + { + typedef typename boost::range_iterator + < + Multi const + >::type iterator_type; + + int i = 0; + for (iterator_type it = boost::begin(multi); + it != boost::end(multi); + ++it, ++i) + { + // Call its single version + get_turns_polygon_cs + < + typename boost::range_value::type, Box, + Reverse, ReverseBox, + TurnPolicy + >::apply(source_id1, *it, source_id2, box, + robust_policy, turns, interrupt_policy, i); + } + } +}; + + +// GET_TURN_INFO_TYPE + +template +struct topological_tag_base +{ + typedef typename tag_cast::type, pointlike_tag, linear_tag, areal_tag>::type type; +}; + +template ::type, typename Tag2 = typename tag::type, + typename TagBase1 = typename topological_tag_base::type, typename TagBase2 = typename topological_tag_base::type> +struct get_turn_info_type + : overlay::get_turn_info +{}; + +template +struct get_turn_info_type + : overlay::get_turn_info_linear_linear +{}; + +template +struct get_turn_info_type + : overlay::get_turn_info_linear_areal +{}; + +template ::type, typename Tag2 = typename tag::type, + typename TagBase1 = typename topological_tag_base::type, typename TagBase2 = typename topological_tag_base::type> +struct turn_operation_type +{ + typedef overlay::turn_operation type; +}; + +template +struct turn_operation_type +{ + typedef overlay::turn_operation_linear type; +}; + +template +struct turn_operation_type +{ + typedef overlay::turn_operation_linear type; +}; + }} // namespace detail::get_turns #endif // DOXYGEN_NO_DETAIL @@ -697,18 +861,14 @@ template typename GeometryTag1, typename GeometryTag2, typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns : detail::get_turns::get_turns_generic < Geometry1, Geometry2, Reverse1, Reverse2, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > {}; @@ -717,23 +877,19 @@ template < typename Polygon, typename Box, bool ReversePolygon, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns < polygon_tag, box_tag, Polygon, Box, ReversePolygon, ReverseBox, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > : detail::get_turns::get_turns_polygon_cs < Polygon, Box, ReversePolygon, ReverseBox, - Turns, TurnPolicy, InterruptPolicy + TurnPolicy > {}; @@ -742,51 +898,71 @@ template < typename Ring, typename Box, bool ReverseRing, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns < ring_tag, box_tag, Ring, Box, ReverseRing, ReverseBox, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > : detail::get_turns::get_turns_cs < Ring, Box, ReverseRing, ReverseBox, - Turns, TurnPolicy, InterruptPolicy + TurnPolicy > {}; +template +< + typename MultiPolygon, + typename Box, + bool ReverseMultiPolygon, bool ReverseBox, + typename TurnPolicy +> +struct get_turns + < + multi_polygon_tag, box_tag, + MultiPolygon, Box, + ReverseMultiPolygon, ReverseBox, + TurnPolicy + > + : detail::get_turns::get_turns_multi_polygon_cs + < + MultiPolygon, Box, + ReverseMultiPolygon, ReverseBox, + TurnPolicy + > +{}; + + template < typename GeometryTag1, typename GeometryTag2, typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns_reversed { + template static inline void apply( int source_id1, Geometry1 const& g1, int source_id2, Geometry2 const& g2, - Turns& turns, InterruptPolicy& interrupt_policy) + RobustPolicy const& robust_policy, + Turns& turns, + InterruptPolicy& interrupt_policy) { get_turns < GeometryTag2, GeometryTag1, Geometry2, Geometry1, Reverse2, Reverse1, - Turns, TurnPolicy, - InterruptPolicy - >::apply(source_id2, g2, source_id1, g1, turns, interrupt_policy); + TurnPolicy + >::apply(source_id2, g2, source_id1, g1, robust_policy, + turns, interrupt_policy); } }; @@ -804,6 +980,7 @@ struct get_turns_reversed \tparam Turns type of turn-container (e.g. vector of "intersection/turn point"'s) \param geometry1 \param_geometry \param geometry2 \param_geometry +\param robust_policy policy to handle robustness issues \param turns container which will contain turn points \param interrupt_policy policy determining if process is stopped when intersection is found @@ -814,31 +991,20 @@ template typename AssignPolicy, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Turns, typename InterruptPolicy > inline void get_turns(Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { concept::check_concepts_and_equal_dimensions(); - typedef typename strategy_intersection - < - typename cs_tag::type, - Geometry1, - Geometry2, - typename boost::range_value::type - >::segment_intersection_strategy_type segment_intersection_strategy_type; - - typedef detail::overlay::get_turn_info - < - typename point_type::type, - typename point_type::type, - typename boost::range_value::type, - AssignPolicy - > TurnPolicy; + typedef detail::overlay::get_turn_info TurnPolicy; + //typedef detail::get_turns::get_turn_info_type TurnPolicy; boost::mpl::if_c < @@ -849,8 +1015,7 @@ inline void get_turns(Geometry1 const& geometry1, typename tag::type, Geometry1, Geometry2, Reverse1, Reverse2, - Turns, TurnPolicy, - InterruptPolicy + TurnPolicy >, dispatch::get_turns < @@ -858,15 +1023,18 @@ inline void get_turns(Geometry1 const& geometry1, typename tag::type, Geometry1, Geometry2, Reverse1, Reverse2, - Turns, TurnPolicy, - InterruptPolicy + TurnPolicy > >::type::apply( 0, geometry1, 1, geometry2, + robust_policy, turns, interrupt_policy); } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif }} // namespace boost::geometry diff --git a/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp index 1e878ca525..085933dd7a 100644 --- a/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp +++ b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp @@ -14,7 +14,17 @@ #include #include #include +#include +#include +#include +#include + +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) +#include +#endif + +#include #include @@ -31,6 +41,7 @@ template typename TurnPoints, typename Indexed, typename Geometry1, typename Geometry2, + typename RobustPolicy, bool Reverse1, bool Reverse2, typename Strategy > @@ -39,10 +50,12 @@ struct sort_in_cluster inline sort_in_cluster(TurnPoints const& turn_points , Geometry1 const& geometry1 , Geometry2 const& geometry2 + , RobustPolicy const& robust_policy , Strategy const& strategy) : m_turn_points(turn_points) , m_geometry1(geometry1) , m_geometry2(geometry2) + , m_rescale_policy(robust_policy) , m_strategy(strategy) {} @@ -51,49 +64,100 @@ private : TurnPoints const& m_turn_points; Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; + RobustPolicy const& m_rescale_policy; Strategy const& m_strategy; typedef typename Indexed::type turn_operation_type; typedef typename geometry::point_type::type point_type; - typedef model::referring_segment segment_type; + + typedef typename geometry::robust_point_type + < + point_type, + RobustPolicy + >::type robust_point_type; + + // Still necessary in some situations, + // for example #case_102_multi, #case_107_multi, #case_recursive_boxes_3 + inline void get_situation_map(Indexed const& left, Indexed const& right, + robust_point_type& pi_rob, robust_point_type& pj_rob, + robust_point_type& ri_rob, robust_point_type& rj_rob, + robust_point_type& si_rob, robust_point_type& sj_rob) const + { + point_type pi, pj, ri, rj, si, sj; + + geometry::copy_segment_points(m_geometry1, m_geometry2, + left.subject->seg_id, + pi, pj); + geometry::copy_segment_points(m_geometry1, m_geometry2, + *left.other_seg_id, + ri, rj); + geometry::copy_segment_points(m_geometry1, m_geometry2, + *right.other_seg_id, + si, sj); + + geometry::recalculate(pi_rob, pi, m_rescale_policy); + geometry::recalculate(pj_rob, pj, m_rescale_policy); + geometry::recalculate(ri_rob, ri, m_rescale_policy); + geometry::recalculate(rj_rob, rj, m_rescale_policy); + geometry::recalculate(si_rob, si, m_rescale_policy); + geometry::recalculate(sj_rob, sj, m_rescale_policy); + } + +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO + // This method was still called but did no effect whatsoever on the results, + // with or without robustness-rescaling. + // Probable cause: we rescale in this file ourselves, ignoring passed policy + // TODO: check this more. + // Besides this, it currently does not compile for yet unknown reasons + // (does not find specialization for segment_ratio_type) + // It is currently only called from the Unit Test "multi_intersection.cpp" // Determine how p/r and p/s are located. - template - static inline void overlap_info(P const& pi, P const& pj, - P const& ri, P const& rj, - P const& si, P const& sj, - bool& pr_overlap, bool& ps_overlap, bool& rs_overlap) + inline void overlap_info( + robust_point_type const& pi, robust_point_type const& pj, + robust_point_type const& ri, robust_point_type const& rj, + robust_point_type const& si, robust_point_type const& sj, + bool& pr_overlap, bool& ps_overlap, bool& rs_overlap) const { // Determine how p/r and p/s are located. // One of them is coming from opposite direction. + typedef segment_intersection_points + < + point_type, + typename segment_ratio_type + < + point_type, RobustPolicy + >::type + > intersection_return_type; + typedef strategy::intersection::relate_cartesian_segments < policies::relate::segments_intersection_points < - segment_type, - segment_type, - segment_intersection_points + intersection_return_type > > policy; + typedef model::referring_segment segment_type; segment_type p(pi, pj); segment_type r(ri, rj); segment_type s(si, sj); // Get the intersection point (or two points) - segment_intersection_points pr = policy::apply(p, r); - segment_intersection_points ps = policy::apply(p, s); - segment_intersection_points rs = policy::apply(r, s); + intersection_return_type pr = policy::apply(p, r, m_rescale_policy, pi, pj, ri, rj); + intersection_return_type ps = policy::apply(p, s, m_rescale_policy, pi, pj, si, sj); + intersection_return_type rs = policy::apply(r, s, m_rescale_policy, ri, rj, si, sj); // Check on overlap pr_overlap = pr.count == 2; ps_overlap = ps.count == 2; rs_overlap = rs.count == 2; } +#endif -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES inline void debug_consider(int order, Indexed const& left, Indexed const& right, std::string const& header, bool skip = true, @@ -102,19 +166,15 @@ private : { if (skip) return; - point_type pi, pj, ri, rj, si, sj; - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.seg_id, - pi, pj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.other_id, - ri, rj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - right.subject.other_id, - si, sj); + std::cout << "Case: " << header << " for " << left.turn_index << " / " << right.turn_index << std::endl; + + robust_point_type pi, pj, ri, rj, si, sj; + get_situation_map(left, right, pi, pj, ri, rj, si, sj); +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO bool prc = false, psc = false, rsc = false; overlap_info(pi, pj, ri, rj, si, sj, prc, psc, rsc); +#endif int const side_ri_p = m_strategy.apply(pi, pj, ri); int const side_rj_p = m_strategy.apply(pi, pj, rj); @@ -123,8 +183,7 @@ private : int const side_si_r = m_strategy.apply(ri, rj, si); int const side_sj_r = m_strategy.apply(ri, rj, sj); - std::cout << "Case: " << header << " for " << left.index << " / " << right.index << std::endl; -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH_MORE +#ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES_MORE std::cout << " Segment p:" << geometry::wkt(pi) << " .. " << geometry::wkt(pj) << std::endl; std::cout << " Segment r:" << geometry::wkt(ri) << " .. " << geometry::wkt(rj) << std::endl; std::cout << " Segment s:" << geometry::wkt(si) << " .. " << geometry::wkt(sj) << std::endl; @@ -136,13 +195,15 @@ private : std::cout << header //<< " order: " << order - << " ops: " << operation_char(left.subject.operation) - << "/" << operation_char(right.subject.operation) + << " ops: " << operation_char(left.subject->operation) + << "/" << operation_char(right.subject->operation) << " ri//p: " << side_ri_p << " si//p: " << side_si_p << " si//r: " << side_si_r +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO << " cnts: " << int(prc) << "," << int(psc) << "," << int(rsc) - //<< " idx: " << left.index << "/" << right.index +#endif + //<< " idx: " << left.turn_index << "/" << right.turn_index ; if (! extra.empty()) @@ -167,23 +228,23 @@ private : , std::string const& // header ) const { - bool ret = left.index < right.index; + bool ret = left.turn_index < right.turn_index; // In combination of u/x, x/u: take first union, then blocked. // Solves #88, #61, #56, #80 - if (left.subject.operation == operation_union - && right.subject.operation == operation_blocked) + if (left.subject->operation == operation_union + && right.subject->operation == operation_blocked) { ret = true; } - else if (left.subject.operation == operation_blocked - && right.subject.operation == operation_union) + else if (left.subject->operation == operation_blocked + && right.subject->operation == operation_union) { ret = false; } else { -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << "ux/ux unhandled" << std::endl; #endif } @@ -201,32 +262,32 @@ private : { bool ret = false; - if (left.subject.operation == operation_union - && right.subject.operation == operation_union) + if (left.subject->operation == operation_union + && right.subject->operation == operation_union) { ret = order == 1; } - else if (left.subject.operation == operation_union - && right.subject.operation == operation_blocked) + else if (left.subject->operation == operation_union + && right.subject->operation == operation_blocked) { ret = true; } - else if (right.subject.operation == operation_union - && left.subject.operation == operation_blocked) + else if (right.subject->operation == operation_union + && left.subject->operation == operation_blocked) { ret = false; } - else if (left.subject.operation == operation_union) + else if (left.subject->operation == operation_union) { ret = true; } - else if (right.subject.operation == operation_union) + else if (right.subject->operation == operation_union) { ret = false; } else { -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) // this still happens in the traverse.cpp test std::cout << " iu/ux unhandled" << std::endl; #endif @@ -245,43 +306,61 @@ private : { //debug_consider(order, left, right, header, false, "iu/ix"); - return left.subject.operation == operation_intersection - && right.subject.operation == operation_intersection ? order == 1 - : left.subject.operation == operation_intersection ? false - : right.subject.operation == operation_intersection ? true + return left.subject->operation == operation_intersection + && right.subject->operation == operation_intersection ? order == 1 + : left.subject->operation == operation_intersection ? false + : right.subject->operation == operation_intersection ? true : order == 1; } + inline bool consider_ix_ix(Indexed const& left, Indexed const& right + , std::string const& // header + ) const + { + // Take first intersection, then blocked. + if (left.subject->operation == operation_intersection + && right.subject->operation == operation_blocked) + { + return true; + } + else if (left.subject->operation == operation_blocked + && right.subject->operation == operation_intersection) + { + return false; + } + + // Default case, should not occur + +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) + std::cout << "ix/ix unhandled" << std::endl; +#endif + //debug_consider(0, left, right, header, false, "-> return", ret); + + return left.turn_index < right.turn_index; + } + inline bool consider_iu_iu(Indexed const& left, Indexed const& right, - std::string const& header) const + std::string const& header, bool redo = false) const { //debug_consider(0, left, right, header); // In general, order it like "union, intersection". - if (left.subject.operation == operation_intersection - && right.subject.operation == operation_union) + if (left.subject->operation == operation_intersection + && right.subject->operation == operation_union) { //debug_consider(0, left, right, header, false, "i,u", false); return false; } - else if (left.subject.operation == operation_union - && right.subject.operation == operation_intersection) + else if (left.subject->operation == operation_union + && right.subject->operation == operation_intersection) { //debug_consider(0, left, right, header, false, "u,i", true); return true; } - point_type pi, pj, ri, rj, si, sj; - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.seg_id, - pi, pj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.other_id, - ri, rj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - right.subject.other_id, - si, sj); + robust_point_type pi, pj, ri, rj, si, sj; + get_situation_map(left, right, pi, pj, ri, rj, si, sj); int const side_ri_p = m_strategy.apply(pi, pj, ri); int const side_si_p = m_strategy.apply(pi, pj, si); @@ -291,8 +370,8 @@ private : if (side_ri_p * side_si_p == 1 && side_si_r != 0) { // Take the most left one - if (left.subject.operation == operation_union - && right.subject.operation == operation_union) + if (left.subject->operation == operation_union + && right.subject->operation == operation_union) { bool ret = side_si_r == 1; //debug_consider(0, left, right, header, false, "same side", ret); @@ -311,14 +390,18 @@ private : debug_consider(0, left, right, header, false, "opp.", ret); return ret; } -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << " iu/iu coming from opposite unhandled" << std::endl; #endif } +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO // We need EXTRA information here: are p/r/s overlapping? bool pr_ov = false, ps_ov = false, rs_ov = false; overlap_info(pi, pj, ri, rj, si, sj, pr_ov, ps_ov, rs_ov); +#else + // std::cout << "Boost.Geometry: skipping overlap_info" << std::endl; +#endif // One coming from right (#83,#90) // One coming from left (#90, #94, #95) @@ -326,12 +409,14 @@ private : { bool ret = false; +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO if (pr_ov || ps_ov) { int r = side_ri_p != 0 ? side_ri_p : side_si_p; ret = r * side_si_r == 1; } else +#endif { ret = side_si_r == 1; } @@ -348,6 +433,7 @@ private : // Take the one NOT overlapping bool ret = false; bool found = false; +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO if (pr_ov && ! ps_ov) { ret = true; @@ -358,6 +444,7 @@ private : ret = false; found = true; } +#endif debug_consider(0, left, right, header, false, "aligned", ret); if (found) @@ -366,11 +453,18 @@ private : } } -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << " iu/iu unhandled" << std::endl; - debug_consider(0, left, right, header, false, "unhandled", left.index < right.index); + debug_consider(0, left, right, header, false, "unhandled", left.turn_index < right.turn_index); #endif - return left.index < right.index; + if (! redo) + { + // In some cases behaviour is not symmetrical. TODO: fix this properly + // OR: alternatively we might consider calling all these functions one-way anyway + return ! consider_iu_iu(right, left, header, true); + } + + return left.turn_index < right.turn_index; } inline bool consider_ii(Indexed const& left, Indexed const& right, @@ -378,16 +472,8 @@ private : { debug_consider(0, left, right, header); - point_type pi, pj, ri, rj, si, sj; - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.seg_id, - pi, pj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.other_id, - ri, rj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - right.subject.other_id, - si, sj); + robust_point_type pi, pj, ri, rj, si, sj; + get_situation_map(left, right, pi, pj, ri, rj, si, sj); int const side_ri_p = m_strategy.apply(pi, pj, ri); int const side_si_p = m_strategy.apply(pi, pj, si); @@ -402,84 +488,89 @@ private : bool const ret = side_si_r != 1; return ret; } - return left.index < right.index; + return left.turn_index < right.turn_index; } public : inline bool operator()(Indexed const& left, Indexed const& right) const { - bool const default_order = left.index < right.index; + bool const default_order = left.turn_index < right.turn_index; - if ((m_turn_points[left.index].discarded || left.discarded) - && (m_turn_points[right.index].discarded || right.discarded)) + if ((m_turn_points[left.turn_index].discarded || left.discarded) + && (m_turn_points[right.turn_index].discarded || right.discarded)) { return default_order; } - else if (m_turn_points[left.index].discarded || left.discarded) + else if (m_turn_points[left.turn_index].discarded || left.discarded) { // Be careful to sort discarded first, then all others return true; } - else if (m_turn_points[right.index].discarded || right.discarded) + else if (m_turn_points[right.turn_index].discarded || right.discarded) { // See above so return false here such that right (discarded) // is sorted before left (not discarded) return false; } - else if (m_turn_points[left.index].combination(operation_blocked, operation_union) - && m_turn_points[right.index].combination(operation_blocked, operation_union)) + else if (m_turn_points[left.turn_index].combination(operation_blocked, operation_union) + && m_turn_points[right.turn_index].combination(operation_blocked, operation_union)) { // ux/ux return consider_ux_ux(left, right, "ux/ux"); } - else if (m_turn_points[left.index].both(operation_union) - && m_turn_points[right.index].both(operation_union)) + else if (m_turn_points[left.turn_index].both(operation_union) + && m_turn_points[right.turn_index].both(operation_union)) { // uu/uu, Order is arbitrary // Note: uu/uu is discarded now before so this point will // not be reached. return default_order; } - else if (m_turn_points[left.index].combination(operation_intersection, operation_union) - && m_turn_points[right.index].combination(operation_intersection, operation_union)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_union)) { return consider_iu_iu(left, right, "iu/iu"); } - else if (m_turn_points[left.index].both(operation_intersection) - && m_turn_points[right.index].both(operation_intersection)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_blocked) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_blocked)) + { + return consider_ix_ix(left, right, "ix/ix"); + } + else if (m_turn_points[left.turn_index].both(operation_intersection) + && m_turn_points[right.turn_index].both(operation_intersection)) { return consider_ii(left, right, "ii/ii"); } - else if (m_turn_points[left.index].combination(operation_union, operation_blocked) - && m_turn_points[right.index].combination(operation_intersection, operation_union)) + else if (m_turn_points[left.turn_index].combination(operation_union, operation_blocked) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_union)) { return consider_iu_ux(left, right, -1, "ux/iu"); } - else if (m_turn_points[left.index].combination(operation_intersection, operation_union) - && m_turn_points[right.index].combination(operation_union, operation_blocked)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union) + && m_turn_points[right.turn_index].combination(operation_union, operation_blocked)) { return consider_iu_ux(left, right, 1, "iu/ux"); } - else if (m_turn_points[left.index].combination(operation_intersection, operation_blocked) - && m_turn_points[right.index].combination(operation_intersection, operation_union)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_blocked) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_union)) { return consider_iu_ix(left, right, 1, "ix/iu"); } - else if (m_turn_points[left.index].combination(operation_intersection, operation_union) - && m_turn_points[right.index].combination(operation_intersection, operation_blocked)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_blocked)) { return consider_iu_ix(left, right, -1, "iu/ix"); } - else if (m_turn_points[left.index].method != method_equal - && m_turn_points[right.index].method == method_equal + else if (m_turn_points[left.turn_index].method != method_equal + && m_turn_points[right.turn_index].method == method_equal ) { // If one of them was EQUAL or CONTINUES, it should always come first return false; } - else if (m_turn_points[left.index].method == method_equal - && m_turn_points[right.index].method != method_equal + else if (m_turn_points[left.turn_index].method == method_equal + && m_turn_points[right.turn_index].method != method_equal ) { return true; @@ -487,13 +578,13 @@ public : // Now we have no clue how to sort. -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH - std::cout << " Consider: " << operation_char(m_turn_points[left.index].operations[0].operation) - << operation_char(m_turn_points[left.index].operations[1].operation) - << "/" << operation_char(m_turn_points[right.index].operations[0].operation) - << operation_char(m_turn_points[right.index].operations[1].operation) - << " " << " Take " << left.index << " < " << right.index - << std::cout; +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) + std::cout << " Consider: " << operation_char(m_turn_points[left.turn_index].operations[0].operation) + << operation_char(m_turn_points[left.turn_index].operations[1].operation) + << "/" << operation_char(m_turn_points[right.turn_index].operations[0].operation) + << operation_char(m_turn_points[right.turn_index].operations[1].operation) + << " " << " Take " << left.turn_index << " < " << right.turn_index + << std::endl; #endif return default_order; @@ -523,8 +614,8 @@ inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, std::map, int> inspection; for (Iterator it = begin_cluster; it != end_cluster; ++it) { - operation_type first = turn_points[it->index].operations[0].operation; - operation_type second = turn_points[it->index].operations[1].operation; + operation_type first = turn_points[it->turn_index].operations[0].operation; + operation_type second = turn_points[it->turn_index].operations[1].operation; if (first > second) { std::swap(first, second); @@ -553,7 +644,7 @@ inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, // Because (in case of not discarding iu) correctly ordering of ii/iu appears impossible for (Iterator it = begin_cluster; it != end_cluster; ++it) { - if (turn_points[it->index].combination(operation_intersection, operation_union)) + if (turn_points[it->turn_index].combination(operation_intersection, operation_union)) { it->discarded = true; } @@ -569,7 +660,7 @@ inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, if (! it->discarded) { nd_count++; - if (turn_points[it->index].both(operation_continue)) + if (turn_points[it->turn_index].both(operation_continue)) { cc_count++; } @@ -585,7 +676,7 @@ inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, { for (Iterator it = begin_cluster; it != end_cluster; ++it) { - if (turn_points[it->index].both(operation_continue)) + if (turn_points[it->turn_index].both(operation_continue)) { it->discarded = true; } @@ -602,12 +693,14 @@ template typename TurnPoints, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Strategy > inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, TurnPoints& turn_points, operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy& robust_policy, Strategy const& strategy) { // First inspect and (possibly) discard rows @@ -622,31 +715,31 @@ inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, TurnPoints, IndexType, Geometry1, Geometry2, + RobustPolicy, Reverse1, Reverse2, Strategy - >(turn_points, geometry1, geometry2, strategy)); - + >(turn_points, geometry1, geometry2, robust_policy, strategy)); -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) typedef typename IndexType::type operations_type; - operations_type const& op = turn_points[begin_cluster->index].operations[begin_cluster->operation_index]; - std::cout << "Clustered points on equal distance " << op.enriched.distance << std::endl; - std::cout << "->Indexes "; + operations_type const& op = turn_points[begin_cluster->turn_index].operations[begin_cluster->operation_index]; + std::cout << std::endl << "Clustered points on equal distance " << op.fraction << std::endl; + std::cout << "->Indexes "; for (Iterator it = begin_cluster; it != end_cluster; ++it) { - std::cout << " " << it->index; + std::cout << " " << it->turn_index; } std::cout << std::endl << "->Methods: "; for (Iterator it = begin_cluster; it != end_cluster; ++it) { - std::cout << " " << method_char(turn_points[it->index].method); + std::cout << " " << method_char(turn_points[it->turn_index].method); } std::cout << std::endl << "->Operations: "; for (Iterator it = begin_cluster; it != end_cluster; ++it) { - std::cout << " " << operation_char(turn_points[it->index].operations[0].operation) - << operation_char(turn_points[it->index].operations[1].operation); + std::cout << " " << operation_char(turn_points[it->turn_index].operations[0].operation) + << operation_char(turn_points[it->turn_index].operations[1].operation); } std::cout << std::endl << "->Discarded: "; for (Iterator it = begin_cluster; it != end_cluster; ++it) @@ -656,7 +749,7 @@ inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, std::cout << std::endl; //<< "\tOn segments: " << prev_op.seg_id << " / " << prev_op.other_id //<< " and " << op.seg_id << " / " << op.other_id - //<< geometry::distance(turn_points[prev->index].point, turn_points[it->index].point) + //<< geometry::distance(turn_points[prev->turn_index].point, turn_points[it->turn_index].point) #endif } diff --git a/boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp b/boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp new file mode 100644 index 0000000000..dd041b0d7d --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp @@ -0,0 +1,84 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP + + +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + +template +struct intersection_box_box +{ + template + < + typename Box1, typename Box2, + typename RobustPolicy, + typename BoxOut, + typename Strategy + > + static inline bool apply(Box1 const& box1, + Box2 const& box2, + RobustPolicy const& robust_policy, + BoxOut& box_out, + Strategy const& strategy) + { + typedef typename coordinate_type::type ct; + + ct min1 = get(box1); + ct min2 = get(box2); + ct max1 = get(box1); + ct max2 = get(box2); + + if (max1 < min2 || max2 < min1) + { + return false; + } + // Set dimensions of output coordinate + set(box_out, min1 < min2 ? min2 : min1); + set(box_out, max1 > max2 ? max2 : max1); + + return intersection_box_box + ::apply(box1, box2, robust_policy, box_out, strategy); + } +}; + +template +struct intersection_box_box +{ + template + < + typename Box1, typename Box2, + typename RobustPolicy, + typename BoxOut, + typename Strategy + > + static inline bool apply(Box1 const&, Box2 const&, + RobustPolicy const&, BoxOut&, Strategy const&) + { + return true; + } +}; + + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP diff --git a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index 8bca790d74..a13a627456 100644 --- a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -1,6 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -28,8 +33,17 @@ #include #include #include + +#include +#include +#include + #include +#include +#include + + #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) #include #endif @@ -41,31 +55,64 @@ namespace boost { namespace geometry namespace detail { namespace intersection { -template -< - typename Segment1, typename Segment2, - typename OutputIterator, typename PointOut, - typename Strategy -> +template struct intersection_segment_segment_point { + template + < + typename Segment1, typename Segment2, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > static inline OutputIterator apply(Segment1 const& segment1, - Segment2 const& segment2, OutputIterator out, + Segment2 const& segment2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) { typedef typename point_type::type point_type; + typedef typename geometry::robust_point_type + < + typename geometry::point_type::type, + RobustPolicy + >::type robust_point_type; + + // TODO: rescale segment -> robust points + robust_point_type pi_rob, pj_rob, qi_rob, qj_rob; + { + // Workaround: + point_type pi, pj, qi, qj; + assign_point_from_index<0>(segment1, pi); + assign_point_from_index<1>(segment1, pj); + assign_point_from_index<0>(segment2, qi); + assign_point_from_index<1>(segment2, qj); + geometry::recalculate(pi_rob, pi, robust_policy); + geometry::recalculate(pj_rob, pj, robust_policy); + geometry::recalculate(qi_rob, qi, robust_policy); + geometry::recalculate(qj_rob, qj, robust_policy); + } + // Get the intersection point (or two points) - segment_intersection_points is - = strategy::intersection::relate_cartesian_segments + typedef segment_intersection_points + < + point_type, + typename segment_ratio_type + < + point_type, RobustPolicy + >::type + > intersection_return_type; + + typedef strategy::intersection::relate_cartesian_segments < policies::relate::segments_intersection_points < - Segment1, - Segment2, - segment_intersection_points + intersection_return_type > - >::apply(segment1, segment2); + > policy; + + intersection_return_type is = policy::apply(segment1, segment2, + robust_policy, pi_rob, pj_rob, qi_rob, qj_rob); for (std::size_t i = 0; i < is.count; i++) { @@ -77,24 +124,31 @@ struct intersection_segment_segment_point } }; -template -< - typename Linestring1, typename Linestring2, - typename OutputIterator, typename PointOut, - typename Strategy -> +template struct intersection_linestring_linestring_point { + template + < + typename Linestring1, typename Linestring2, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > static inline OutputIterator apply(Linestring1 const& linestring1, - Linestring2 const& linestring2, OutputIterator out, + Linestring2 const& linestring2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) { typedef typename point_type::type point_type; - typedef detail::overlay::turn_info turn_info; + typedef detail::overlay::turn_info + < + point_type, + typename segment_ratio_type::type + > turn_info; std::deque turns; - geometry::get_intersection_points(linestring1, linestring2, turns); + geometry::get_intersection_points(linestring1, linestring2, robust_policy, turns); for (typename boost::range_iterator const>::type it = boost::begin(turns); it != boost::end(turns); ++it) @@ -112,25 +166,15 @@ struct intersection_linestring_linestring_point */ template < - typename LineString, typename Areal, bool ReverseAreal, - typename OutputIterator, typename LineStringOut, - overlay_type OverlayType, - typename Strategy + typename LineStringOut, + overlay_type OverlayType > struct intersection_of_linestring_with_areal { - typedef detail::overlay::follow - < - LineStringOut, - LineString, - Areal, - OverlayType - > follower; - #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) template - static inline void debug_follow(Turn const& turn, Operation op, + static inline void debug_follow(Turn const& turn, Operation op, int index) { std::cout << index @@ -145,7 +189,14 @@ struct intersection_of_linestring_with_areal } #endif + template + < + typename LineString, typename Areal, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > static inline OutputIterator apply(LineString const& linestring, Areal const& areal, + RobustPolicy const& robust_policy, OutputIterator out, Strategy const& ) { @@ -154,9 +205,20 @@ struct intersection_of_linestring_with_areal return out; } - typedef typename point_type::type point_type; + typedef detail::overlay::follow + < + LineStringOut, + LineString, + Areal, + OverlayType + > follower; - typedef detail::overlay::traversal_turn_info turn_info; + typedef typename point_type::type point_type; + typedef detail::overlay::traversal_turn_info + < + point_type, + typename geometry::segment_ratio_type::type + > turn_info; std::deque turns; detail::get_turns::no_interrupt_policy policy; @@ -164,12 +226,12 @@ struct intersection_of_linestring_with_areal < false, (OverlayType == overlay_intersection ? ReverseAreal : !ReverseAreal), - detail::overlay::calculate_distance_policy - >(linestring, areal, turns, policy); + detail::overlay::assign_null_policy + >(linestring, areal, robust_policy, turns, policy); if (turns.empty()) { - // No intersection points, it is either completely + // No intersection points, it is either completely // inside (interior + borders) // or completely outside @@ -181,8 +243,7 @@ struct intersection_of_linestring_with_areal return out; } - - if (follower::included(border_point, areal)) + if (follower::included(border_point, areal, robust_policy)) { LineStringOut copy; geometry::convert(linestring, copy); @@ -203,7 +264,7 @@ struct intersection_of_linestring_with_areal ( linestring, areal, geometry::detail::overlay::operation_intersection, - turns, out + turns, robust_policy, out ); } }; @@ -220,18 +281,22 @@ namespace dispatch template < - // tag dispatching: - typename TagIn1, typename TagIn2, typename TagOut, - // orientation - // metafunction finetuning helpers: - bool Areal1, bool Areal2, bool ArealOut, // real types typename Geometry1, typename Geometry2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, overlay_type OverlayType, - typename Strategy + // orientation + bool Reverse1 = detail::overlay::do_reverse::value>::value, + bool Reverse2 = detail::overlay::do_reverse::value>::value, + bool ReverseOut = detail::overlay::do_reverse::value>::value, + // tag dispatching: + typename TagIn1 = typename geometry::tag::type, + typename TagIn2 = typename geometry::tag::type, + typename TagOut = typename geometry::tag::type, + // metafunction finetuning helpers: + bool Areal1 = geometry::is_areal::value, + bool Areal2 = geometry::is_areal::value, + bool ArealOut = geometry::is_areal::value > struct intersection_insert { @@ -245,124 +310,106 @@ struct intersection_insert template < - typename TagIn1, typename TagIn2, typename TagOut, typename Geometry1, typename Geometry2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut, + typename TagIn1, typename TagIn2, typename TagOut > struct intersection_insert < - TagIn1, TagIn2, TagOut, - true, true, true, Geometry1, Geometry2, - Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + Reverse1, Reverse2, ReverseOut, + TagIn1, TagIn2, TagOut, + true, true, true > : detail::overlay::overlay - + {}; // Any areal type with box: template < - typename TagIn, typename TagOut, typename Geometry, typename Box, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut, + typename TagIn, typename TagOut > struct intersection_insert < - TagIn, box_tag, TagOut, - true, true, true, Geometry, Box, - Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + Reverse1, Reverse2, ReverseOut, + TagIn, box_tag, TagOut, + true, true, true > : detail::overlay::overlay - + {}; template < typename Segment1, typename Segment2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert < - segment_tag, segment_tag, point_tag, - false, false, false, Segment1, Segment2, + GeometryOut, + OverlayType, Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, - OverlayType, Strategy - > : detail::intersection::intersection_segment_segment_point - < - Segment1, Segment2, - OutputIterator, GeometryOut, - Strategy - > + segment_tag, segment_tag, point_tag, + false, false, false + > : detail::intersection::intersection_segment_segment_point {}; template < typename Linestring1, typename Linestring2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert < - linestring_tag, linestring_tag, point_tag, - false, false, false, Linestring1, Linestring2, + GeometryOut, + OverlayType, Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, - OverlayType, Strategy - > : detail::intersection::intersection_linestring_linestring_point - < - Linestring1, Linestring2, - OutputIterator, GeometryOut, - Strategy - > + linestring_tag, linestring_tag, point_tag, + false, false, false + > : detail::intersection::intersection_linestring_linestring_point {}; template < typename Linestring, typename Box, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, - overlay_type OverlayType, - typename Strategy + typename GeometryOut, + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert < - linestring_tag, box_tag, linestring_tag, - false, true, false, Linestring, Box, + GeometryOut, + overlay_intersection, Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, - OverlayType, - Strategy + linestring_tag, box_tag, linestring_tag, + false, true, false > { + template static inline OutputIterator apply(Linestring const& linestring, - Box const& box, OutputIterator out, Strategy const& ) + Box const& box, + RobustPolicy const& , + OutputIterator out, Strategy const& ) { typedef typename point_type::type point_type; strategy::intersection::liang_barsky lb_strategy; @@ -375,27 +422,23 @@ struct intersection_insert template < typename Linestring, typename Polygon, - bool ReverseLinestring, bool ReversePolygon, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool ReverseLinestring, bool ReversePolygon, bool ReverseOut > struct intersection_insert < - linestring_tag, polygon_tag, linestring_tag, - false, true, false, Linestring, Polygon, - ReverseLinestring, ReversePolygon, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + ReverseLinestring, ReversePolygon, ReverseOut, + linestring_tag, polygon_tag, linestring_tag, + false, true, false > : detail::intersection::intersection_of_linestring_with_areal < - Linestring, Polygon, ReversePolygon, - OutputIterator, GeometryOut, - OverlayType, - Strategy + GeometryOut, + OverlayType > {}; @@ -403,51 +446,48 @@ struct intersection_insert template < typename Linestring, typename Ring, - bool ReverseLinestring, bool ReverseRing, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool ReverseLinestring, bool ReverseRing, bool ReverseOut > struct intersection_insert < - linestring_tag, ring_tag, linestring_tag, - false, true, false, Linestring, Ring, - ReverseLinestring, ReverseRing, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + ReverseLinestring, ReverseRing, ReverseOut, + linestring_tag, ring_tag, linestring_tag, + false, true, false > : detail::intersection::intersection_of_linestring_with_areal < - Linestring, Ring, ReverseRing, - OutputIterator, GeometryOut, - OverlayType, - Strategy + GeometryOut, + OverlayType > {}; template < typename Segment, typename Box, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert < - segment_tag, box_tag, linestring_tag, - false, true, false, Segment, Box, - Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + Reverse1, Reverse2, ReverseOut, + segment_tag, box_tag, linestring_tag, + false, true, false > { + template static inline OutputIterator apply(Segment const& segment, - Box const& box, OutputIterator out, Strategy const& ) + Box const& box, + RobustPolicy const& ,// TODO: propagate to clip_range_with_box + OutputIterator out, Strategy const& ) { geometry::segment_view range(segment); @@ -460,37 +500,42 @@ struct intersection_insert template < - typename Tag1, typename Tag2, - bool Areal1, bool Areal2, typename Geometry1, typename Geometry2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename PointOut, + typename PointOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut, + typename Tag1, typename Tag2, + bool Areal1, bool Areal2 > struct intersection_insert < - Tag1, Tag2, point_tag, - Areal1, Areal2, false, Geometry1, Geometry2, - Reverse1, Reverse2, ReverseOut, - OutputIterator, PointOut, + PointOut, OverlayType, - Strategy + Reverse1, Reverse2, ReverseOut, + Tag1, Tag2, point_tag, + Areal1, Areal2, false > { + template static inline OutputIterator apply(Geometry1 const& geometry1, - Geometry2 const& geometry2, OutputIterator out, Strategy const& ) + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) { - typedef detail::overlay::turn_info turn_info; + typedef detail::overlay::turn_info + < + PointOut, + typename segment_ratio_type::type + > turn_info; std::vector turns; detail::get_turns::no_interrupt_policy policy; geometry::get_turns < false, false, detail::overlay::assign_null_policy - >(geometry1, geometry2, turns, policy); + >(geometry1, geometry2, robust_policy, turns, policy); for (typename std::vector::const_iterator it = turns.begin(); it != turns.end(); ++it) { @@ -504,35 +549,156 @@ struct intersection_insert template < - typename GeometryTag1, typename GeometryTag2, typename GeometryTag3, - bool Areal1, bool Areal2, bool ArealOut, - typename Geometry1, typename Geometry2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename Geometry1, typename Geometry2, typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert_reversed { + template static inline OutputIterator apply(Geometry1 const& g1, - Geometry2 const& g2, OutputIterator out, + Geometry2 const& g2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& strategy) { return intersection_insert < - GeometryTag2, GeometryTag1, GeometryTag3, - Areal2, Areal1, ArealOut, - Geometry2, Geometry1, - Reverse2, Reverse1, ReverseOut, - OutputIterator, GeometryOut, + Geometry2, Geometry1, GeometryOut, OverlayType, - Strategy - >::apply(g2, g1, out, strategy); + Reverse2, Reverse1, ReverseOut + >::apply(g2, g1, robust_policy, out, strategy); } }; +// dispatch for non-areal geometries +template +< + typename Geometry1, typename Geometry2, typename GeometryOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename TagIn1, typename TagIn2 +> +struct intersection_insert + < + Geometry1, Geometry2, GeometryOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + TagIn1, TagIn2, linestring_tag, + false, false, false + > : intersection_insert + < + Geometry1, Geometry2, GeometryOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + typename tag_cast::type, + typename tag_cast::type, + linestring_tag, + false, false, false + > +{}; + + +// dispatch for difference/intersection of linear geometries +template +< + typename Linear1, typename Linear2, typename LineStringOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Linear1, Linear2, LineStringOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + linear_tag, linear_tag, linestring_tag, + false, false, false + > : detail::overlay::linear_linear_linestring + < + Linear1, Linear2, LineStringOut, OverlayType + > +{}; + + +// dispatch for difference/intersection of point-like geometries + +template +< + typename Point1, typename Point2, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Point1, Point2, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + point_tag, point_tag, point_tag, + false, false, false + > : detail::overlay::point_point_point + < + Point1, Point2, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint, typename Point, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + MultiPoint, Point, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + multi_point_tag, point_tag, point_tag, + false, false, false + > : detail::overlay::multipoint_point_point + < + MultiPoint, Point, PointOut, OverlayType + > +{}; + + +template +< + typename Point, typename MultiPoint, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Point, MultiPoint, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + point_tag, multi_point_tag, point_tag, + false, false, false + > : detail::overlay::point_multipoint_point + < + Point, MultiPoint, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint1, typename MultiPoint2, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + MultiPoint1, MultiPoint2, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + multi_point_tag, multi_point_tag, point_tag, + false, false, false + > : detail::overlay::multipoint_multipoint_point + < + MultiPoint1, MultiPoint2, PointOut, OverlayType + > +{}; + + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH @@ -548,50 +714,37 @@ template bool ReverseSecond, overlay_type OverlayType, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename OutputIterator, typename Strategy > inline OutputIterator insert(Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy robust_policy, OutputIterator out, Strategy const& strategy) { return boost::mpl::if_c + < + geometry::reverse_dispatch::type::value, + geometry::dispatch::intersection_insert_reversed < - geometry::reverse_dispatch::type::value, - geometry::dispatch::intersection_insert_reversed - < - typename geometry::tag::type, - typename geometry::tag::type, - typename geometry::tag::type, - geometry::is_areal::value, - geometry::is_areal::value, - geometry::is_areal::value, - Geometry1, Geometry2, - overlay::do_reverse::value>::value, - overlay::do_reverse::value, ReverseSecond>::value, - overlay::do_reverse::value>::value, - OutputIterator, GeometryOut, - OverlayType, - Strategy - >, - geometry::dispatch::intersection_insert - < - typename geometry::tag::type, - typename geometry::tag::type, - typename geometry::tag::type, - geometry::is_areal::value, - geometry::is_areal::value, - geometry::is_areal::value, - Geometry1, Geometry2, - geometry::detail::overlay::do_reverse::value>::value, - geometry::detail::overlay::do_reverse::value, ReverseSecond>::value, - geometry::detail::overlay::do_reverse::value>::value, - OutputIterator, GeometryOut, - OverlayType, - Strategy - > - >::type::apply(geometry1, geometry2, out, strategy); + Geometry1, Geometry2, + GeometryOut, + OverlayType, + overlay::do_reverse::value>::value, + overlay::do_reverse::value, ReverseSecond>::value, + overlay::do_reverse::value>::value + >, + geometry::dispatch::intersection_insert + < + Geometry1, Geometry2, + GeometryOut, + OverlayType, + geometry::detail::overlay::do_reverse::value>::value, + geometry::detail::overlay::do_reverse::value, ReverseSecond>::value + > + >::type::apply(geometry1, geometry2, robust_policy, out, strategy); } @@ -630,10 +783,14 @@ inline OutputIterator intersection_insert(Geometry1 const& geometry1, concept::check(); concept::check(); + typedef typename Strategy::rescale_policy_type rescale_policy_type; + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, geometry2); + return detail::intersection::insert < GeometryOut, false, overlay_intersection - >(geometry1, geometry2, out, strategy); + >(geometry1, geometry2, robust_policy, out, strategy); } @@ -667,12 +824,18 @@ inline OutputIterator intersection_insert(Geometry1 const& geometry1, concept::check(); concept::check(); + typedef typename geometry::rescale_policy_type + < + typename geometry::point_type::type // TODO from both + >::type rescale_policy_type; + typedef strategy_intersection < typename cs_tag::type, Geometry1, Geometry2, - typename geometry::point_type::type + typename geometry::point_type::type, + rescale_policy_type > strategy; return intersection_insert(geometry1, geometry2, out, diff --git a/boost/geometry/algorithms/detail/overlay/linear_linear.hpp b/boost/geometry/algorithms/detail/overlay/linear_linear.hpp new file mode 100644 index 0000000000..3a7a7a7f3e --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/linear_linear.hpp @@ -0,0 +1,326 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +< + typename LineStringOut, + overlay_type OverlayType, + typename Geometry, + typename GeometryTag +> +struct linear_linear_no_intersections; + + +template +struct linear_linear_no_intersections + < + LineStringOut, overlay_difference, LineString, linestring_tag + > +{ + template + static inline OutputIterator apply(LineString const& linestring, + OutputIterator oit) + { + LineStringOut ls_out; + geometry::convert(linestring, ls_out); + *oit++ = ls_out; + return oit; + } +}; + + +template +struct linear_linear_no_intersections + < + LineStringOut, + overlay_difference, + MultiLineString, + multi_linestring_tag + > +{ + template + static inline OutputIterator apply(MultiLineString const& multilinestring, + OutputIterator oit) + { + for (typename boost::range_iterator::type + it = boost::begin(multilinestring); + it != boost::end(multilinestring); ++it) + { + LineStringOut ls_out; + geometry::convert(*it, ls_out); + *oit++ = ls_out; + } + return oit; + } +}; + + +template +struct linear_linear_no_intersections + < + LineStringOut, overlay_intersection, Geometry, GeometryTag + > +{ + template + static inline OutputIterator apply(Geometry const&, + OutputIterator oit) + { + return oit; + } +}; + + + + + + + +template +< + typename Linear1, + typename Linear2, + typename LinestringOut, + overlay_type OverlayType, + bool EnableFilterContinueTurns = false, + bool EnableRemoveDuplicateTurns = false, + bool EnableDegenerateTurns = true, +#ifdef BOOST_GEOMETRY_INTERSECTION_DO_NOT_INCLUDE_ISOLATED_POINTS + bool EnableFollowIsolatedPoints = false +#else + bool EnableFollowIsolatedPoints = true +#endif +> +class linear_linear_linestring +{ +protected: + struct assign_policy + { + static bool const include_no_turn = false; + static bool const include_degenerate = EnableDegenerateTurns; + static bool const include_opposite = false; + + template + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply(Info& , Point1 const& , Point2 const& , + IntersectionInfo const& , DirInfo const& ) + { + } + }; + + + template + < + typename Turns, + typename LinearGeometry1, + typename LinearGeometry2 + > + static inline void compute_turns(Turns& turns, + LinearGeometry1 const& linear1, + LinearGeometry2 const& linear2) + { + turns.clear(); + geometry::detail::relate::turns::get_turns + < + LinearGeometry1, + LinearGeometry2, + detail::get_turns::get_turn_info_type + < + LinearGeometry1, + LinearGeometry2, + assign_policy + > + >::apply(turns, linear1, linear2); + } + + + template + < + overlay_type OverlayTypeForFollow, + bool FollowIsolatedPoints, + typename Turns, + typename LinearGeometry1, + typename LinearGeometry2, + typename OutputIterator + > + static inline OutputIterator + sort_and_follow_turns(Turns& turns, + LinearGeometry1 const& linear1, + LinearGeometry2 const& linear2, + OutputIterator oit) + { + // remove turns that have no added value + turns::filter_continue_turns + < + Turns, + EnableFilterContinueTurns && OverlayType != overlay_intersection + >::apply(turns); + + // sort by seg_id, distance, and operation + std::sort(boost::begin(turns), boost::end(turns), + detail::turns::less_seg_fraction_other_op<>()); + + // remove duplicate turns + turns::remove_duplicate_turns + < + Turns, EnableRemoveDuplicateTurns + >::apply(turns); + + return detail::overlay::following::linear::follow + < + LinestringOut, + LinearGeometry1, + LinearGeometry2, + OverlayTypeForFollow, + FollowIsolatedPoints, + !EnableFilterContinueTurns || OverlayType == overlay_intersection + >::apply(linear1, linear2, boost::begin(turns), boost::end(turns), + oit); + } + +public: + template + < + typename RobustPolicy, typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(Linear1 const& linear1, + Linear2 const& linear2, + RobustPolicy const&, + OutputIterator oit, + Strategy const& ) + { + typedef typename detail::relate::turns::get_turns + < + Linear1, Linear2 + >::turn_info turn_info; + + typedef std::vector turns_container; + + turns_container turns; + compute_turns(turns, linear1, linear2); + + if ( turns.empty() ) + { + // the two linear geometries are disjoint + return linear_linear_no_intersections + < + LinestringOut, + OverlayType, + Linear1, + typename tag::type + >::apply(linear1, oit); + } + + return sort_and_follow_turns + < + OverlayType, + EnableFollowIsolatedPoints + && OverlayType == overlay_intersection + >(turns, linear1, linear2, oit); + } +}; + + + + +template +< + typename Linear1, + typename Linear2, + typename LinestringOut, + bool EnableFilterContinueTurns, + bool EnableRemoveDuplicateTurns, + bool EnableDegenerateTurns, + bool EnableFollowIsolatedPoints +> +struct linear_linear_linestring + < + Linear1, Linear2, LinestringOut, overlay_union, + EnableFilterContinueTurns, EnableRemoveDuplicateTurns, + EnableDegenerateTurns, EnableFollowIsolatedPoints + > +{ + template + < + typename RobustPolicy, typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(Linear1 const& linear1, + Linear2 const& linear2, + RobustPolicy const& robust_policy, + OutputIterator oit, + Strategy const& strategy) + { + oit = linear_linear_no_intersections + < + LinestringOut, + overlay_difference, + Linear1, + typename tag::type + >::apply(linear1, oit); + + return linear_linear_linestring + < + Linear2, Linear1, LinestringOut, overlay_difference, + EnableFilterContinueTurns, EnableRemoveDuplicateTurns, + EnableDegenerateTurns, EnableFollowIsolatedPoints + >::apply(linear2, linear1, robust_policy, oit, strategy); + } +}; + + + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP diff --git a/boost/geometry/algorithms/detail/overlay/overlay.hpp b/boost/geometry/algorithms/detail/overlay/overlay.hpp index 41665e0af1..44b5a0df3c 100644 --- a/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -17,7 +18,6 @@ #include -#include #include #include #include @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -34,12 +35,19 @@ #include #include #include +#include + +#include #ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE # include #endif +#ifdef BOOST_GEOMETRY_TIME_OVERLAY +# include +#endif + namespace boost { namespace geometry { @@ -66,19 +74,17 @@ inline void map_turns(Map& map, TurnPoints const& turn_points) typedef typename boost::range_value::type turn_point_type; typedef typename turn_point_type::container_type container_type; - int index = 0; for (typename boost::range_iterator::type it = boost::begin(turn_points); it != boost::end(turn_points); - ++it, ++index) + ++it) { if (! skip(*it)) { - int op_index = 0; for (typename boost::range_iterator::type op_it = boost::begin(it->operations); op_it != boost::end(it->operations); - ++op_it, ++op_index) + ++op_it) { ring_identifier ring_id ( @@ -110,6 +116,12 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, typedef ring_properties::type> properties; +// Silence warning C4127: conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + // Union: return either of them // Intersection: return nothing // Difference: return first of them @@ -120,6 +132,11 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, return out; } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + std::map empty; std::map all_of_one_of_them; @@ -134,25 +151,26 @@ template < typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, - overlay_type Direction, - typename Strategy + typename GeometryOut, + overlay_type Direction > struct overlay { + template static inline OutputIterator apply( Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, OutputIterator out, Strategy const& ) { - if (geometry::num_points(geometry1) == 0 - && geometry::num_points(geometry2) == 0) + if ( geometry::num_points(geometry1) == 0 + && geometry::num_points(geometry2) == 0 ) { return out; } - if (geometry::num_points(geometry1) == 0 - || geometry::num_points(geometry2) == 0) + if ( geometry::num_points(geometry1) == 0 + || geometry::num_points(geometry2) == 0 ) { return return_if_one_input_is_empty < @@ -161,7 +179,11 @@ struct overlay } typedef typename geometry::point_type::type point_type; - typedef detail::overlay::traversal_turn_info turn_info; + typedef detail::overlay::traversal_turn_info + < + point_type, + typename geometry::segment_ratio_type::type + > turn_info; typedef std::deque container_type; typedef std::deque @@ -182,8 +204,8 @@ std::cout << "get turns" << std::endl; geometry::get_turns < Reverse1, Reverse2, - detail::overlay::calculate_distance_policy - >(geometry1, geometry2, turn_points, policy); + detail::overlay::assign_null_policy + >(geometry1, geometry2, robust_policy, turn_points, policy); #ifdef BOOST_GEOMETRY_TIME_OVERLAY std::cout << "get_turns: " << timer.elapsed() << std::endl; @@ -198,6 +220,7 @@ std::cout << "enrich" << std::endl; ? geometry::detail::overlay::operation_union : geometry::detail::overlay::operation_intersection, geometry1, geometry2, + robust_policy, side_strategy); #ifdef BOOST_GEOMETRY_TIME_OVERLAY @@ -218,6 +241,7 @@ std::cout << "traverse" << std::endl; Direction == overlay_union ? geometry::detail::overlay::operation_union : geometry::detail::overlay::operation_intersection, + robust_policy, turn_points, rings ); @@ -248,8 +272,8 @@ std::cout << "traverse" << std::endl; ring_identifier id(2, 0, -1); for (typename boost::range_iterator::type it = boost::begin(rings); - it != boost::end(rings); - ++it) + it != boost::end(rings); + ++it) { selected[id] = properties(*it, true); selected[id].reversed = ReverseOut; @@ -273,24 +297,6 @@ std::cout << "traverse" << std::endl; }; -// Metafunction helper for intersection and union -template -struct do_reverse {}; - -template <> -struct do_reverse : boost::false_type {}; - -template <> -struct do_reverse : boost::true_type {}; - -template <> -struct do_reverse : boost::true_type {}; - -template <> -struct do_reverse : boost::false_type {}; - - - }} // namespace detail::overlay #endif // DOXYGEN_NO_DETAIL diff --git a/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp b/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp new file mode 100644 index 0000000000..0af062d271 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp @@ -0,0 +1,435 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +// struct for copying points of the pointlike geometries to output +template +< + typename PointOut, + typename GeometryIn, + typename TagIn = typename tag::type +> +struct copy_points + : not_implemented +{}; + +template +struct copy_points +{ + template + static inline void apply(PointIn const& point_in, + OutputIterator& oit) + { + PointOut point_out; + geometry::convert(point_in, point_out); + *oit++ = point_out; + } +}; + + +template +struct copy_points +{ + template + static inline void apply(MultiPointIn const& multi_point_in, + OutputIterator& oit) + { + for (typename boost::range_iterator::type + it = boost::begin(multi_point_in); + it != boost::end(multi_point_in); ++it) + { + PointOut point_out; + geometry::convert(*it, point_out); + *oit++ = point_out; + } + } +}; + + + +// action struct for difference/intersection +template +struct action_selector_pl_pl +{}; + +template +struct action_selector_pl_pl +{ + template + < + typename Point, + typename OutputIterator + > + static inline void apply(Point const& point, + bool is_common, + OutputIterator& oit) + { + if ( is_common ) + { + copy_points::apply(point, oit); + } + } +}; + + + +template +struct action_selector_pl_pl +{ + template + < + typename Point, + typename OutputIterator + > + static inline void apply(Point const& point, + bool is_common, + OutputIterator& oit) + { + if ( !is_common ) + { + copy_points::apply(point, oit); + } + } +}; + + +//=========================================================================== + +// difference/intersection of point-point +template +< + typename Point1, + typename Point2, + typename PointOut, + overlay_type OverlayType +> +struct point_point_point +{ + template + static inline OutputIterator apply(Point1 const& point1, + Point2 const& point2, + RobustPolicy const& , + OutputIterator oit, + Strategy const&) + { + action_selector_pl_pl + < + PointOut, OverlayType + >::apply(point1, + detail::equals::equals_point_point(point1, point2), + oit); + + return oit; + } +}; + + + +// difference of multipoint-point +// +// the apply method in the following struct is called only for +// difference; for intersection the reversal will +// always call the point-multipoint version +template +< + typename MultiPoint, + typename Point, + typename PointOut, + overlay_type OverlayType +> +struct multipoint_point_point +{ + template + static inline OutputIterator apply(MultiPoint const& multipoint, + Point const& point, + RobustPolicy const& , + OutputIterator oit, + Strategy const&) + { + BOOST_ASSERT( OverlayType == overlay_difference ); + + for (typename boost::range_iterator::type + it = boost::begin(multipoint); + it != boost::end(multipoint); ++it) + { + action_selector_pl_pl + < + PointOut, OverlayType + >::apply(*it, + detail::equals::equals_point_point(*it, point), + oit); + } + + return oit; + } +}; + + +// difference/intersection of point-multipoint +template +< + typename Point, + typename MultiPoint, + typename PointOut, + overlay_type OverlayType +> +struct point_multipoint_point +{ + template + static inline OutputIterator apply(Point const& point, + MultiPoint const& multipoint, + RobustPolicy const& , + OutputIterator oit, + Strategy const&) + { + typedef action_selector_pl_pl action; + + for (typename boost::range_iterator::type + it = boost::begin(multipoint); + it != boost::end(multipoint); ++it) + { + if ( detail::equals::equals_point_point(*it, point) ) + { + action::apply(point, true, oit); + return oit; + } + } + + action::apply(point, false, oit); + return oit; + } +}; + + + +// difference/intersection of multipoint-multipoint +template +< + typename MultiPoint1, + typename MultiPoint2, + typename PointOut, + overlay_type OverlayType +> +struct multipoint_multipoint_point +{ + template + static inline OutputIterator apply(MultiPoint1 const& multipoint1, + MultiPoint2 const& multipoint2, + RobustPolicy const& robust_policy, + OutputIterator oit, + Strategy const& strategy) + { + if ( OverlayType != overlay_difference + && boost::size(multipoint1) > boost::size(multipoint2) ) + { + return multipoint_multipoint_point + < + MultiPoint2, MultiPoint1, PointOut, OverlayType + >::apply(multipoint2, multipoint1, robust_policy, oit, strategy); + } + + std::vector::type> + points2(boost::begin(multipoint2), boost::end(multipoint2)); + + std::sort(points2.begin(), points2.end(), detail::relate::less()); + + for (typename boost::range_iterator::type + it1 = boost::begin(multipoint1); + it1 != boost::end(multipoint1); ++it1) + { + bool found = std::binary_search(points2.begin(), points2.end(), + *it1, detail::relate::less()); + + action_selector_pl_pl + < + PointOut, OverlayType + >::apply(*it1, found, oit); + } + return oit; + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +//=========================================================================== + + +#ifndef DOXYGEN_NO_DISPATCH +namespace detail_dispatch { namespace overlay +{ + +// dispatch struct for pointlike-pointlike difference/intersection +// computation +template +< + typename PointLike1, + typename PointLike2, + typename PointOut, + overlay_type OverlayType, + typename Tag1, + typename Tag2 +> +struct pointlike_pointlike_point + : not_implemented +{}; + + +template +< + typename Point1, + typename Point2, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + Point1, Point2, PointOut, OverlayType, + point_tag, point_tag + > : detail::overlay::point_point_point + < + Point1, Point2, PointOut, OverlayType + > +{}; + + +template +< + typename Point, + typename MultiPoint, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + Point, MultiPoint, PointOut, OverlayType, + point_tag, multi_point_tag + > : detail::overlay::point_multipoint_point + < + Point, MultiPoint, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint, + typename Point, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + MultiPoint, Point, PointOut, OverlayType, + multi_point_tag, point_tag + > : detail::overlay::multipoint_point_point + < + MultiPoint, Point, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint1, + typename MultiPoint2, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + MultiPoint1, MultiPoint2, PointOut, OverlayType, + multi_point_tag, multi_point_tag + > : detail::overlay::multipoint_multipoint_point + < + MultiPoint1, MultiPoint2, PointOut, OverlayType + > +{}; + + +}} // namespace detail_dispatch::overlay +#endif // DOXYGEN_NO_DISPATCH + + +//=========================================================================== + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +// generic pointlike-pointlike union implementation +template +< + typename PointLike1, + typename PointLike2, + typename PointOut +> +struct union_pointlike_pointlike_point +{ + template + static inline OutputIterator apply(PointLike1 const& pointlike1, + PointLike2 const& pointlike2, + RobustPolicy const& robust_policy, + OutputIterator oit, + Strategy const& strategy) + { + copy_points::apply(pointlike1, oit); + + return detail_dispatch::overlay::pointlike_pointlike_point + < + PointLike2, PointLike1, PointOut, overlay_difference, + typename tag::type, + typename tag::type + >::apply(pointlike2, pointlike1, robust_policy, oit, strategy); + } + +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP diff --git a/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp b/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp index 007113ffba..516ec349e8 100644 --- a/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp +++ b/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp @@ -14,19 +14,19 @@ # define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER #endif +#if defined(BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER) +#include +#endif -#include - - -#include -#include +#include namespace boost { namespace geometry { + // Internal struct to uniquely identify a segment // on a linestring,ring // or polygon (needs ring_index) @@ -40,7 +40,10 @@ struct segment_identifier , segment_index(-1) {} - inline segment_identifier(int src, int mul, int rin, int seg) + inline segment_identifier(signed_index_type src, + signed_index_type mul, + signed_index_type rin, + signed_index_type seg) : source_index(src) , multi_index(mul) , ring_index(rin) @@ -68,20 +71,20 @@ struct segment_identifier #if defined(BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER) friend std::ostream& operator<<(std::ostream &os, segment_identifier const& seg_id) { - std::cout + os << "s:" << seg_id.source_index - << ", v:" << seg_id.segment_index // ~vertex + << ", v:" << seg_id.segment_index // v:vertex because s is used for source ; - if (seg_id.ring_index >= 0) std::cout << ", r:" << seg_id.ring_index; - if (seg_id.multi_index >= 0) std::cout << ", m:" << seg_id.multi_index; + if (seg_id.ring_index >= 0) os << ", r:" << seg_id.ring_index; + if (seg_id.multi_index >= 0) os << ", m:" << seg_id.multi_index; return os; } #endif - int source_index; - int multi_index; - int ring_index; - int segment_index; + signed_index_type source_index; + signed_index_type multi_index; + signed_index_type ring_index; + signed_index_type segment_index; }; diff --git a/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/boost/geometry/algorithms/detail/overlay/select_rings.hpp index f664b19514..385658a190 100644 --- a/boost/geometry/algorithms/detail/overlay/select_rings.hpp +++ b/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,11 +10,16 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP + #include +#include + +#include #include #include +#include #include #include #include @@ -40,14 +46,14 @@ namespace dispatch struct select_rings { template - static inline void apply(Box const& box, Geometry const& , + static inline void apply(Box const& box, Geometry const& , ring_identifier const& id, Map& map, bool midpoint) { map[id] = typename Map::mapped_type(box, midpoint); } template - static inline void apply(Box const& box, + static inline void apply(Box const& box, ring_identifier const& id, Map& map, bool midpoint) { map[id] = typename Map::mapped_type(box, midpoint); @@ -68,7 +74,7 @@ namespace dispatch } template - static inline void apply(Ring const& ring, + static inline void apply(Ring const& ring, ring_identifier const& id, Map& map, bool midpoint) { if (boost::size(ring) > 0) @@ -91,9 +97,10 @@ namespace dispatch per_ring::apply(exterior_ring(polygon), geometry, id, map, midpoint); - typename interior_return_type::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + typename interior_return_type::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; per_ring::apply(*it, geometry, id, map, midpoint); @@ -109,16 +116,42 @@ namespace dispatch per_ring::apply(exterior_ring(polygon), id, map, midpoint); - typename interior_return_type::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + typename interior_return_type::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; per_ring::apply(*it, id, map, midpoint); } } }; -} + + template + struct select_rings + { + template + static inline void apply(Multi const& multi, Geometry const& geometry, + ring_identifier id, Map& map, bool midpoint) + { + typedef typename boost::range_iterator + < + Multi const + >::type iterator_type; + + typedef select_rings::type> per_polygon; + + id.multi_index = 0; + for (iterator_type it = boost::begin(multi); it != boost::end(multi); ++it) + { + id.ring_index = -1; + per_polygon::apply(*it, geometry, id, map, midpoint); + id.multi_index++; + } + } + }; + +} // namespace dispatch template @@ -213,7 +246,7 @@ inline void update_selection_map(Geometry1 const& geometry1, typename SelectionMap::mapped_type properties = it->second; // Copy by value // Calculate the "within code" (previously this was done earlier but is - // must efficienter here - it can be even more efficient doing it all at once, + // much efficienter here - it can be even more efficient doing it all at once, // using partition, TODO) // So though this is less elegant than before, it avoids many unused point-in-poly calculations switch(id.source_index) @@ -248,7 +281,7 @@ template typename IntersectionMap, typename SelectionMap > inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2, - IntersectionMap const& intersection_map, + IntersectionMap const& intersection_map, SelectionMap& selection_map, bool midpoint) { typedef typename geometry::tag::type tag1; @@ -271,16 +304,16 @@ template typename IntersectionMap, typename SelectionMap > inline void select_rings(Geometry const& geometry, - IntersectionMap const& intersection_map, + IntersectionMap const& intersection_map, SelectionMap& selection_map, bool midpoint) { typedef typename geometry::tag::type tag; SelectionMap map_with_all; - dispatch::select_rings::apply(geometry, + dispatch::select_rings::apply(geometry, ring_identifier(0, -1, -1), map_with_all, midpoint); - update_selection_map(geometry, geometry, intersection_map, + update_selection_map(geometry, geometry, intersection_map, map_with_all, selection_map); } diff --git a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp index 9c4c99394e..8dffeae283 100644 --- a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -9,16 +9,18 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP + #include #include #include #include +#include #include -#include +#include #include #include @@ -55,17 +57,21 @@ template typename Geometry, typename Turns, typename TurnPolicy, + typename RobustPolicy, typename InterruptPolicy > struct self_section_visitor { Geometry const& m_geometry; + RobustPolicy const& m_rescale_policy; Turns& m_turns; InterruptPolicy& m_interrupt_policy; inline self_section_visitor(Geometry const& g, + RobustPolicy const& rp, Turns& turns, InterruptPolicy& ip) : m_geometry(g) + , m_rescale_policy(rp) , m_turns(turns) , m_interrupt_policy(ip) {} @@ -82,12 +88,12 @@ struct self_section_visitor Geometry, Geometry, false, false, Section, Section, - Turns, TurnPolicy, - InterruptPolicy + TurnPolicy >::apply( 0, m_geometry, sec1, 0, m_geometry, sec2, false, + m_rescale_policy, m_turns, m_interrupt_policy); } if (m_interrupt_policy.has_intersections) @@ -103,17 +109,13 @@ struct self_section_visitor -template -< - typename Geometry, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy -> +template struct get_turns { + template static inline bool apply( Geometry const& geometry, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { @@ -127,20 +129,20 @@ struct get_turns > sections_type; sections_type sec; - geometry::sectionalize(geometry, sec); + geometry::sectionalize(geometry, robust_policy, false, sec); self_section_visitor < Geometry, - Turns, TurnPolicy, InterruptPolicy - > visitor(geometry, turns, interrupt_policy); + Turns, TurnPolicy, RobustPolicy, InterruptPolicy + > visitor(geometry, robust_policy, turns, interrupt_policy); try { geometry::partition < - box_type, - detail::get_turns::get_section_box, + box_type, + detail::get_turns::get_section_box, detail::get_turns::ovelaps_section_box >::apply(sec, visitor); } @@ -166,9 +168,7 @@ template < typename GeometryTag, typename Geometry, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points { @@ -178,44 +178,32 @@ struct self_get_turn_points template < typename Ring, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points < ring_tag, Ring, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > - : detail::self_get_turn_points::get_turns - < - Ring, - Turns, - TurnPolicy, - InterruptPolicy - > + : detail::self_get_turn_points::get_turns {}; template < typename Box, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points < box_tag, Box, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > { + template static inline bool apply( Box const& , + RobustPolicy const& , Turns& , InterruptPolicy& ) { @@ -227,24 +215,28 @@ struct self_get_turn_points template < typename Polygon, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points < polygon_tag, Polygon, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy + > + : detail::self_get_turn_points::get_turns +{}; + + +template +< + typename MultiPolygon, + typename TurnPolicy +> +struct self_get_turn_points + < + multi_polygon_tag, MultiPolygon, + TurnPolicy > - : detail::self_get_turn_points::get_turns - < - Polygon, - Turns, - TurnPolicy, - InterruptPolicy - > + : detail::self_get_turn_points::get_turns {}; @@ -259,6 +251,7 @@ struct self_get_turn_points \tparam Turns type of intersection container (e.g. vector of "intersection/turn point"'s) \param geometry geometry + \param robust_policy policy to handle robustness issues \param turns container which will contain intersection points \param interrupt_policy policy determining if process is stopped when intersection is found @@ -267,38 +260,24 @@ template < typename AssignPolicy, typename Geometry, + typename RobustPolicy, typename Turns, typename InterruptPolicy > inline void self_turns(Geometry const& geometry, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { concept::check(); - typedef typename strategy_intersection - < - typename cs_tag::type, - Geometry, - Geometry, - typename boost::range_value::type - >::segment_intersection_strategy_type strategy_type; - - typedef detail::overlay::get_turn_info - < - typename point_type::type, - typename point_type::type, - typename boost::range_value::type, - detail::overlay::assign_null_policy - > TurnPolicy; + typedef detail::overlay::get_turn_info turn_policy; dispatch::self_get_turn_points < typename tag::type, Geometry, - Turns, - TurnPolicy, - InterruptPolicy - >::apply(geometry, turns, interrupt_policy); + turn_policy + >::apply(geometry, robust_policy, turns, interrupt_policy); } diff --git a/boost/geometry/algorithms/detail/overlay/stream_info.hpp b/boost/geometry/algorithms/detail/overlay/stream_info.hpp index eebe381944..51fd1b3dca 100644 --- a/boost/geometry/algorithms/detail/overlay/stream_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/stream_info.hpp @@ -35,7 +35,6 @@ namespace detail { namespace overlay template std::ostream& operator<<(std::ostream &os, turn_info

const& info) { - typename geometry::coordinate_type

::type d = info.distance; os << "\t" << " src " << info.seg_id.source_index << " seg " << info.seg_id.segment_index @@ -54,7 +53,7 @@ namespace detail { namespace overlay << " nxt seg " << info.travels_to_vertex_index << " , ip " << info.travels_to_ip_index << " , or " << info.next_ip_index - << " dst " << double(d) + << " frac " << info.fraction << info.visit_state; if (info.flagged) { diff --git a/boost/geometry/algorithms/detail/overlay/traversal_info.hpp b/boost/geometry/algorithms/detail/overlay/traversal_info.hpp index 810a27af04..6ee32c17c0 100644 --- a/boost/geometry/algorithms/detail/overlay/traversal_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/traversal_info.hpp @@ -24,15 +24,21 @@ namespace detail { namespace overlay { -template -struct traversal_turn_operation : public turn_operation +template +struct traversal_turn_operation : public turn_operation { - enrichment_info

enriched; + enrichment_info enriched; visit_info visited; }; -template -struct traversal_turn_info : public turn_info > +template +struct traversal_turn_info + : public turn_info + < + Point, + SegmentRatio, + traversal_turn_operation + > {}; diff --git a/boost/geometry/algorithms/detail/overlay/traverse.hpp b/boost/geometry/algorithms/detail/overlay/traverse.hpp index 12daafa0cf..59d2ba703e 100644 --- a/boost/geometry/algorithms/detail/overlay/traverse.hpp +++ b/boost/geometry/algorithms/detail/overlay/traverse.hpp @@ -13,11 +13,12 @@ #include -#include #include #include #include +#include #include +#include #include #include @@ -38,7 +39,7 @@ namespace detail { namespace overlay template #ifdef BOOST_GEOMETRY_DEBUG_TRAVERSE -inline void debug_traverse(Turn const& turn, Operation op, +inline void debug_traverse(Turn const& turn, Operation op, std::string const& header) { std::cout << header @@ -57,7 +58,7 @@ inline void debug_traverse(Turn const& turn, Operation op, } } #else -inline void debug_traverse(Turn const& , Operation, std::string const& ) +inline void debug_traverse(Turn const& , Operation, const char*) { } #endif @@ -92,14 +93,16 @@ template typename G1, typename G2, typename Turns, - typename IntersectionInfo + typename IntersectionInfo, + typename RobustPolicy > inline bool assign_next_ip(G1 const& g1, G2 const& g2, Turns& turns, typename boost::range_iterator::type& ip, GeometryOut& current_output, IntersectionInfo& info, - segment_identifier& seg_id) + segment_identifier& seg_id, + RobustPolicy const& robust_policy) { info.visited.set_visited(); set_visited_for_continue(*ip, info); @@ -107,7 +110,7 @@ inline bool assign_next_ip(G1 const& g1, G2 const& g2, // If there is no next IP on this segment if (info.enriched.next_ip_index < 0) { - if (info.enriched.travels_to_vertex_index < 0 + if (info.enriched.travels_to_vertex_index < 0 || info.enriched.travels_to_ip_index < 0) { return false; @@ -120,12 +123,14 @@ inline bool assign_next_ip(G1 const& g1, G2 const& g2, { geometry::copy_segments(g1, info.seg_id, info.enriched.travels_to_vertex_index, + robust_policy, current_output); } else { geometry::copy_segments(g2, info.seg_id, info.enriched.travels_to_vertex_index, + robust_policy, current_output); } seg_id = info.seg_id; @@ -137,12 +142,16 @@ inline bool assign_next_ip(G1 const& g1, G2 const& g2, seg_id = info.seg_id; } - detail::overlay::append_no_duplicates(current_output, ip->point); + detail::overlay::append_no_dups_or_spikes(current_output, ip->point, + robust_policy); + return true; } -inline bool select_source(operation_type operation, int source1, int source2) +inline bool select_source(operation_type operation, + signed_index_type source1, + signed_index_type source2) { return (operation == operation_intersection && source1 != source2) || (operation == operation_union && source1 == source2) @@ -227,12 +236,14 @@ template class traverse { public : - template + template static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, detail::overlay::operation_type operation, + RobustPolicy const& robust_policy, Turns& turns, Rings& rings) { + typedef typename boost::range_value::type ring_type; typedef typename boost::range_iterator::type turn_iterator; typedef typename boost::range_value::type turn_type; typedef typename boost::range_iterator @@ -240,6 +251,12 @@ public : typename turn_type::container_type >::type turn_operation_iterator_type; + std::size_t const min_num_points + = core_detail::closure::minimum_ring_size + < + geometry::closure::value + >::value; + std::size_t size_at_start = boost::size(rings); typename Backtrack::state_type state; @@ -253,7 +270,7 @@ public : ++it) { // Skip discarded ones - if (! (it->is_discarded() || it->blocked())) + if (! (it->discarded || ! it->selectable_start || it->blocked())) { for (turn_operation_iterator_type iit = boost::begin(it->operations); state.good() && iit != boost::end(it->operations); @@ -267,9 +284,9 @@ public : { set_visited_for_continue(*it, *iit); - typename boost::range_value::type current_output; - detail::overlay::append_no_duplicates(current_output, - it->point, true); + ring_type current_output; + detail::overlay::append_no_dups_or_spikes(current_output, + it->point, robust_policy); turn_iterator current = it; turn_operation_iterator_type current_iit = iit; @@ -279,13 +296,14 @@ public : geometry1, geometry2, turns, current, current_output, - *iit, current_seg_id)) + *iit, current_seg_id, + robust_policy)) { Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *current_iit, "No next IP", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); } if (! detail::overlay::select_next_ip( @@ -295,10 +313,10 @@ public : current_iit)) { Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *iit, "Dead end at start", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); } else { @@ -308,7 +326,7 @@ public : detail::overlay::debug_traverse(*current, *current_iit, "Selected "); - unsigned int i = 0; + typename boost::range_size::type i = 0; while (current_iit != iit && state.good()) { @@ -317,10 +335,10 @@ public : // It visits a visited node again, without passing the start node. // This makes it suspicious for endless loops Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *iit, "Visit again", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); } else { @@ -339,7 +357,8 @@ public : detail::overlay::assign_next_ip( geometry1, geometry2, turns, current, current_output, - *current_iit, current_seg_id); + *current_iit, current_seg_id, + robust_policy); if (! detail::overlay::select_next_ip( operation, @@ -351,12 +370,15 @@ public : // Should not occur in self-intersecting polygons without spikes // Might occur in polygons with spikes Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *iit, "Dead end", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); + } + else + { + detail::overlay::debug_traverse(*current, *current_iit, "Selected "); } - detail::overlay::debug_traverse(*current, *current_iit, "Selected "); if (i++ > 2 + 2 * turns.size()) { @@ -364,10 +386,10 @@ public : // than turn points. // Turn points marked as "ii" can be visited twice. Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *iit, "Endless loop", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); } } } @@ -376,7 +398,11 @@ public : { iit->visited.set_finished(); detail::overlay::debug_traverse(*current, *iit, "->Finished"); - rings.push_back(current_output); + if (geometry::num_points(current_output) >= min_num_points) + { + clean_closing_dups_and_spikes(current_output, robust_policy); + rings.push_back(current_output); + } } } } diff --git a/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/boost/geometry/algorithms/detail/overlay/turn_info.hpp index 89a60b21ab..26669a4b1f 100644 --- a/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -54,11 +54,12 @@ enum method_type The class is to be included in the turn_info class, either direct or a derived or similar class with more (e.g. enrichment) information. */ +template struct turn_operation { operation_type operation; segment_identifier seg_id; - segment_identifier other_id; + SegmentRatio fraction; inline turn_operation() : operation(operation_none) @@ -78,7 +79,8 @@ struct turn_operation template < typename Point, - typename Operation = turn_operation, + typename SegmentRatio, + typename Operation = turn_operation, typename Container = boost::array > struct turn_info @@ -90,6 +92,7 @@ struct turn_info Point point; method_type method; bool discarded; + bool selectable_start; // Can be used as starting-turn in traverse Container operations; @@ -97,13 +100,14 @@ struct turn_info inline turn_info() : method(method_none) , discarded(false) + , selectable_start(true) {} inline bool both(operation_type type) const { return has12(type, type); } - + inline bool has(operation_type type) const { return this->operations[0].operation == type @@ -115,8 +119,6 @@ struct turn_info return has12(type1, type2) || has12(type2, type1); } - - inline bool is_discarded() const { return discarded; } inline bool blocked() const { return both(operation_blocked); diff --git a/boost/geometry/algorithms/detail/overlay/visit_info.hpp b/boost/geometry/algorithms/detail/overlay/visit_info.hpp index 6be63f42b4..4284a801a1 100644 --- a/boost/geometry/algorithms/detail/overlay/visit_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/visit_info.hpp @@ -10,11 +10,6 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_VISIT_INFO_HPP -#ifdef BOOST_GEOMETRY_USE_MSM -# include -#endif - - namespace boost { namespace geometry { @@ -22,9 +17,6 @@ namespace boost { namespace geometry namespace detail { namespace overlay { - -#if ! defined(BOOST_GEOMETRY_USE_MSM) - class visit_info { private : @@ -66,8 +58,6 @@ public: } } - - #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION friend std::ostream& operator<<(std::ostream &os, visit_info const& v) { @@ -82,50 +72,6 @@ public: }; -#else - - -class visit_info -{ - -private : - -#ifndef USE_MSM_MINI - mutable -#endif - traverse_state state; - -public : - inline visit_info() - { - state.start(); - } - - inline void set_none() { state.process_event(none()); } // Not Yet Implemented! - inline void set_visited() { state.process_event(visit()); } - inline void set_started() { state.process_event(starting()); } - inline void set_finished() { state.process_event(finish()); } - -#ifdef USE_MSM_MINI - inline bool none() const { return state.flag_none(); } - inline bool visited() const { return state.flag_visited(); } - inline bool started() const { return state.flag_started(); } -#else - inline bool none() const { return state.is_flag_active(); } - inline bool visited() const { return state.is_flag_active(); } - inline bool started() const { return state.is_flag_active(); } -#endif - -#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION - friend std::ostream& operator<<(std::ostream &os, visit_info const& v) - { - return os; - } -#endif -}; -#endif - - }} // namespace detail::overlay #endif //DOXYGEN_NO_DETAIL diff --git a/boost/geometry/algorithms/detail/partition.hpp b/boost/geometry/algorithms/detail/partition.hpp index 45ff52ccb1..a44d5637bc 100644 --- a/boost/geometry/algorithms/detail/partition.hpp +++ b/boost/geometry/algorithms/detail/partition.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2011-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2011-2014 Barend Gehrels, Amsterdam, the Netherlands. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -10,6 +10,7 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP #include +#include #include #include #include @@ -23,7 +24,7 @@ namespace detail { namespace partition typedef std::vector index_vector_type; template -inline void divide_box(Box const& box, Box& lower_box, Box& upper_box) +void divide_box(Box const& box, Box& lower_box, Box& upper_box) { typedef typename coordinate_type::type ctype; @@ -39,10 +40,10 @@ inline void divide_box(Box const& box, Box& lower_box, Box& upper_box) } // Divide collection into three subsets: lower, upper and oversized -// (not-fitting) +// (not-fitting) // (lower == left or bottom, upper == right or top) template -static inline void divide_into_subsets(Box const& lower_box, +inline void divide_into_subsets(Box const& lower_box, Box const& upper_box, InputCollection const& collection, index_vector_type const& input, @@ -79,14 +80,14 @@ static inline void divide_into_subsets(Box const& lower_box, else { // Is nowhere! Should not occur! - BOOST_ASSERT(true); + BOOST_ASSERT(false); } } } // Match collection with itself template -static inline void handle_one(InputCollection const& collection, +inline void handle_one(InputCollection const& collection, index_vector_type const& input, Policy& policy) { @@ -106,10 +107,15 @@ static inline void handle_one(InputCollection const& collection, } // Match collection 1 with collection 2 -template -static inline void handle_two( - InputCollection const& collection1, index_vector_type const& input1, - InputCollection const& collection2, index_vector_type const& input2, +template +< + typename InputCollection1, + typename InputCollection2, + typename Policy +> +inline void handle_two( + InputCollection1 const& collection1, index_vector_type const& input1, + InputCollection2 const& collection2, index_vector_type const& input2, Policy& policy) { typedef boost::range_iterator @@ -209,7 +215,8 @@ template < int Dimension, typename Box, - typename OverlapsPolicy, + typename OverlapsPolicy1, + typename OverlapsPolicy2, typename VisitBoxPolicy > class partition_two_collections @@ -220,15 +227,21 @@ class partition_two_collections < 1 - Dimension, Box, - OverlapsPolicy, + OverlapsPolicy1, + OverlapsPolicy2, VisitBoxPolicy > sub_divide; - template + template + < + typename InputCollection1, + typename InputCollection2, + typename Policy + > static inline void next_level(Box const& box, - InputCollection const& collection1, + InputCollection1 const& collection1, index_vector_type const& input1, - InputCollection const& collection2, + InputCollection2 const& collection2, index_vector_type const& input2, int level, std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) @@ -252,10 +265,15 @@ class partition_two_collections } public : - template + template + < + typename InputCollection1, + typename InputCollection2, + typename Policy + > static inline void apply(Box const& box, - InputCollection const& collection1, index_vector_type const& input1, - InputCollection const& collection2, index_vector_type const& input2, + InputCollection1 const& collection1, index_vector_type const& input1, + InputCollection2 const& collection2, index_vector_type const& input2, int level, std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) @@ -267,9 +285,9 @@ public : index_vector_type lower1, upper1, exceeding1; index_vector_type lower2, upper2, exceeding2; - divide_into_subsets(lower_box, upper_box, collection1, + divide_into_subsets(lower_box, upper_box, collection1, input1, lower1, upper1, exceeding1); - divide_into_subsets(lower_box, upper_box, collection2, + divide_into_subsets(lower_box, upper_box, collection2, input2, lower2, upper2, exceeding2); if (boost::size(exceeding1) > 0) @@ -308,15 +326,17 @@ struct visit_no_policy template < typename Box, - typename ExpandPolicy, - typename OverlapsPolicy, + typename ExpandPolicy1, + typename OverlapsPolicy1, + typename ExpandPolicy2 = ExpandPolicy1, + typename OverlapsPolicy2 = OverlapsPolicy1, typename VisitBoxPolicy = visit_no_policy > class partition { typedef std::vector index_vector_type; - template + template static inline void expand_to_collection(InputCollection const& collection, Box& total, index_vector_type& index_vector) { @@ -344,12 +364,12 @@ public : index_vector_type index_vector; Box total; assign_inverse(total); - expand_to_collection(collection, total, index_vector); + expand_to_collection(collection, total, index_vector); detail::partition::partition_one_collection < 0, Box, - OverlapsPolicy, + OverlapsPolicy1, VisitBoxPolicy >::apply(total, collection, index_vector, 0, min_elements, visitor, box_visitor); @@ -373,9 +393,14 @@ public : } } - template - static inline void apply(InputCollection const& collection1, - InputCollection const& collection2, + template + < + typename InputCollection1, + typename InputCollection2, + typename VisitPolicy + > + static inline void apply(InputCollection1 const& collection1, + InputCollection2 const& collection2, VisitPolicy& visitor, std::size_t min_elements = 16, VisitBoxPolicy box_visitor = visit_no_policy() @@ -387,12 +412,12 @@ public : index_vector_type index_vector1, index_vector2; Box total; assign_inverse(total); - expand_to_collection(collection1, total, index_vector1); - expand_to_collection(collection2, total, index_vector2); + expand_to_collection(collection1, total, index_vector1); + expand_to_collection(collection2, total, index_vector2); detail::partition::partition_two_collections < - 0, Box, OverlapsPolicy, VisitBoxPolicy + 0, Box, OverlapsPolicy1, OverlapsPolicy2, VisitBoxPolicy >::apply(total, collection1, index_vector1, collection2, index_vector2, @@ -402,13 +427,17 @@ public : { typedef typename boost::range_iterator < - InputCollection const - >::type iterator_type; - for(iterator_type it1 = boost::begin(collection1); + InputCollection1 const + >::type iterator_type1; + typedef typename boost::range_iterator + < + InputCollection2 const + >::type iterator_type2; + for(iterator_type1 it1 = boost::begin(collection1); it1 != boost::end(collection1); ++it1) { - for(iterator_type it2 = boost::begin(collection2); + for(iterator_type2 it2 = boost::begin(collection2); it2 != boost::end(collection2); ++it2) { @@ -417,9 +446,9 @@ public : } } } - }; + }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP diff --git a/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp b/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp new file mode 100644 index 0000000000..cd3acb5ba4 --- /dev/null +++ b/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp @@ -0,0 +1,126 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2013 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2013 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2013 Mateusz Loskot, London, UK. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_IS_EQUAL_OR_SPIKE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_IS_EQUAL_OR_SPIKE_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +// Checks if a point ("last_point") causes a spike w.r.t. +// the specified two other points (segment_a, segment_b) +// +// x-------x------x +// a lp b +// +// Above, lp generates a spike w.r.t. segment(a,b) +// So specify last point first, then (a,b) (this is unordered, so unintuitive) +template +static inline bool point_is_spike_or_equal(Point1 const& last_point, + Point2 const& segment_a, + Point3 const& segment_b) +{ + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + typedef Point1 vector_type; + + int const side = side_strategy::apply(last_point, segment_a, segment_b); + if (side == 0) + { + // Last point is collinear w.r.t previous segment. + // Check if it is equal + vector_type diff1; + conversion::convert_point_to_point(last_point, diff1); + geometry::subtract_point(diff1, segment_b); + int const sgn_x1 = math::sign(geometry::get<0>(diff1)); + int const sgn_y1 = math::sign(geometry::get<1>(diff1)); + if (sgn_x1 == 0 && sgn_y1 == 0) + { + return true; + } + + // Check if it moves forward + vector_type diff2; + conversion::convert_point_to_point(segment_b, diff2); + geometry::subtract_point(diff2, segment_a); + int const sgn_x2 = math::sign(geometry::get<0>(diff2)); + int const sgn_y2 = math::sign(geometry::get<1>(diff2)); + + return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; + } + return false; +} + +template +< + typename Point1, + typename Point2, + typename Point3, + typename RobustPolicy +> +static inline bool point_is_spike_or_equal(Point1 const& last_point, + Point2 const& segment_a, + Point3 const& segment_b, + RobustPolicy const& robust_policy) +{ + if (point_is_spike_or_equal(last_point, segment_a, segment_b)) + { + return true; + } + + if (! RobustPolicy::enabled) + { + return false; + } + + // Try using specified robust policy + typedef typename geometry::robust_point_type + < + Point1, + RobustPolicy + >::type robust_point_type; + + robust_point_type last_point_rob, segment_a_rob, segment_b_rob; + geometry::recalculate(last_point_rob, last_point, robust_policy); + geometry::recalculate(segment_a_rob, segment_a, robust_policy); + geometry::recalculate(segment_b_rob, segment_b, robust_policy); + + return point_is_spike_or_equal + ( + last_point_rob, + segment_a_rob, + segment_b_rob + ); +} + + +} // namespace detail +#endif + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_IS_EQUAL_OR_SPIKE_HPP diff --git a/boost/geometry/algorithms/detail/point_on_border.hpp b/boost/geometry/algorithms/detail/point_on_border.hpp index b7e15ba3f9..24b88a8d19 100644 --- a/boost/geometry/algorithms/detail/point_on_border.hpp +++ b/boost/geometry/algorithms/detail/point_on_border.hpp @@ -19,6 +19,7 @@ #include +#include #include #include @@ -26,7 +27,7 @@ #include #include -#include +#include namespace boost { namespace geometry @@ -153,6 +154,35 @@ struct point_on_box }; +template +< + typename Point, + typename MultiGeometry, + typename Policy +> +struct point_on_multi +{ + 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) + for (typename boost::range_iterator + < + MultiGeometry const + >::type it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + if (Policy::apply(point, *it, midpoint)) + { + return true; + } + } + return false; + } +}; + + }} // namespace detail::point_on_border #endif // DOXYGEN_NO_DETAIL @@ -203,6 +233,36 @@ struct point_on_border {}; +template +struct point_on_border + : detail::point_on_border::point_on_multi + < + Point, + Multi, + detail::point_on_border::point_on_polygon + < + Point, + typename boost::range_value::type + > + > +{}; + + +template +struct point_on_border + : detail::point_on_border::point_on_multi + < + Point, + Multi, + detail::point_on_border::point_on_range + < + Point, + typename boost::range_value::type + > + > +{}; + + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH @@ -229,8 +289,6 @@ inline bool point_on_border(Point& point, concept::check(); concept::check(); - typedef typename point_type::type point_type; - return dispatch::point_on_border < typename tag::type, diff --git a/boost/geometry/algorithms/detail/recalculate.hpp b/boost/geometry/algorithms/detail/recalculate.hpp new file mode 100644 index 0000000000..2c3ea7413b --- /dev/null +++ b/boost/geometry/algorithms/detail/recalculate.hpp @@ -0,0 +1,231 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2013 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Bruno Lalande, Paris, France. +// Copyright (c) 2013 Mateusz Loskot, London, UK. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RECALCULATE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RECALCULATE_HPP + + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace recalculate +{ + +template +struct recalculate_point +{ + template + static inline void apply(Point1& point1, Point2 const& point2, Strategy const& strategy) + { + std::size_t const dim = Dimension - 1; + geometry::set(point1, strategy.template apply(geometry::get(point2))); + recalculate_point::apply(point1, point2, strategy); + } +}; + +template <> +struct recalculate_point<0> +{ + template + static inline void apply(Point1&, Point2 const&, Strategy const&) + { + } +}; + + +template +struct recalculate_indexed +{ + template + static inline void apply(Geometry1& geometry1, Geometry2 const& geometry2, Strategy const& strategy) + { + // Do it for both indices in one dimension + static std::size_t const dim = Dimension - 1; + geometry::set<0, dim>(geometry1, strategy.template apply(geometry::get<0, dim>(geometry2))); + geometry::set<1, dim>(geometry1, strategy.template apply(geometry::get<1, dim>(geometry2))); + recalculate_indexed::apply(geometry1, geometry2, strategy); + } +}; + +template <> +struct recalculate_indexed<0> +{ + + template + static inline void apply(Geometry1& , Geometry2 const& , Strategy const& ) + { + } +}; + +struct range_to_range +{ + template + < + typename Range1, + typename Range2, + typename Strategy + > + static inline void apply(Range1& destination, Range2 const& source, + Strategy const& strategy) + { + typedef typename geometry::point_type::type point_type; + typedef recalculate_point::value> per_point; + geometry::clear(destination); + + for (typename boost::range_iterator::type it + = boost::begin(source); + it != boost::end(source); + ++it) + { + point_type p; + per_point::apply(p, *it, strategy); + geometry::append(destination, p); + } + } +}; + +struct polygon_to_polygon +{ +private: + template + < + typename IteratorIn, + typename IteratorOut, + typename Strategy + > + static inline void iterate(IteratorIn begin, IteratorIn end, + IteratorOut it_out, + Strategy const& strategy) + { + for (IteratorIn it_in = begin; it_in != end; ++it_in, ++it_out) + { + range_to_range::apply(*it_out, *it_in, strategy); + } + } + + template + < + typename InteriorRingsOut, + typename InteriorRingsIn, + typename Strategy + > + static inline void apply_interior_rings( + InteriorRingsOut& interior_rings_out, + InteriorRingsIn const& interior_rings_in, + Strategy const& strategy) + { + traits::resize::apply(interior_rings_out, + boost::size(interior_rings_in)); + + iterate( + boost::begin(interior_rings_in), boost::end(interior_rings_in), + boost::begin(interior_rings_out), + strategy); + } + +public: + template + < + typename Polygon1, + typename Polygon2, + typename Strategy + > + static inline void apply(Polygon1& destination, Polygon2 const& source, + Strategy const& strategy) + { + range_to_range::apply(geometry::exterior_ring(destination), + geometry::exterior_ring(source), strategy); + + apply_interior_rings(geometry::interior_rings(destination), + geometry::interior_rings(source), strategy); + } +}; + +}} // namespace detail::recalculate +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1 = typename geometry::tag::type, + typename Tag2 = typename geometry::tag::type +> +struct recalculate : not_implemented +{}; + +template +struct recalculate + : detail::recalculate::recalculate_point::value> +{}; + +template +struct recalculate + : detail::recalculate::recalculate_indexed::value> +{}; + +template +struct recalculate + : detail::recalculate::recalculate_indexed::value> +{}; + +template +struct recalculate + : detail::recalculate::polygon_to_polygon +{}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + +template +inline void recalculate(Geometry1& geometry1, Geometry2 const& geometry2, Strategy const& strategy) +{ + concept::check(); + concept::check(); + + // static assert dimensions (/types) are the same + + dispatch::recalculate::apply(geometry1, geometry2, strategy); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RECALCULATE_HPP diff --git a/boost/geometry/algorithms/detail/relate/areal_areal.hpp b/boost/geometry/algorithms/detail/relate/areal_areal.hpp new file mode 100644 index 0000000000..31d206ac99 --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/areal_areal.hpp @@ -0,0 +1,823 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_AREAL_AREAL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_AREAL_AREAL_HPP + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +// WARNING! +// TODO: In the worst case calling this Pred in a loop for MultiPolygon/MultiPolygon may take O(NM) +// Use the rtree in this case! + +// may be used to set EI and EB for an Areal geometry for which no turns were generated +template +class no_turns_aa_pred +{ +public: + no_turns_aa_pred(OtherAreal const& other_areal, Result & res) + : m_result(res) + , m_other_areal(other_areal) + , m_flags(0) + { + // check which relations must be analysed + + if ( ! may_update(m_result) + && ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 1; + } + + if ( ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 2; + } + } + + template + bool operator()(Areal const& areal) + { + // if those flags are set nothing will change + if ( m_flags == 3 ) + { + return false; + } + + typedef typename geometry::point_type::type point_type; + point_type pt; + bool const ok = boost::geometry::point_on_border(pt, areal); + + // TODO: for now ignore, later throw an exception? + if ( !ok ) + { + return true; + } + + // check if the areal is inside the other_areal + // TODO: This is O(N) + // Run in a loop O(NM) - optimize! + int const pig = detail::within::point_in_geometry(pt, m_other_areal); + //BOOST_ASSERT( pig != 0 ); + + // inside + if ( pig > 0 ) + { + update(m_result); + update(m_result); + update(m_result); + m_flags |= 1; + + // TODO: OPTIMIZE! + // Only the interior rings of other ONE single geometry must be checked + // NOT all geometries + + // Check if any interior ring is outside + ring_identifier ring_id(0, -1, 0); + int const irings_count = boost::numeric_cast( + geometry::num_interior_rings(areal) ); + for ( ; ring_id.ring_index < irings_count ; ++ring_id.ring_index ) + { + typename detail::sub_range_return_type::type + range_ref = detail::sub_range(areal, ring_id); + + if ( boost::empty(range_ref) ) + { + // TODO: throw exception? + continue; // ignore + } + + // TODO: O(N) + // Optimize! + int const hpig = detail::within::point_in_geometry(range::front(range_ref), m_other_areal); + + // hole outside + if ( hpig < 0 ) + { + update(m_result); + update(m_result); + m_flags |= 2; + break; + } + } + } + // outside + else + { + update(m_result); + update(m_result); + m_flags |= 2; + + // Check if any interior ring is inside + ring_identifier ring_id(0, -1, 0); + int const irings_count = boost::numeric_cast( + geometry::num_interior_rings(areal) ); + for ( ; ring_id.ring_index < irings_count ; ++ring_id.ring_index ) + { + typename detail::sub_range_return_type::type + range_ref = detail::sub_range(areal, ring_id); + + if ( boost::empty(range_ref) ) + { + // TODO: throw exception? + continue; // ignore + } + + // TODO: O(N) + // Optimize! + int const hpig = detail::within::point_in_geometry(range::front(range_ref), m_other_areal); + + // hole inside + if ( hpig > 0 ) + { + update(m_result); + update(m_result); + update(m_result); + m_flags |= 1; + break; + } + } + } + + return m_flags != 3 && !m_result.interrupt; + } + +private: + Result & m_result; + OtherAreal const& m_other_areal; + int m_flags; +}; + +// The implementation of an algorithm calculating relate() for A/A +template +struct areal_areal +{ + // check Linear / Areal + BOOST_STATIC_ASSERT(topological_dimension::value == 2 + && topological_dimension::value == 2); + + static const bool interruption_enabled = true; + + typedef typename geometry::point_type::type point1_type; + typedef typename geometry::point_type::type point2_type; + + template + static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result) + { +// TODO: If Areal geometry may have infinite size, change the following line: + + // The result should be FFFFFFFFF + relate::set::value>(result);// FFFFFFFFd, d in [1,9] or T + + if ( result.interrupt ) + return; + + // get and analyse turns + typedef typename turns::get_turns::turn_info turn_type; + std::vector turns; + + interrupt_policy_areal_areal interrupt_policy(geometry1, geometry2, result); + + turns::get_turns::apply(turns, geometry1, geometry2, interrupt_policy); + if ( result.interrupt ) + return; + + no_turns_aa_pred pred1(geometry2, result); + for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); + if ( result.interrupt ) + return; + + no_turns_aa_pred pred2(geometry1, result); + for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); + if ( result.interrupt ) + return; + + if ( turns.empty() ) + return; + + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) + { + // sort turns + typedef turns::less<0, turns::less_op_areal_areal<0> > less; + std::sort(turns.begin(), turns.end(), less()); + + /*if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) )*/ + { + // analyse sorted turns + turns_analyser analyser; + analyse_each_turn(result, analyser, turns.begin(), turns.end()); + + if ( result.interrupt ) + return; + } + + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) + { + // analyse rings for which turns were not generated + // or only i/i or u/u was generated + uncertain_rings_analyser<0, Result, Geometry1, Geometry2> rings_analyser(result, geometry1, geometry2); + analyse_uncertain_rings<0>::apply(rings_analyser, turns.begin(), turns.end()); + + if ( result.interrupt ) + return; + } + } + + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) + { + // sort turns + typedef turns::less<1, turns::less_op_areal_areal<1> > less; + std::sort(turns.begin(), turns.end(), less()); + + /*if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) )*/ + { + // analyse sorted turns + turns_analyser analyser; + analyse_each_turn(result, analyser, turns.begin(), turns.end()); + + if ( result.interrupt ) + return; + } + + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) + { + // analyse rings for which turns were not generated + // or only i/i or u/u was generated + uncertain_rings_analyser<1, Result, Geometry2, Geometry1> rings_analyser(result, geometry2, geometry1); + analyse_uncertain_rings<1>::apply(rings_analyser, turns.begin(), turns.end()); + + //if ( result.interrupt ) + // return; + } + } + } + + // interrupt policy which may be passed to get_turns to interrupt the analysis + // based on the info in the passed result/mask + template + class interrupt_policy_areal_areal + { + public: + static bool const enabled = true; + + interrupt_policy_areal_areal(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Result & result) + : m_result(result) + , m_geometry1(geometry1) + , m_geometry2(geometry2) + {} + + template + inline bool apply(Range const& turns) + { + typedef typename boost::range_iterator::type iterator; + + for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it) + { + per_turn<0>(*it); + per_turn<1>(*it); + } + + return m_result.interrupt; + } + + private: + template + inline void per_turn(Turn const& turn) + { + static const std::size_t other_op_id = (OpId + 1) % 2; + static const bool transpose_result = OpId != 0; + + overlay::operation_type const op = turn.operations[OpId].operation; + + if ( op == overlay::operation_union ) + { + // ignore u/u + /*if ( turn.operations[other_op_id].operation != overlay::operation_union ) + { + update(m_result); + update(m_result); + }*/ + + update(m_result); + } + else if ( op == overlay::operation_intersection ) + { + // ignore i/i + if ( turn.operations[other_op_id].operation != overlay::operation_intersection ) + { + update(m_result); + //update(m_result); + } + + update(m_result); + } + else if ( op == overlay::operation_continue ) + { + update(m_result); + update(m_result); + } + else if ( op == overlay::operation_blocked ) + { + update(m_result); + update(m_result); + } + } + + Result & m_result; + Geometry1 const& m_geometry1; + Geometry2 const& m_geometry2; + }; + + // This analyser should be used like Input or SinglePass Iterator + // IMPORTANT! It should be called also for the end iterator - last + template + class turns_analyser + { + typedef typename TurnInfo::point_type turn_point_type; + + static const std::size_t op_id = OpId; + static const std::size_t other_op_id = (OpId + 1) % 2; + static const bool transpose_result = OpId != 0; + + public: + turns_analyser() + : m_previous_turn_ptr(0) + , m_previous_operation(overlay::operation_none) + , m_enter_detected(false) + , m_exit_detected(false) + {} + + template + void apply(Result & result, TurnIt it) + { + //BOOST_ASSERT( it != last ); + + overlay::operation_type const op = it->operations[op_id].operation; + + if ( op != overlay::operation_union + && op != overlay::operation_intersection + && op != overlay::operation_blocked + && op != overlay::operation_continue ) + { + return; + } + + segment_identifier const& seg_id = it->operations[op_id].seg_id; + //segment_identifier const& other_id = it->operations[other_op_id].seg_id; + + const bool first_in_range = m_seg_watcher.update(seg_id); + + if ( m_previous_turn_ptr ) + { + if ( m_exit_detected /*m_previous_operation == overlay::operation_union*/ ) + { + // real exit point - may be multiple + if ( first_in_range + || ! turn_on_the_same_ip(*m_previous_turn_ptr, *it) ) + { + update_exit(result); + m_exit_detected = false; + } + // fake exit point, reset state + else if ( op != overlay::operation_union ) + { + m_exit_detected = false; + } + } + /*else*/ + if ( m_enter_detected /*m_previous_operation == overlay::operation_intersection*/ ) + { + // real entry point + if ( first_in_range + || ! turn_on_the_same_ip(*m_previous_turn_ptr, *it) ) + { + update_enter(result); + m_enter_detected = false; + } + // fake entry point, reset state + else if ( op != overlay::operation_intersection ) + { + m_enter_detected = false; + } + } + } + + if ( op == overlay::operation_union ) + { + // already set in interrupt policy + //update(m_result); + + // ignore u/u + //if ( it->operations[other_op_id].operation != overlay::operation_union ) + { + m_exit_detected = true; + } + } + else if ( op == overlay::operation_intersection ) + { + // ignore i/i + if ( it->operations[other_op_id].operation != overlay::operation_intersection ) + { + // already set in interrupt policy + //update(result); + //update(result); + m_enter_detected = true; + } + } + else if ( op == overlay::operation_blocked ) + { + // already set in interrupt policy + } + else // if ( op == overlay::operation_continue ) + { + // already set in interrupt policy + } + + // store ref to previously analysed (valid) turn + m_previous_turn_ptr = boost::addressof(*it); + // and previously analysed (valid) operation + m_previous_operation = op; + } + + // it == last + template + void apply(Result & result) + { + //BOOST_ASSERT( first != last ); + + if ( m_exit_detected /*m_previous_operation == overlay::operation_union*/ ) + { + update_exit(result); + m_exit_detected = false; + } + + if ( m_enter_detected /*m_previous_operation == overlay::operation_intersection*/ ) + { + update_enter(result); + m_enter_detected = false; + } + } + + template + static inline void update_exit(Result & result) + { + update(result); + update(result); + } + + template + static inline void update_enter(Result & result) + { + update(result); + update(result); + } + + private: + segment_watcher m_seg_watcher; + TurnInfo * m_previous_turn_ptr; + overlay::operation_type m_previous_operation; + bool m_enter_detected; + bool m_exit_detected; + }; + + // call analyser.apply() for each turn in range + // IMPORTANT! The analyser is also called for the end iterator - last + template + static inline void analyse_each_turn(Result & res, + Analyser & analyser, + TurnIt first, TurnIt last) + { + if ( first == last ) + return; + + for ( TurnIt it = first ; it != last ; ++it ) + { + analyser.apply(res, it); + + if ( res.interrupt ) + return; + } + + analyser.apply(res); + } + + template + class uncertain_rings_analyser + { + static const bool transpose_result = OpId != 0; + static const int other_id = (OpId + 1) % 2; + + public: + inline uncertain_rings_analyser(Result & result, + Geometry const& geom, + OtherGeometry const& other_geom) + : geometry(geom), other_geometry(other_geom) + , interrupt(result.interrupt) // just in case, could be false as well + , m_result(result) + , m_flags(0) + { + // check which relations must be analysed + + if ( ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 1; + } + + if ( ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 2; + } + + if ( ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 4; + } + } + + inline void no_turns(segment_identifier const& seg_id) + { + // if those flags are set nothing will change + if ( (m_flags & 3) == 3 ) + { + return; + } + + typename detail::sub_range_return_type::type + range_ref = detail::sub_range(geometry, seg_id); + + if ( boost::empty(range_ref) ) + { + // TODO: throw an exception? + return; // ignore + } + + // TODO: possible optimization + // if the range is an interior ring we may use other IPs generated for this single geometry + // to know which other single geometries should be checked + + // TODO: optimize! e.g. use spatial index + // O(N) - running it in a loop would gives O(NM) + int const pig = detail::within::point_in_geometry(range::front(range_ref), other_geometry); + + //BOOST_ASSERT(pig != 0); + if ( pig > 0 ) + { + update(m_result); + update(m_result); + m_flags |= 1; + } + else + { + update(m_result); + update(m_result); + m_flags |= 2; + } + +// TODO: break if all things are set +// also some of them could be checked outside, before the analysis +// In this case we shouldn't relay just on dummy flags +// Flags should be initialized with proper values +// or the result should be checked directly +// THIS IS ALSO TRUE FOR OTHER ANALYSERS! in L/L and L/A + + interrupt = m_flags == 7 || m_result.interrupt; + } + + template + inline void turns(TurnIt first, TurnIt last) + { + // if those flags are set nothing will change + if ( (m_flags & 6) == 6 ) + { + return; + } + + bool found_ii = false; + bool found_uu = false; + + for ( TurnIt it = first ; it != last ; ++it ) + { + if ( it->operations[0].operation == overlay::operation_intersection + && it->operations[1].operation == overlay::operation_intersection ) + { + // ignore exterior ring + if ( it->operations[OpId].seg_id.ring_index >= 0 ) + { + found_ii = true; + } + } + else if ( it->operations[0].operation == overlay::operation_union + && it->operations[1].operation == overlay::operation_union ) + { + // ignore if u/u is for holes + //if ( it->operations[OpId].seg_id.ring_index >= 0 + // && it->operations[other_id].seg_id.ring_index >= 0 ) + { + found_uu = true; + } + } + else // ignore + { + return; // don't interrupt + } + } + + // only i/i was generated for this ring + if ( found_ii ) + { + //update(m_result); + //update(m_result); + update(m_result); + update(m_result); + m_flags |= 4; + } + + // only u/u was generated for this ring + if ( found_uu ) + { + update(m_result); + update(m_result); + m_flags |= 2; + + // not necessary since this will be checked in the next iteration + // but increases the pruning strength + // WARNING: this is not reflected in flags + update(m_result); + update(m_result); + } + + interrupt = m_flags == 7 || m_result.interrupt; // interrupt if the result won't be changed in the future + } + + Geometry const& geometry; + OtherGeometry const& other_geometry; + bool interrupt; + + private: + Result & m_result; + int m_flags; + }; + + template + class analyse_uncertain_rings + { + public: + template + static inline void apply(Analyser & analyser, TurnIt first, TurnIt last) + { + if ( first == last ) + return; + + for_preceding_rings(analyser, *first); + //analyser.per_turn(*first); + + TurnIt prev = first; + for ( ++first ; first != last ; ++first, ++prev ) + { + // same multi + if ( prev->operations[OpId].seg_id.multi_index + == first->operations[OpId].seg_id.multi_index ) + { + // same ring + if ( prev->operations[OpId].seg_id.ring_index + == first->operations[OpId].seg_id.ring_index ) + { + //analyser.per_turn(*first); + } + // same multi, next ring + else + { + //analyser.end_ring(*prev); + analyser.turns(prev, first); + + //if ( prev->operations[OpId].seg_id.ring_index + 1 + // < first->operations[OpId].seg_id.ring_index) + { + for_no_turns_rings(analyser, + *first, + prev->operations[OpId].seg_id.ring_index + 1, + first->operations[OpId].seg_id.ring_index); + } + + //analyser.per_turn(*first); + } + } + // next multi + else + { + //analyser.end_ring(*prev); + analyser.turns(prev, first); + for_following_rings(analyser, *prev); + for_preceding_rings(analyser, *first); + //analyser.per_turn(*first); + } + + if ( analyser.interrupt ) + { + return; + } + } + + //analyser.end_ring(*prev); + analyser.turns(prev, first); // first == last + for_following_rings(analyser, *prev); + } + + private: + template + static inline void for_preceding_rings(Analyser & analyser, Turn const& turn) + { + segment_identifier const& seg_id = turn.operations[OpId].seg_id; + + for_no_turns_rings(analyser, turn, -1, seg_id.ring_index); + } + + template + static inline void for_following_rings(Analyser & analyser, Turn const& turn) + { + segment_identifier const& seg_id = turn.operations[OpId].seg_id; + + int count = boost::numeric_cast( + geometry::num_interior_rings( + detail::single_geometry(analyser.geometry, seg_id))); + + for_no_turns_rings(analyser, turn, seg_id.ring_index + 1, count); + } + + template + static inline void for_no_turns_rings(Analyser & analyser, Turn const& turn, int first, int last) + { + segment_identifier seg_id = turn.operations[OpId].seg_id; + + for ( seg_id.ring_index = first ; seg_id.ring_index < last ; ++seg_id.ring_index ) + { + analyser.no_turns(seg_id); + } + } + }; +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_AREAL_AREAL_HPP diff --git a/boost/geometry/algorithms/detail/relate/boundary_checker.hpp b/boost/geometry/algorithms/detail/relate/boundary_checker.hpp new file mode 100644 index 0000000000..f98c3e9b82 --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/boundary_checker.hpp @@ -0,0 +1,134 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_BOUNDARY_CHECKER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_BOUNDARY_CHECKER_HPP + +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +enum boundary_query { boundary_front, boundary_back, boundary_any }; + +template ::type> +class boundary_checker {}; + +template +class boundary_checker +{ + typedef typename point_type::type point_type; + +public: + boundary_checker(Geometry const& g) + : has_boundary( boost::size(g) >= 2 + && !detail::equals::equals_point_point(range::front(g), range::back(g)) ) + , geometry(g) + {} + + template + bool is_endpoint_boundary(point_type const& pt) const + { + boost::ignore_unused_variable_warning(pt); +#ifdef BOOST_GEOMETRY_DEBUG_RELATE_BOUNDARY_CHECKER + // may give false positives for INT + BOOST_ASSERT( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) + && detail::equals::equals_point_point(pt, range::front(geometry)) + || (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) + && detail::equals::equals_point_point(pt, range::back(geometry)) ); +#endif + return has_boundary; + } + +private: + bool has_boundary; + Geometry const& geometry; +}; + +template +class boundary_checker +{ + typedef typename point_type::type point_type; + +public: + boundary_checker(Geometry const& g) + : is_filled(false), geometry(g) + {} + + // First call O(NlogN) + // Each next call O(logN) + template + bool is_endpoint_boundary(point_type const& pt) const + { + typedef typename boost::range_size::type size_type; + size_type multi_count = boost::size(geometry); + + if ( multi_count < 1 ) + return false; + + if ( ! is_filled ) + { + //boundary_points.clear(); + boundary_points.reserve(multi_count * 2); + + typedef typename boost::range_iterator::type multi_iterator; + for ( multi_iterator it = boost::begin(geometry) ; + it != boost::end(geometry) ; ++ it ) + { + // empty or point - no boundary + if ( boost::size(*it) < 2 ) + continue; + + // linear ring or point - no boundary + if ( equals::equals_point_point(range::front(*it), range::back(*it)) ) + continue; + + boundary_points.push_back(range::front(*it)); + boundary_points.push_back(range::back(*it)); + } + + std::sort(boundary_points.begin(), boundary_points.end(), geometry::less()); + + is_filled = true; + } + + std::size_t equal_points_count + = boost::size( + std::equal_range(boundary_points.begin(), + boundary_points.end(), + pt, + geometry::less()) + ); + + return equal_points_count % 2 != 0;// && equal_points_count > 0; // the number is odd and > 0 + } + +private: + mutable bool is_filled; + // TODO: store references/pointers instead of points? + mutable std::vector boundary_points; + + Geometry const& geometry; +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_BOUNDARY_CHECKER_HPP diff --git a/boost/geometry/algorithms/detail/relate/follow_helpers.hpp b/boost/geometry/algorithms/detail/relate/follow_helpers.hpp new file mode 100644 index 0000000000..78fa03798d --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/follow_helpers.hpp @@ -0,0 +1,401 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP + +#include +//#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +// NOTE: This iterates through single geometries for which turns were not generated. +// It doesn't mean that the geometry is disjoint, only that no turns were detected. + +template ::type, + bool IsMulti = boost::is_base_of::value +> +struct for_each_disjoint_geometry_if + : public not_implemented +{}; + +template +struct for_each_disjoint_geometry_if +{ + template + static inline bool apply(TurnIt first, TurnIt last, + Geometry const& geometry, + Pred & pred) + { + if ( first != last ) + return false; + pred(geometry); + return true; + } +}; + +template +struct for_each_disjoint_geometry_if +{ + template + static inline bool apply(TurnIt first, TurnIt last, + Geometry const& geometry, + Pred & pred) + { + if ( first != last ) + return for_turns(first, last, geometry, pred); + else + return for_empty(geometry, pred); + } + + template + static inline bool for_empty(Geometry const& geometry, + Pred & pred) + { + typedef typename boost::range_iterator::type iterator; + + // O(N) + // check predicate for each contained geometry without generated turn + for ( iterator it = boost::begin(geometry) ; + it != boost::end(geometry) ; ++it ) + { + bool cont = pred(*it); + if ( !cont ) + break; + } + + return !boost::empty(geometry); + } + + template + static inline bool for_turns(TurnIt first, TurnIt last, + Geometry const& geometry, + Pred & pred) + { + BOOST_ASSERT(first != last); + + const std::size_t count = boost::size(geometry); + boost::ignore_unused_variable_warning(count); + + // O(I) + // gather info about turns generated for contained geometries + std::vector detected_intersections(count, false); + for ( TurnIt it = first ; it != last ; ++it ) + { + int multi_index = it->operations[OpId].seg_id.multi_index; + BOOST_ASSERT(multi_index >= 0); + std::size_t index = static_cast(multi_index); + BOOST_ASSERT(index < count); + detected_intersections[index] = true; + } + + bool found = false; + + // O(N) + // check predicate for each contained geometry without generated turn + for ( std::vector::iterator it = detected_intersections.begin() ; + it != detected_intersections.end() ; ++it ) + { + // if there were no intersections for this multi_index + if ( *it == false ) + { + found = true; + bool cont = pred(range::at(geometry, + std::distance(detected_intersections.begin(), it))); + if ( !cont ) + break; + } + } + + return found; + } +}; + +// WARNING! This class stores pointers! +// Passing a reference to local variable will result in undefined behavior! +template +class point_info +{ +public: + point_info() : sid_ptr(NULL), pt_ptr(NULL) {} + point_info(Point const& pt, segment_identifier const& sid) + : sid_ptr(boost::addressof(sid)) + , pt_ptr(boost::addressof(pt)) + {} + segment_identifier const& seg_id() const + { + BOOST_ASSERT(sid_ptr); + return *sid_ptr; + } + Point const& point() const + { + BOOST_ASSERT(pt_ptr); + return *pt_ptr; + } + + //friend bool operator==(point_identifier const& l, point_identifier const& r) + //{ + // return l.seg_id() == r.seg_id() + // && detail::equals::equals_point_point(l.point(), r.point()); + //} + +private: + const segment_identifier * sid_ptr; + const Point * pt_ptr; +}; + +// WARNING! This class stores pointers! +// Passing a reference to local variable will result in undefined behavior! +class same_single +{ +public: + same_single(segment_identifier const& sid) + : sid_ptr(boost::addressof(sid)) + {} + + bool operator()(segment_identifier const& sid) const + { + return sid.multi_index == sid_ptr->multi_index; + } + + template + bool operator()(point_info const& pid) const + { + return operator()(pid.seg_id()); + } + +private: + const segment_identifier * sid_ptr; +}; + +class same_ring +{ +public: + same_ring(segment_identifier const& sid) + : sid_ptr(boost::addressof(sid)) + {} + + bool operator()(segment_identifier const& sid) const + { + return sid.multi_index == sid_ptr->multi_index + && sid.ring_index == sid_ptr->ring_index; + } + +private: + const segment_identifier * sid_ptr; +}; + +// WARNING! This class stores pointers! +// Passing a reference to local variable will result in undefined behavior! +template +class segment_watcher +{ +public: + segment_watcher() + : m_seg_id_ptr(NULL) + {} + + bool update(segment_identifier const& seg_id) + { + bool result = m_seg_id_ptr == 0 || !SameRange(*m_seg_id_ptr)(seg_id); + m_seg_id_ptr = boost::addressof(seg_id); + return result; + } + +private: + const segment_identifier * m_seg_id_ptr; +}; + +// WARNING! This class stores pointers! +// Passing a reference to local variable will result in undefined behavior! +template +class exit_watcher +{ + static const std::size_t op_id = OpId; + static const std::size_t other_op_id = (OpId + 1) % 2; + + typedef typename TurnInfo::point_type point_type; + typedef detail::relate::point_info point_info; + +public: + exit_watcher() + : m_exit_operation(overlay::operation_none) + , m_exit_turn_ptr(NULL) + {} + + void enter(TurnInfo const& turn) + { + m_other_entry_points.push_back( + point_info(turn.point, turn.operations[other_op_id].seg_id) ); + } + + // TODO: exit_per_geometry parameter looks not very safe + // wrong value may be easily passed + + void exit(TurnInfo const& turn, bool exit_per_geometry = true) + { + //segment_identifier const& seg_id = turn.operations[op_id].seg_id; + segment_identifier const& other_id = turn.operations[other_op_id].seg_id; + overlay::operation_type exit_op = turn.operations[op_id].operation; + + typedef typename std::vector::iterator point_iterator; + // search for the entry point in the same range of other geometry + point_iterator entry_it = std::find_if(m_other_entry_points.begin(), + m_other_entry_points.end(), + same_single(other_id)); + + // this end point has corresponding entry point + if ( entry_it != m_other_entry_points.end() ) + { + // erase the corresponding entry point + m_other_entry_points.erase(entry_it); + + if ( exit_per_geometry || m_other_entry_points.empty() ) + { + // here we know that we possibly left LS + // we must still check if we didn't get back on the same point + m_exit_operation = exit_op; + m_exit_turn_ptr = boost::addressof(turn); + } + } + } + + bool is_outside() const + { + // if we didn't entered anything in the past, we're outside + return m_other_entry_points.empty(); + } + + bool is_outside(TurnInfo const& turn) const + { + return m_other_entry_points.empty() + || std::find_if(m_other_entry_points.begin(), + m_other_entry_points.end(), + same_single( + turn.operations[other_op_id].seg_id)) + == m_other_entry_points.end(); + } + + overlay::operation_type get_exit_operation() const + { + return m_exit_operation; + } + + point_type const& get_exit_point() const + { + BOOST_ASSERT(m_exit_operation != overlay::operation_none); + BOOST_ASSERT(m_exit_turn_ptr); + return m_exit_turn_ptr->point; + } + + TurnInfo const& get_exit_turn() const + { + BOOST_ASSERT(m_exit_operation != overlay::operation_none); + BOOST_ASSERT(m_exit_turn_ptr); + return *m_exit_turn_ptr; + } + + void reset_detected_exit() + { + m_exit_operation = overlay::operation_none; + } + + void reset() + { + m_exit_operation = overlay::operation_none; + m_other_entry_points.clear(); + } + +private: + overlay::operation_type m_exit_operation; + const TurnInfo * m_exit_turn_ptr; + std::vector m_other_entry_points; // TODO: use map here or sorted vector? +}; + +template +inline bool turn_on_the_same_ip(Turn const& prev_turn, Turn const& curr_turn) +{ + segment_identifier const& prev_seg_id = prev_turn.operations[OpId].seg_id; + segment_identifier const& curr_seg_id = curr_turn.operations[OpId].seg_id; + + if ( prev_seg_id.multi_index != curr_seg_id.multi_index + || prev_seg_id.ring_index != curr_seg_id.ring_index ) + { + return false; + } + + // TODO: will this work if between segments there will be some number of degenerated ones? + + if ( prev_seg_id.segment_index != curr_seg_id.segment_index + && ( ! curr_turn.operations[OpId].fraction.is_zero() + || prev_seg_id.segment_index + 1 != curr_seg_id.segment_index ) ) + { + return false; + } + + return detail::equals::equals_point_point(prev_turn.point, curr_turn.point); +} + +template +static inline bool is_endpoint_on_boundary(Point const& pt, + BoundaryChecker & boundary_checker) +{ + return boundary_checker.template is_endpoint_boundary(pt); +} + +template +static inline bool is_ip_on_boundary(IntersectionPoint const& ip, + OperationInfo const& operation_info, + BoundaryChecker & boundary_checker, + segment_identifier const& seg_id) +{ + boost::ignore_unused_variable_warning(seg_id); + + bool res = false; + + // IP on the last point of the linestring + if ( (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) + && operation_info.position == overlay::position_back ) + { + // check if this point is a boundary + res = boundary_checker.template is_endpoint_boundary(ip); + } + // IP on the last point of the linestring + else if ( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) + && operation_info.position == overlay::position_front ) + { + // check if this point is a boundary + res = boundary_checker.template is_endpoint_boundary(ip); + } + + return res; +} + + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP diff --git a/boost/geometry/algorithms/detail/relate/less.hpp b/boost/geometry/algorithms/detail/relate/less.hpp new file mode 100644 index 0000000000..3f11d4e87d --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/less.hpp @@ -0,0 +1,79 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LESS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LESS_HPP + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace detail_dispatch { namespace relate { + +// TODO: Integrate it with geometry::less? + +template ::value> +struct less +{ + static inline bool apply(Point1 const& left, Point2 const& right) + { + typename geometry::coordinate_type::type + cleft = geometry::get(left); + typename geometry::coordinate_type::type + cright = geometry::get(right); + + if ( geometry::math::equals(cleft, cright) ) + { + return less::apply(left, right); + } + else + { + return cleft < cright; + } + } +}; + +template +struct less +{ + static inline bool apply(Point1 const&, Point2 const&) + { + return false; + } +}; + +}} // namespace detail_dispatch::relate + +#endif + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +struct less +{ + template + inline bool operator()(Point1 const& point1, Point2 const& point2) const + { + return detail_dispatch::relate::less::apply(point1, point2); + } +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LESS_HPP diff --git a/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/boost/geometry/algorithms/detail/relate/linear_areal.hpp new file mode 100644 index 0000000000..3159ab329d --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -0,0 +1,1115 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +// WARNING! +// TODO: In the worst case calling this Pred in a loop for MultiLinestring/MultiPolygon may take O(NM) +// Use the rtree in this case! + +// may be used to set IE and BE for a Linear geometry for which no turns were generated +template +class no_turns_la_linestring_pred +{ +public: + no_turns_la_linestring_pred(Geometry2 const& geometry2, + Result & res, + BoundaryChecker const& boundary_checker) + : m_geometry2(geometry2) + , m_result(res) + , m_boundary_checker(boundary_checker) + , m_interrupt_flags(0) + { + if ( ! may_update(m_result) ) + { + m_interrupt_flags |= 1; + } + + if ( ! may_update(m_result) ) + { + m_interrupt_flags |= 2; + } + + if ( ! may_update(m_result) ) + { + m_interrupt_flags |= 4; + } + + if ( ! may_update(m_result) ) + { + m_interrupt_flags |= 8; + } + } + + template + bool operator()(Linestring const& linestring) + { + std::size_t const count = boost::size(linestring); + + // invalid input + if ( count < 2 ) + { + // ignore + // TODO: throw an exception? + return true; + } + + // if those flags are set nothing will change + if ( m_interrupt_flags == 0xF ) + { + return false; + } + + int const pig = detail::within::point_in_geometry(range::front(linestring), m_geometry2); + //BOOST_ASSERT_MSG(pig != 0, "There should be no IPs"); + + if ( pig > 0 ) + { + update(m_result); + m_interrupt_flags |= 1; + } + else + { + update(m_result); + m_interrupt_flags |= 2; + } + + // check if there is a boundary + if ( ( m_interrupt_flags & 0xC ) != 0xC // if wasn't already set + && ( m_boundary_checker.template + is_endpoint_boundary(range::front(linestring)) + || m_boundary_checker.template + is_endpoint_boundary(range::back(linestring)) ) ) + { + if ( pig > 0 ) + { + update(m_result); + m_interrupt_flags |= 4; + } + else + { + update(m_result); + m_interrupt_flags |= 8; + } + } + + return m_interrupt_flags != 0xF + && ! m_result.interrupt; + } + +private: + Geometry2 const& m_geometry2; + Result & m_result; + BoundaryChecker const& m_boundary_checker; + unsigned m_interrupt_flags; +}; + +// may be used to set EI and EB for an Areal geometry for which no turns were generated +template +class no_turns_la_areal_pred +{ +public: + no_turns_la_areal_pred(Result & res) + : m_result(res) + , interrupt(! may_update(m_result) + && ! may_update(m_result) ) + {} + + template + bool operator()(Areal const& areal) + { + if ( interrupt ) + { + return false; + } + + // TODO: + // handle empty/invalid geometries in a different way than below? + + typedef typename geometry::point_type::type point_type; + point_type dummy; + bool const ok = boost::geometry::point_on_border(dummy, areal); + + // TODO: for now ignore, later throw an exception? + if ( !ok ) + { + return true; + } + + update(m_result); + update(m_result); + + return false; + } + +private: + Result & m_result; + bool const interrupt; +}; + +// The implementation of an algorithm calculating relate() for L/A +template +struct linear_areal +{ + // check Linear / Areal + BOOST_STATIC_ASSERT(topological_dimension::value == 1 + && topological_dimension::value == 2); + + static const bool interruption_enabled = true; + + typedef typename geometry::point_type::type point1_type; + typedef typename geometry::point_type::type point2_type; + + template + static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result) + { +// TODO: If Areal geometry may have infinite size, change the following line: + + // The result should be FFFFFFFFF + relate::set::value, TransposeResult>(result);// FFFFFFFFd, d in [1,9] or T + + if ( result.interrupt ) + return; + + // get and analyse turns + typedef typename turns::get_turns::turn_info turn_type; + std::vector turns; + + interrupt_policy_linear_areal interrupt_policy(geometry2, result); + + turns::get_turns::apply(turns, geometry1, geometry2, interrupt_policy); + if ( result.interrupt ) + return; + + boundary_checker boundary_checker1(geometry1); + no_turns_la_linestring_pred + < + Geometry2, + Result, + boundary_checker, + TransposeResult + > pred1(geometry2, result, boundary_checker1); + for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); + if ( result.interrupt ) + return; + + no_turns_la_areal_pred pred2(result); + for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); + if ( result.interrupt ) + return; + + if ( turns.empty() ) + return; + + // This is set here because in the case if empty Areal geometry were passed + // those shouldn't be set + relate::set(result);// FFFFFF2Fd + if ( result.interrupt ) + return; + + { + // for different multi or same ring id: x, u, i, c + // for same multi and different ring id: c, i, u, x + typedef turns::less<0, turns::less_op_linear_areal<0> > less; + std::sort(turns.begin(), turns.end(), less()); + + turns_analyser analyser; + analyse_each_turn(result, analyser, + turns.begin(), turns.end(), + geometry1, geometry2, + boundary_checker1); + + if ( result.interrupt ) + return; + } + + // If 'c' (insersection_boundary) was not found we know that any Ls isn't equal to one of the Rings + if ( !interrupt_policy.is_boundary_found ) + { + relate::set(result); + } + // Don't calculate it if it's required + else if ( may_update(result) ) + { +// TODO: REVISE THIS CODE AND PROBABLY REWRITE SOME PARTS TO BE MORE HUMAN-READABLE +// IN GENERAL IT ANALYSES THE RINGS OF AREAL GEOMETRY AND DETECTS THE ONES THAT +// MAY OVERLAP THE INTERIOR OF LINEAR GEOMETRY (NO IPs OR NON-FAKE 'u' OPERATION) +// NOTE: For one case std::sort may be called again to sort data by operations for data already sorted by ring index +// In the worst case scenario the complexity will be O( NlogN + R*(N/R)log(N/R) ) +// So always should remain O(NlogN) -> for R==1 <-> 1(N/1)log(N/1), for R==N <-> N(N/N)log(N/N) +// Some benchmarking should probably be done to check if only one std::sort should be used + + // sort by multi_index and rind_index + std::sort(turns.begin(), turns.end(), less_ring()); + + typedef typename std::vector::iterator turn_iterator; + + turn_iterator it = turns.begin(); + segment_identifier * prev_seg_id_ptr = NULL; + // for each ring + for ( ; it != turns.end() ; ) + { + // it's the next single geometry + if ( prev_seg_id_ptr == NULL + || prev_seg_id_ptr->multi_index != it->operations[1].seg_id.multi_index ) + { + // if the first ring has no IPs + if ( it->operations[1].seg_id.ring_index > -1 ) + { + // we can be sure that the exterior overlaps the boundary + relate::set(result); + break; + } + // if there was some previous ring + if ( prev_seg_id_ptr != NULL ) + { + int const next_ring_index = prev_seg_id_ptr->ring_index + 1; + BOOST_ASSERT(next_ring_index >= 0); + + // if one of the last rings of previous single geometry was ommited + if ( static_cast(next_ring_index) + < geometry::num_interior_rings( + single_geometry(geometry2, *prev_seg_id_ptr)) ) + { + // we can be sure that the exterior overlaps the boundary + relate::set(result); + break; + } + } + } + // if it's the same single geometry + else /*if ( previous_multi_index == it->operations[1].seg_id.multi_index )*/ + { + // and we jumped over one of the rings + if ( prev_seg_id_ptr != NULL // just in case + && prev_seg_id_ptr->ring_index + 1 < it->operations[1].seg_id.ring_index ) + { + // we can be sure that the exterior overlaps the boundary + relate::set(result); + break; + } + } + + prev_seg_id_ptr = boost::addressof(it->operations[1].seg_id); + + // find the next ring first iterator and check if the analysis should be performed + has_boundary_intersection has_boundary_inters; + turn_iterator next = find_next_ring(it, turns.end(), has_boundary_inters); + + // if there is no 1d overlap with the boundary + if ( !has_boundary_inters.result ) + { + // we can be sure that the exterior overlaps the boundary + relate::set(result); + break; + } + // else there is 1d overlap with the boundary so we must analyse the boundary + else + { + // u, c + typedef turns::less<1, turns::less_op_areal_linear<1> > less; + std::sort(it, next, less()); + + // analyse + areal_boundary_analyser analyser; + for ( turn_iterator rit = it ; rit != next ; ++rit ) + { + // if the analyser requests, break the search + if ( !analyser.apply(it, rit, next) ) + break; + } + + // if the boundary of Areal goes out of the Linear + if ( analyser.is_union_detected ) + { + // we can be sure that the boundary of Areal overlaps the exterior of Linear + relate::set(result); + break; + } + } + + it = next; + } + + // if there was some previous ring + if ( prev_seg_id_ptr != NULL ) + { + int const next_ring_index = prev_seg_id_ptr->ring_index + 1; + BOOST_ASSERT(next_ring_index >= 0); + + // if one of the last rings of previous single geometry was ommited + if ( static_cast(next_ring_index) + < geometry::num_interior_rings( + single_geometry(geometry2, *prev_seg_id_ptr)) ) + { + // we can be sure that the exterior overlaps the boundary + relate::set(result); + } + } + } + } + + // interrupt policy which may be passed to get_turns to interrupt the analysis + // based on the info in the passed result/mask + template + class interrupt_policy_linear_areal + { + public: + static bool const enabled = true; + + interrupt_policy_linear_areal(Areal const& areal, Result & result) + : m_result(result), m_areal(areal) + , is_boundary_found(false) + {} + +// TODO: since we update result for some operations here, we may not do it in the analyser! + + template + inline bool apply(Range const& turns) + { + typedef typename boost::range_iterator::type iterator; + + for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it) + { + if ( it->operations[0].operation == overlay::operation_intersection ) + { + bool const no_interior_rings + = geometry::num_interior_rings( + single_geometry(m_areal, it->operations[1].seg_id)) == 0; + + // WARNING! THIS IS TRUE ONLY IF THE POLYGON IS SIMPLE! + // OR WITHOUT INTERIOR RINGS (AND OF COURSE VALID) + if ( no_interior_rings ) + update(m_result); + } + else if ( it->operations[0].operation == overlay::operation_continue ) + { + update(m_result); + is_boundary_found = true; + } + else if ( ( it->operations[0].operation == overlay::operation_union + || it->operations[0].operation == overlay::operation_blocked ) + && it->operations[0].position == overlay::position_middle ) + { +// TODO: here we could also check the boundaries and set BB at this point + update(m_result); + } + } + + return m_result.interrupt; + } + + private: + Result & m_result; + Areal const& m_areal; + + public: + bool is_boundary_found; + }; + + // This analyser should be used like Input or SinglePass Iterator + // IMPORTANT! It should be called also for the end iterator - last + template + class turns_analyser + { + typedef typename TurnInfo::point_type turn_point_type; + + static const std::size_t op_id = 0; + static const std::size_t other_op_id = 1; + + public: + turns_analyser() + : m_previous_turn_ptr(NULL) + , m_previous_operation(overlay::operation_none) + , m_boundary_counter(0) + , m_interior_detected(false) + , m_first_interior_other_id_ptr(NULL) + {} + + template + void apply(Result & res, TurnIt it, + Geometry const& geometry, + OtherGeometry const& other_geometry, + BoundaryChecker const& boundary_checker) + { + overlay::operation_type op = it->operations[op_id].operation; + + if ( op != overlay::operation_union + && op != overlay::operation_intersection + && op != overlay::operation_blocked + && op != overlay::operation_continue ) // operation_boundary / operation_boundary_intersection + { + return; + } + + segment_identifier const& seg_id = it->operations[op_id].seg_id; + segment_identifier const& other_id = it->operations[other_op_id].seg_id; + + const bool first_in_range = m_seg_watcher.update(seg_id); + + // handle possible exit + bool fake_enter_detected = false; + if ( m_exit_watcher.get_exit_operation() == overlay::operation_union ) + { + // real exit point - may be multiple + // we know that we entered and now we exit + if ( ! turn_on_the_same_ip(m_exit_watcher.get_exit_turn(), *it) ) + { + m_exit_watcher.reset_detected_exit(); + + // not the last IP + update(res); + } + // fake exit point, reset state + else if ( op == overlay::operation_intersection + || op == overlay::operation_continue ) // operation_boundary + { + m_exit_watcher.reset_detected_exit(); + fake_enter_detected = true; + } + } + else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked ) + { + // ignore multiple BLOCKs + if ( op == overlay::operation_blocked ) + return; + + if ( ( op == overlay::operation_intersection + || op == overlay::operation_continue ) + && turn_on_the_same_ip(m_exit_watcher.get_exit_turn(), *it) ) + { + fake_enter_detected = true; + } + + m_exit_watcher.reset_detected_exit(); + } + +// NOTE: THE WHOLE m_interior_detected HANDLING IS HERE BECAUSE WE CAN'T EFFICIENTLY SORT TURNS (CORRECTLY) +// BECAUSE THE SAME IP MAY BE REPRESENTED BY TWO SEGMENTS WITH DIFFERENT DISTANCES +// IT WOULD REQUIRE THE CALCULATION OF MAX DISTANCE +// TODO: WE COULD GET RID OF THE TEST IF THE DISTANCES WERE NORMALIZED + +// TODO: THIS IS POTENTIALLY ERROREOUS! +// THIS ALGORITHM DEPENDS ON SOME SPECIFIC SEQUENCE OF OPERATIONS +// IT WOULD GIVE WRONG RESULTS E.G. +// IN THE CASE OF SELF-TOUCHING POINT WHEN 'i' WOULD BE BEFORE 'u' + + // handle the interior overlap + if ( m_interior_detected ) + { + // real interior overlap + if ( ! turn_on_the_same_ip(*m_previous_turn_ptr, *it) ) + { + update(res); + m_interior_detected = false; + } + // fake interior overlap + else if ( op == overlay::operation_continue ) + { + m_interior_detected = false; + } + else if ( op == overlay::operation_union ) + { +// TODO: this probably is not a good way of handling the interiors/enters +// the solution similar to exit_watcher would be more robust +// all enters should be kept and handled. +// maybe integrate it with the exit_watcher -> enter_exit_watcher + if ( m_first_interior_other_id_ptr + && m_first_interior_other_id_ptr->multi_index == other_id.multi_index ) + { + m_interior_detected = false; + } + } + } + + // i/u, c/u + if ( op == overlay::operation_intersection + || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection + { + bool no_enters_detected = m_exit_watcher.is_outside(); + m_exit_watcher.enter(*it); + + if ( op == overlay::operation_intersection ) + { + if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear ) + --m_boundary_counter; + + if ( m_boundary_counter == 0 ) + { + // interiors overlaps + //update(res); + +// TODO: think about the implementation of the more robust version +// this way only the first enter will be handled + if ( !m_interior_detected ) + { + // don't update now + // we might enter a boundary of some other ring on the same IP + m_interior_detected = true; + m_first_interior_other_id_ptr = boost::addressof(other_id); + } + } + } + else // operation_boundary + { + // don't add to the count for all met boundaries + // only if this is the "new" boundary + if ( first_in_range || !it->operations[op_id].is_collinear ) + ++m_boundary_counter; + + update(res); + } + + bool const this_b + = is_ip_on_boundary(it->point, + it->operations[op_id], + boundary_checker, + seg_id); + // going inside on boundary point + if ( this_b ) + { + update(res); + } + // going inside on non-boundary point + else + { + update(res); + + // if we didn't enter in the past, we were outside + if ( no_enters_detected + && ! fake_enter_detected + && it->operations[op_id].position != overlay::position_front ) + { +// TODO: calculate_from_inside() is only needed if the current Linestring is not closed + bool const from_inside = first_in_range + && calculate_from_inside(geometry, + other_geometry, + *it); + + if ( from_inside ) + update(res); + else + update(res); + + // if it's the first IP then the first point is outside + if ( first_in_range ) + { + bool const front_b = is_endpoint_on_boundary( + range::front(sub_range(geometry, seg_id)), + boundary_checker); + + // if there is a boundary on the first point + if ( front_b ) + { + if ( from_inside ) + update(res); + else + update(res); + } + } + } + } + } + // u/u, x/u + else if ( op == overlay::operation_union || op == overlay::operation_blocked ) + { + bool const op_blocked = op == overlay::operation_blocked; + bool const no_enters_detected = m_exit_watcher.is_outside() +// TODO: is this condition ok? +// TODO: move it into the exit_watcher? + && m_exit_watcher.get_exit_operation() == overlay::operation_none; + + if ( op == overlay::operation_union ) + { + if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear ) + --m_boundary_counter; + } + else // overlay::operation_blocked + { + m_boundary_counter = 0; + } + + // we're inside, possibly going out right now + if ( ! no_enters_detected ) + { + if ( op_blocked + && it->operations[op_id].position == overlay::position_back ) // ignore spikes! + { + // check if this is indeed the boundary point + // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same + if ( is_endpoint_on_boundary(it->point, boundary_checker) ) + { + update(res); + } + } + // union, inside, but no exit -> collinear on self-intersection point + // not needed since we're already inside the boundary + /*else if ( !exit_detected ) + { + update(res); + }*/ + } + // we're outside or inside and this is the first turn + else + { + bool const this_b = is_ip_on_boundary(it->point, + it->operations[op_id], + boundary_checker, + seg_id); + // if current IP is on boundary of the geometry + if ( this_b ) + { + update(res); + } + // if current IP is not on boundary of the geometry + else + { + update(res); + } + + // TODO: very similar code is used in the handling of intersection + if ( it->operations[op_id].position != overlay::position_front ) + { +// TODO: calculate_from_inside() is only needed if the current Linestring is not closed + bool const first_from_inside = first_in_range + && calculate_from_inside(geometry, + other_geometry, + *it); + if ( first_from_inside ) + { + update(res); + + // notify the exit_watcher that we started inside + m_exit_watcher.enter(*it); + } + else + { + update(res); + } + + // first IP on the last segment point - this means that the first point is outside or inside + if ( first_in_range && ( !this_b || op_blocked ) ) + { + bool const front_b = is_endpoint_on_boundary( + range::front(sub_range(geometry, seg_id)), + boundary_checker); + + // if there is a boundary on the first point + if ( front_b ) + { + if ( first_from_inside ) + update(res); + else + update(res); + } + } + } + } + + // if we're going along a boundary, we exit only if the linestring was collinear + if ( m_boundary_counter == 0 + || it->operations[op_id].is_collinear ) + { + // notify the exit watcher about the possible exit + m_exit_watcher.exit(*it); + } + } + + // store ref to previously analysed (valid) turn + m_previous_turn_ptr = boost::addressof(*it); + // and previously analysed (valid) operation + m_previous_operation = op; + } + + // it == last + template + void apply(Result & res, + TurnIt first, TurnIt last, + Geometry const& geometry, + OtherGeometry const& /*other_geometry*/, + BoundaryChecker const& boundary_checker) + { + boost::ignore_unused(first, last); + //BOOST_ASSERT( first != last ); + + // here, the possible exit is the real one + // we know that we entered and now we exit + if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT + ||*/ m_previous_operation == overlay::operation_union + && !m_interior_detected ) + { + // for sure + update(res); + + BOOST_ASSERT(first != last); + BOOST_ASSERT(m_previous_turn_ptr); + + segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + bool const prev_back_b = is_endpoint_on_boundary( + range::back(sub_range(geometry, prev_seg_id)), + boundary_checker); + + // if there is a boundary on the last point + if ( prev_back_b ) + { + update(res); + } + } + // we might enter some Areal and didn't go out, + else if ( m_previous_operation == overlay::operation_intersection + || m_interior_detected ) + { + // just in case + update(res); + m_interior_detected = false; + + BOOST_ASSERT(first != last); + BOOST_ASSERT(m_previous_turn_ptr); + + segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + bool const prev_back_b = is_endpoint_on_boundary( + range::back(sub_range(geometry, prev_seg_id)), + boundary_checker); + + // if there is a boundary on the last point + if ( prev_back_b ) + { + update(res); + } + } + + BOOST_ASSERT_MSG(m_previous_operation != overlay::operation_continue, + "Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)"); + + // Reset exit watcher before the analysis of the next Linestring + m_exit_watcher.reset(); + m_boundary_counter = 0; + } + + // check if the passed turn's segment of Linear geometry arrived + // from the inside or the outside of the Areal geometry + template + static inline bool calculate_from_inside(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Turn const& turn) + { + if ( turn.operations[op_id].position == overlay::position_front ) + return false; + + typename sub_range_return_type::type + range1 = sub_range(geometry1, turn.operations[op_id].seg_id); + + typedef detail::normalized_view const range2_type; + typedef typename boost::range_iterator::type range2_iterator; + range2_type range2(sub_range(geometry2, turn.operations[other_op_id].seg_id)); + + BOOST_ASSERT(boost::size(range1)); + std::size_t const s2 = boost::size(range2); + BOOST_ASSERT(s2 > 2); + std::size_t const seg_count2 = s2 - 1; + + std::size_t const p_seg_ij = turn.operations[op_id].seg_id.segment_index; + std::size_t const q_seg_ij = turn.operations[other_op_id].seg_id.segment_index; + + BOOST_ASSERT(p_seg_ij + 1 < boost::size(range1)); + BOOST_ASSERT(q_seg_ij + 1 < s2); + + point1_type const& pi = range::at(range1, p_seg_ij); + point2_type const& qi = range::at(range2, q_seg_ij); + point2_type const& qj = range::at(range2, q_seg_ij + 1); + point1_type qi_conv; + geometry::convert(qi, qi_conv); + bool const is_ip_qj = equals::equals_point_point(turn.point, qj); +// TODO: test this! +// BOOST_ASSERT(!equals::equals_point_point(turn.point, pi)); +// BOOST_ASSERT(!equals::equals_point_point(turn.point, qi)); + point1_type new_pj; + geometry::convert(turn.point, new_pj); + + if ( is_ip_qj ) + { + std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2; +// TODO: the following function should return immediately, however the worst case complexity is O(N) +// It would be good to replace it with some O(1) mechanism + range2_iterator qk_it = find_next_non_duplicated(boost::begin(range2), + boost::begin(range2) + q_seg_jk, + boost::end(range2)); + + // Will this sequence of points be always correct? + overlay::side_calculator side_calc(qi_conv, new_pj, pi, qi, qj, *qk_it); + + return calculate_from_inside_sides(side_calc); + } + else + { + point1_type new_qj; + geometry::convert(turn.point, new_qj); + + overlay::side_calculator side_calc(qi_conv, new_pj, pi, qi, new_qj, qj); + + return calculate_from_inside_sides(side_calc); + } + } + + template + static inline It find_next_non_duplicated(It first, It current, It last) + { + BOOST_ASSERT( current != last ); + + It it = current; + + for ( ++it ; it != last ; ++it ) + { + if ( !equals::equals_point_point(*current, *it) ) + return it; + } + + // if not found start from the beginning + for ( it = first ; it != current ; ++it ) + { + if ( !equals::equals_point_point(*current, *it) ) + return it; + } + + return current; + } + + // calculate inside or outside based on side_calc + // this is simplified version of a check from equal<> + template + static inline bool calculate_from_inside_sides(SideCalc const& side_calc) + { + int const side_pk_p = side_calc.pk_wrt_p1(); + int const side_qk_p = side_calc.qk_wrt_p1(); + // If they turn to same side (not opposite sides) + if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p)) + { + int const side_pk_q2 = side_calc.pk_wrt_q2(); + return side_pk_q2 == -1; + } + else + { + return side_pk_p == -1; + } + } + + private: + exit_watcher m_exit_watcher; + segment_watcher m_seg_watcher; + TurnInfo * m_previous_turn_ptr; + overlay::operation_type m_previous_operation; + unsigned m_boundary_counter; + bool m_interior_detected; + const segment_identifier * m_first_interior_other_id_ptr; + }; + + // call analyser.apply() for each turn in range + // IMPORTANT! The analyser is also called for the end iterator - last + template + static inline void analyse_each_turn(Result & res, + Analyser & analyser, + TurnIt first, TurnIt last, + Geometry const& geometry, + OtherGeometry const& other_geometry, + BoundaryChecker const& boundary_checker) + { + if ( first == last ) + return; + + for ( TurnIt it = first ; it != last ; ++it ) + { + analyser.apply(res, it, + geometry, other_geometry, + boundary_checker); + + if ( res.interrupt ) + return; + } + + analyser.apply(res, first, last, + geometry, other_geometry, + boundary_checker); + } + + // less comparator comparing multi_index then ring_index + // may be used to sort turns by ring + struct less_ring + { + template + inline bool operator()(Turn const& left, Turn const& right) const + { + return left.operations[1].seg_id.multi_index < right.operations[1].seg_id.multi_index + || ( left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index + && left.operations[1].seg_id.ring_index < right.operations[1].seg_id.ring_index ); + } + }; + + // policy/functor checking if a turn's operation is operation_continue + struct has_boundary_intersection + { + has_boundary_intersection() + : result(false) {} + + template + inline void operator()(Turn const& turn) + { + if ( turn.operations[1].operation == overlay::operation_continue ) + result = true; + } + + bool result; + }; + + // iterate through the range and search for the different multi_index or ring_index + // also call fun for each turn in the current range + template + static inline It find_next_ring(It first, It last, Fun & fun) + { + if ( first == last ) + return last; + + int const multi_index = first->operations[1].seg_id.multi_index; + int const ring_index = first->operations[1].seg_id.ring_index; + + fun(*first); + ++first; + + for ( ; first != last ; ++first ) + { + if ( multi_index != first->operations[1].seg_id.multi_index + || ring_index != first->operations[1].seg_id.ring_index ) + { + return first; + } + + fun(*first); + } + + return last; + } + + // analyser which called for turns sorted by seg/distance/operation + // checks if the boundary of Areal geometry really got out + // into the exterior of Linear geometry + template + class areal_boundary_analyser + { + public: + areal_boundary_analyser() + : is_union_detected(false) + , m_previous_turn_ptr(NULL) + {} + + template + bool apply(TurnIt /*first*/, TurnIt it, TurnIt last) + { + overlay::operation_type op = it->operations[1].operation; + + if ( it != last ) + { + if ( op != overlay::operation_union + && op != overlay::operation_continue ) + { + return true; + } + + if ( is_union_detected ) + { + BOOST_ASSERT(m_previous_turn_ptr != NULL); + if ( !detail::equals::equals_point_point(it->point, m_previous_turn_ptr->point) ) + { + // break + return false; + } + else if ( op == overlay::operation_continue ) // operation_boundary + { + is_union_detected = false; + } + } + + if ( op == overlay::operation_union ) + { + is_union_detected = true; + m_previous_turn_ptr = boost::addressof(*it); + } + + return true; + } + else + { + return false; + } + } + + bool is_union_detected; + + private: + const TurnInfo * m_previous_turn_ptr; + }; +}; + +template +struct areal_linear +{ + template + static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result) + { + linear_areal::apply(geometry2, geometry1, result); + } +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP diff --git a/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/boost/geometry/algorithms/detail/relate/linear_linear.hpp new file mode 100644 index 0000000000..263c82de56 --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -0,0 +1,768 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +template +class disjoint_linestring_pred +{ +public: + disjoint_linestring_pred(Result & res, + BoundaryChecker const& boundary_checker) + : m_result(res) + , m_boundary_checker(boundary_checker) + , m_flags(0) + { + if ( ! may_update(m_result) ) + { + m_flags |= 1; + } + if ( ! may_update(m_result) ) + { + m_flags |= 2; + } + } + + template + bool operator()(Linestring const& linestring) + { + if ( m_flags == 3 ) + { + return false; + } + + std::size_t const count = boost::size(linestring); + + // invalid input + if ( count < 2 ) + { + // ignore + // TODO: throw an exception? + return true; + } + + // point-like linestring + if ( count == 2 + && equals::equals_point_point(range::front(linestring), + range::back(linestring)) ) + { + update(m_result); + } + else + { + update(m_result); + m_flags |= 1; + + // check if there is a boundary + if ( m_flags < 2 + && ( m_boundary_checker.template + is_endpoint_boundary(range::front(linestring)) + || m_boundary_checker.template + is_endpoint_boundary(range::back(linestring)) ) ) + { + update(m_result); + m_flags |= 2; + } + } + + return m_flags != 3 + && ! m_result.interrupt; + } + +private: + Result & m_result; + BoundaryChecker const& m_boundary_checker; + unsigned m_flags; +}; + +template +struct linear_linear +{ + static const bool interruption_enabled = true; + + typedef typename geometry::point_type::type point1_type; + typedef typename geometry::point_type::type point2_type; + + template + static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result) + { + // The result should be FFFFFFFFF + relate::set::value>(result);// FFFFFFFFd, d in [1,9] or T + if ( result.interrupt ) + return; + + // get and analyse turns + typedef typename turns::get_turns::turn_info turn_type; + std::vector turns; + + interrupt_policy_linear_linear interrupt_policy(result); + + turns::get_turns + < + Geometry1, + Geometry2, + detail::get_turns::get_turn_info_type > + >::apply(turns, geometry1, geometry2, interrupt_policy); + + if ( result.interrupt ) + return; + + boundary_checker boundary_checker1(geometry1); + disjoint_linestring_pred, false> pred1(result, boundary_checker1); + for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); + if ( result.interrupt ) + return; + + boundary_checker boundary_checker2(geometry2); + disjoint_linestring_pred, true> pred2(result, boundary_checker2); + for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); + if ( result.interrupt ) + return; + + if ( turns.empty() ) + return; + + // TODO: turns must be sorted and followed only if it's possible to go out and in on the same point + // for linear geometries union operation must be detected which I guess would be quite often + + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) + { + typedef turns::less<0, turns::less_op_linear_linear<0> > less; + std::sort(turns.begin(), turns.end(), less()); + + turns_analyser analyser; + analyse_each_turn(result, analyser, + turns.begin(), turns.end(), + geometry1, geometry2, + boundary_checker1, boundary_checker2); + } + + if ( result.interrupt ) + return; + + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) + { + typedef turns::less<1, turns::less_op_linear_linear<1> > less; + std::sort(turns.begin(), turns.end(), less()); + + turns_analyser analyser; + analyse_each_turn(result, analyser, + turns.begin(), turns.end(), + geometry2, geometry1, + boundary_checker2, boundary_checker1); + } + } + + template + class interrupt_policy_linear_linear + { + public: + static bool const enabled = true; + + explicit interrupt_policy_linear_linear(Result & result) + : m_result(result) + {} + +// TODO: since we update result for some operations here, we may not do it in the analyser! + + template + inline bool apply(Range const& turns) + { + typedef typename boost::range_iterator::type iterator; + + for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it) + { + if ( it->operations[0].operation == overlay::operation_intersection + || it->operations[1].operation == overlay::operation_intersection ) + { + update(m_result); + } + else if ( ( it->operations[0].operation == overlay::operation_union + || it->operations[0].operation == overlay::operation_blocked + || it->operations[1].operation == overlay::operation_union + || it->operations[1].operation == overlay::operation_blocked ) + && it->operations[0].position == overlay::position_middle + && it->operations[1].position == overlay::position_middle ) + { +// TODO: here we could also check the boundaries and set IB,BI,BB at this point + update(m_result); + } + } + + return m_result.interrupt; + } + + private: + Result & m_result; + }; + + // This analyser should be used like Input or SinglePass Iterator + template + class turns_analyser + { + typedef typename TurnInfo::point_type turn_point_type; + + static const std::size_t op_id = OpId; + static const std::size_t other_op_id = (OpId + 1) % 2; + static const bool transpose_result = OpId != 0; + + public: + turns_analyser() + : m_previous_turn_ptr(NULL) + , m_previous_operation(overlay::operation_none) + , m_degenerated_turn_ptr(NULL) + {} + + template + void apply(Result & res, TurnIt it, + Geometry const& geometry, + OtherGeometry const& other_geometry, + BoundaryChecker const& boundary_checker, + OtherBoundaryChecker const& other_boundary_checker) + { + overlay::operation_type const op = it->operations[op_id].operation; + + segment_identifier const& seg_id = it->operations[op_id].seg_id; + segment_identifier const& other_id = it->operations[other_op_id].seg_id; + + bool const first_in_range = m_seg_watcher.update(seg_id); + + if ( op != overlay::operation_union + && op != overlay::operation_intersection + && op != overlay::operation_blocked ) + { + // degenerated turn + if ( op == overlay::operation_continue + && it->method == overlay::method_none + && m_exit_watcher.is_outside(*it) + /*&& ( m_exit_watcher.get_exit_operation() == overlay::operation_none + || ! turn_on_the_same_ip(m_exit_watcher.get_exit_turn(), *it) )*/ ) + { + // TODO: rewrite the above condition + + // WARNING! For spikes the above condition may be TRUE + // When degenerated turns are be marked in a different way than c,c/c + // different condition will be checked + + handle_degenerated(res, *it, + geometry, other_geometry, + boundary_checker, other_boundary_checker, + first_in_range); + + // TODO: not elegant solution! should be rewritten. + if ( it->operations[op_id].position == overlay::position_back ) + { + m_previous_operation = overlay::operation_blocked; + m_exit_watcher.reset_detected_exit(); + } + } + + return; + } + + // reset + m_degenerated_turn_ptr = NULL; + + // handle possible exit + bool fake_enter_detected = false; + if ( m_exit_watcher.get_exit_operation() == overlay::operation_union ) + { + // real exit point - may be multiple + // we know that we entered and now we exit + if ( ! turn_on_the_same_ip(m_exit_watcher.get_exit_turn(), *it) ) + { + m_exit_watcher.reset_detected_exit(); + + // not the last IP + update(res); + } + // fake exit point, reset state + else if ( op == overlay::operation_intersection ) + { + m_exit_watcher.reset_detected_exit(); + fake_enter_detected = true; + } + } + else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked ) + { + // ignore multiple BLOCKs + if ( op == overlay::operation_blocked ) + return; + + if ( op == overlay::operation_intersection + && turn_on_the_same_ip(m_exit_watcher.get_exit_turn(), *it) ) + { + fake_enter_detected = true; + } + + m_exit_watcher.reset_detected_exit(); + } + + // i/i, i/x, i/u + if ( op == overlay::operation_intersection ) + { + bool const was_outside = m_exit_watcher.is_outside(); + m_exit_watcher.enter(*it); + + // interiors overlaps + update(res); + + bool const this_b = it->operations[op_id].position == overlay::position_front // ignore spikes! + && is_ip_on_boundary(it->point, + it->operations[op_id], + boundary_checker, + seg_id); + + // going inside on boundary point + // may be front only + if ( this_b ) + { + // may be front and back + bool const other_b = is_ip_on_boundary(it->point, + it->operations[other_op_id], + other_boundary_checker, + other_id); + + // it's also the boundary of the other geometry + if ( other_b ) + { + update(res); + } + else + { + update(res); + } + } + // going inside on non-boundary point + else + { + // if we didn't enter in the past, we were outside + if ( was_outside + && ! fake_enter_detected + && it->operations[op_id].position != overlay::position_front ) + { + update(res); + + // if it's the first IP then the first point is outside + if ( first_in_range ) + { + bool const front_b = is_endpoint_on_boundary( + range::front(sub_range(geometry, seg_id)), + boundary_checker); + + // if there is a boundary on the first point + if ( front_b ) + { + update(res); + } + } + } + } + } + // u/i, u/u, u/x, x/i, x/u, x/x + else if ( op == overlay::operation_union || op == overlay::operation_blocked ) + { + // TODO: is exit watcher still needed? + // couldn't is_collinear and some going inside counter be used instead? + + bool const is_collinear = it->operations[op_id].is_collinear; + bool const was_outside = m_exit_watcher.is_outside() + && m_exit_watcher.get_exit_operation() == overlay::operation_none; +// TODO: move the above condition into the exit_watcher? + + // to exit we must be currently inside and the current segment must be collinear + if ( !was_outside && is_collinear ) + { + m_exit_watcher.exit(*it, false); + } + + bool const op_blocked = op == overlay::operation_blocked; + + // we're inside and going out from inside + // possibly going out right now + if ( ! was_outside && is_collinear ) + { + if ( op_blocked + && it->operations[op_id].position == overlay::position_back ) // ignore spikes! + { + // check if this is indeed the boundary point + // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same + if ( is_endpoint_on_boundary(it->point, boundary_checker) ) + { + // may be front and back + bool const other_b = is_ip_on_boundary(it->point, + it->operations[other_op_id], + other_boundary_checker, + other_id); + // it's also the boundary of the other geometry + if ( other_b ) + { + update(res); + } + else + { + update(res); + } + } + } + } + // we're outside or intersects some segment from the outside + else + { + // if we are truly outside + if ( was_outside + && it->operations[op_id].position != overlay::position_front + /*&& !is_collinear*/ ) + { + update(res); + } + + // boundaries don't overlap - just an optimization + if ( it->method == overlay::method_crosses ) + { + // the L1 is going from one side of the L2 to the other through the point + update(res); + + // it's the first point in range + if ( first_in_range ) + { + bool const front_b = is_endpoint_on_boundary( + range::front(sub_range(geometry, seg_id)), + boundary_checker); + + // if there is a boundary on the first point + if ( front_b ) + { + update(res); + } + } + } + // method other than crosses, check more conditions + else + { + bool const this_b = is_ip_on_boundary(it->point, + it->operations[op_id], + boundary_checker, + seg_id); + + bool const other_b = is_ip_on_boundary(it->point, + it->operations[other_op_id], + other_boundary_checker, + other_id); + + // if current IP is on boundary of the geometry + if ( this_b ) + { + // it's also the boundary of the other geometry + if ( other_b ) + { + update(res); + } + else + { + update(res); + } + } + // if current IP is not on boundary of the geometry + else + { + // it's also the boundary of the other geometry + if ( other_b ) + { + update(res); + } + else + { + update(res); + } + } + + // first IP on the last segment point - this means that the first point is outside + if ( first_in_range + && ( !this_b || op_blocked ) + && was_outside + && it->operations[op_id].position != overlay::position_front + /*&& !is_collinear*/ ) + { + bool const front_b = is_endpoint_on_boundary( + range::front(sub_range(geometry, seg_id)), + boundary_checker); + + // if there is a boundary on the first point + if ( front_b ) + { + update(res); + } + } + + } + } + } + + // store ref to previously analysed (valid) turn + m_previous_turn_ptr = boost::addressof(*it); + // and previously analysed (valid) operation + m_previous_operation = op; + } + + // Called for last + template + void apply(Result & res, + TurnIt first, TurnIt last, + Geometry const& geometry, + OtherGeometry const& /*other_geometry*/, + BoundaryChecker const& boundary_checker, + OtherBoundaryChecker const& /*other_boundary_checker*/) + { + boost::ignore_unused(first, last); + //BOOST_ASSERT( first != last ); + + // here, the possible exit is the real one + // we know that we entered and now we exit + if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT + ||*/ m_previous_operation == overlay::operation_union + || m_degenerated_turn_ptr ) + { + update(res); + + BOOST_ASSERT(first != last); + + const TurnInfo * turn_ptr = NULL; + if ( m_degenerated_turn_ptr ) + turn_ptr = m_degenerated_turn_ptr; + else if ( m_previous_turn_ptr ) + turn_ptr = m_previous_turn_ptr; + + if ( turn_ptr ) + { + segment_identifier const& prev_seg_id = turn_ptr->operations[op_id].seg_id; + + //BOOST_ASSERT(!boost::empty(sub_range(geometry, prev_seg_id))); + bool const prev_back_b = is_endpoint_on_boundary( + range::back(sub_range(geometry, prev_seg_id)), + boundary_checker); + + // if there is a boundary on the last point + if ( prev_back_b ) + { + update(res); + } + } + } + + // Just in case, + // reset exit watcher before the analysis of the next Linestring + // note that if there are some enters stored there may be some error above + m_exit_watcher.reset(); + + m_previous_turn_ptr = NULL; + m_previous_operation = overlay::operation_none; + m_degenerated_turn_ptr = NULL; + } + + template + void handle_degenerated(Result & res, + Turn const& turn, + Geometry const& geometry, + OtherGeometry const& other_geometry, + BoundaryChecker const& boundary_checker, + OtherBoundaryChecker const& /*other_boundary_checker*/, + bool first_in_range) + { + typename detail::single_geometry_return_type::type + ls1_ref = detail::single_geometry(geometry, turn.operations[op_id].seg_id); + typename detail::single_geometry_return_type::type + ls2_ref = detail::single_geometry(other_geometry, turn.operations[other_op_id].seg_id); + + // only one of those should be true: + + if ( turn.operations[op_id].position == overlay::position_front ) + { + // valid, point-sized + if ( boost::size(ls2_ref) == 2 ) + { + bool const front_b = is_endpoint_on_boundary(turn.point, boundary_checker); + + if ( front_b ) + { + update(res); + } + else + { + update(res); + } + + // operation 'c' should be last for the same IP so we know that the next point won't be the same + update(res); + + m_degenerated_turn_ptr = boost::addressof(turn); + } + } + else if ( turn.operations[op_id].position == overlay::position_back ) + { + // valid, point-sized + if ( boost::size(ls2_ref) == 2 ) + { + update(res); + + bool const back_b = is_endpoint_on_boundary(turn.point, boundary_checker); + + if ( back_b ) + { + update(res); + } + else + { + update(res); + } + + if ( first_in_range ) + { + //BOOST_ASSERT(!boost::empty(ls1_ref)); + bool const front_b = is_endpoint_on_boundary( + range::front(ls1_ref), boundary_checker); + if ( front_b ) + { + update(res); + } + } + } + } + else if ( turn.operations[op_id].position == overlay::position_middle + && turn.operations[other_op_id].position == overlay::position_middle ) + { + update(res); + + // here we don't know which one is degenerated + + bool const is_point1 = boost::size(ls1_ref) == 2 + && equals::equals_point_point(range::front(ls1_ref), range::back(ls1_ref)); + bool const is_point2 = boost::size(ls2_ref) == 2 + && equals::equals_point_point(range::front(ls2_ref), range::back(ls2_ref)); + + // if the second one is degenerated + if ( !is_point1 && is_point2 ) + { + update(res); + + if ( first_in_range ) + { + //BOOST_ASSERT(!boost::empty(ls1_ref)); + bool const front_b = is_endpoint_on_boundary( + range::front(ls1_ref), boundary_checker); + if ( front_b ) + { + update(res); + } + } + + m_degenerated_turn_ptr = boost::addressof(turn); + } + } + + // NOTE: other.position == front and other.position == back + // will be handled later, for the other geometry + } + + private: + exit_watcher m_exit_watcher; + segment_watcher m_seg_watcher; + const TurnInfo * m_previous_turn_ptr; + overlay::operation_type m_previous_operation; + const TurnInfo * m_degenerated_turn_ptr; + }; + + template + static inline void analyse_each_turn(Result & res, + Analyser & analyser, + TurnIt first, TurnIt last, + Geometry const& geometry, + OtherGeometry const& other_geometry, + BoundaryChecker const& boundary_checker, + OtherBoundaryChecker const& other_boundary_checker) + { + if ( first == last ) + return; + + for ( TurnIt it = first ; it != last ; ++it ) + { + analyser.apply(res, it, + geometry, other_geometry, + boundary_checker, other_boundary_checker); + + if ( res.interrupt ) + return; + } + + analyser.apply(res, first, last, + geometry, other_geometry, + boundary_checker, other_boundary_checker); + } +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP diff --git a/boost/geometry/algorithms/detail/relate/point_geometry.hpp b/boost/geometry/algorithms/detail/relate/point_geometry.hpp new file mode 100644 index 0000000000..86c236d357 --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/point_geometry.hpp @@ -0,0 +1,205 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013, 2014, Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_POINT_GEOMETRY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_POINT_GEOMETRY_HPP + +#include +//#include +//#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +// non-point geometry +template +struct point_geometry +{ + // TODO: interrupt only if the topology check is complex + + static const bool interruption_enabled = true; + + template + static inline void apply(Point const& point, Geometry const& geometry, Result & result) + { + int pig = detail::within::point_in_geometry(point, geometry); + + if ( pig > 0 ) // within + { + set(result); + } + else if ( pig == 0 ) + { + set(result); + } + else // pig < 0 - not within + { + set(result); + } + + set::value, Transpose>(result); + + if ( result.interrupt ) + return; + + // the point is on the boundary + if ( pig == 0 ) + { + // NOTE: even for MLs, if there is at least one boundary point, + // somewhere there must be another one + + // check if there are other boundaries outside + typedef detail::relate::topology_check tc_t; + //tc_t tc(geometry, point); + //if ( tc.has_interior ) + set(result); + //if ( tc.has_boundary ) + set(result); + } + else + { + // check if there is a boundary in Geometry + typedef detail::relate::topology_check tc_t; + tc_t tc(geometry); + if ( tc.has_interior ) + set(result); + if ( tc.has_boundary ) + set(result); + } + } +}; + +// transposed result of point_geometry +template +struct geometry_point +{ + // TODO: interrupt only if the topology check is complex + + static const bool interruption_enabled = true; + + template + static inline void apply(Geometry const& geometry, Point const& point, Result & result) + { + point_geometry::apply(point, geometry, result); + } +}; + +// TODO: rewrite the folowing: + +//// NOTE: Those tests should be consistent with within(Point, Box) and covered_by(Point, Box) +//// There is no EPS used in those functions, values are compared using < or <= +//// so comparing MIN and MAX in the same way should be fine +// +//template ::value> +//struct box_has_interior +//{ +// static inline bool apply(Box const& box) +// { +// return geometry::get(box) < geometry::get(box) +// && box_has_interior::apply(box); +// } +//}; +// +//template +//struct box_has_interior +//{ +// static inline bool apply(Box const&) { return true; } +//}; +// +//// NOTE: especially important here (see the NOTE above). +// +//template ::value> +//struct box_has_equal_min_max +//{ +// static inline bool apply(Box const& box) +// { +// return geometry::get(box) == geometry::get(box) +// && box_has_equal_min_max::apply(box); +// } +//}; +// +//template +//struct box_has_equal_min_max +//{ +// static inline bool apply(Box const&) { return true; } +//}; +// +//template +//struct point_box +//{ +// static inline result apply(Point const& point, Box const& box) +// { +// result res; +// +// if ( geometry::within(point, box) ) // this also means that the box has interior +// { +// return result("0FFFFFTTT"); +// } +// else if ( geometry::covered_by(point, box) ) // point is on the boundary +// { +// //if ( box_has_interior::apply(box) ) +// //{ +// // return result("F0FFFFTTT"); +// //} +// //else if ( box_has_equal_min_max::apply(box) ) // no boundary outside point +// //{ +// // return result("F0FFFFFFT"); +// //} +// //else // no interior outside point +// //{ +// // return result("F0FFFFFTT"); +// //} +// return result("F0FFFF**T"); +// } +// else +// { +// /*if ( box_has_interior::apply(box) ) +// { +// return result("FF0FFFTTT"); +// } +// else +// { +// return result("FF0FFFFTT"); +// }*/ +// return result("FF0FFF*TT"); +// } +// +// return res; +// } +//}; +// +//template +//struct box_point +//{ +// static inline result apply(Box const& box, Point const& point) +// { +// if ( geometry::within(point, box) ) +// return result("0FTFFTFFT"); +// else if ( geometry::covered_by(point, box) ) +// return result("FF*0F*FFT"); +// else +// return result("FF*FFT0FT"); +// } +//}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_POINT_GEOMETRY_HPP diff --git a/boost/geometry/algorithms/detail/relate/point_point.hpp b/boost/geometry/algorithms/detail/relate/point_point.hpp new file mode 100644 index 0000000000..e623868b92 --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/point_point.hpp @@ -0,0 +1,242 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013, 2014, Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_POINT_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_POINT_POINT_HPP + +#include +#include + +#include +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +template +struct point_point +{ + static const bool interruption_enabled = false; + + template + static inline void apply(Point1 const& point1, Point2 const& point2, Result & result) + { + bool equal = detail::equals::equals_point_point(point1, point2); + if ( equal ) + { + set(result); + } + else + { + set(result); + set(result); + } + + set::value>(result); + } +}; + +template +std::pair point_multipoint_check(Point const& point, MultiPoint const& multi_point) +{ + bool found_inside = false; + bool found_outside = false; + + // point_in_geometry could be used here but why iterate over MultiPoint twice? + // we must search for a point in the exterior because all points in MultiPoint can be equal + + typedef typename boost::range_iterator::type iterator; + iterator it = boost::begin(multi_point); + iterator last = boost::end(multi_point); + for ( ; it != last ; ++it ) + { + bool ii = detail::equals::equals_point_point(point, *it); + + if ( ii ) + found_inside = true; + else + found_outside = true; + + if ( found_inside && found_outside ) + break; + } + + return std::make_pair(found_inside, found_outside); +} + +template +struct point_multipoint +{ + static const bool interruption_enabled = false; + + template + static inline void apply(Point const& point, MultiPoint const& multi_point, Result & result) + { + if ( boost::empty(multi_point) ) + { + // TODO: throw on empty input? + set(result); + return; + } + + std::pair rel = point_multipoint_check(point, multi_point); + + if ( rel.first ) // some point of MP is equal to P + { + set(result); + + if ( rel.second ) // a point of MP was found outside P + { + set(result); + } + } + else + { + set(result); + set(result); + } + + set::value, Transpose>(result); + } +}; + +template +struct multipoint_point +{ + static const bool interruption_enabled = false; + + template + static inline void apply(MultiPoint const& multi_point, Point const& point, Result & result) + { + point_multipoint::apply(point, multi_point, result); + } +}; + +template +struct multipoint_multipoint +{ + static const bool interruption_enabled = true; + + template + static inline void apply(MultiPoint1 const& multi_point1, MultiPoint2 const& multi_point2, Result & result) + { + { + // TODO: throw on empty input? + bool empty1 = boost::empty(multi_point1); + bool empty2 = boost::empty(multi_point2); + if ( empty1 && empty2 ) + { + return; + } + else if ( empty1 ) + { + set(result); + return; + } + else if ( empty2 ) + { + set(result); + return; + } + } + +// TODO: ADD A CHECK TO THE RESULT INDICATING IF THE FIRST AND/OR SECOND GEOMETRY MUST BE ANALYSED + +// TODO: if I/I is set for one MPt, this won't be changed when the other one in analysed +// so if e.g. only I/I must be analysed we musn't check the other MPt + +// TODO: Also, the geometry with the smaller number of points may be analysed first + //if ( boost::size(multi_point1) < boost::size(multi_point2) ) + + // NlogN + MlogN + bool all_handled = search(multi_point1, multi_point2, result); + + if ( all_handled || result.interrupt ) + return; + + // MlogM + NlogM + search(multi_point2, multi_point1, result); + } + + template + static inline bool search(SortedMultiPoint const& sorted_mpt, + IteratedMultiPoint const& iterated_mpt, + Result & result) + { + // sort points from the 1 MPt + typedef typename geometry::point_type::type point_type; + std::vector points(boost::begin(sorted_mpt), boost::end(sorted_mpt)); + std::sort(points.begin(), points.end(), less()); + + bool found_inside = false; + bool found_outside = false; + + // for each point in the second MPt + typedef typename boost::range_iterator::type iterator; + for ( iterator it = boost::begin(iterated_mpt) ; + it != boost::end(iterated_mpt) ; ++it ) + { + bool ii = + std::binary_search(points.begin(), points.end(), *it, less()); + if ( ii ) + found_inside = true; + else + found_outside = true; + + if ( found_inside && found_outside ) + break; + } + + // an optimization + bool all_handled = false; + + if ( found_inside ) // some point of MP2 is equal to some of MP1 + { +// TODO: if I/I is set for one MPt, this won't be changed when the other one in analysed +// so if e.g. only I/I must be analysed we musn't check the other MPt + + set(result); + + if ( found_outside ) // some point of MP2 was found outside of MP1 + { + set(result); + } + } + else + { + set(result); + set(result); + + // if no point is intersecting the other MPt then we musn't analyse the reversed case + all_handled = true; + } + + set::value, Transpose>(result); + + return all_handled; + } +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_POINT_POINT_HPP diff --git a/boost/geometry/algorithms/detail/relate/relate.hpp b/boost/geometry/algorithms/detail/relate/relate.hpp new file mode 100644 index 0000000000..946653452a --- /dev/null +++ b/boost/geometry/algorithms/detail/relate/relate.hpp @@ -0,0 +1,339 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// 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) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_RELATE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_RELATE_HPP + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +// Those are used only to allow dispatch::relate to produce compile-time error + +template ::type> +struct is_supported_by_generic +{ + static const bool value + = boost::is_same::value + || boost::is_same::value + || boost::is_same::value + || boost::is_same::value + || boost::is_same::value; +}; + +template ::type, + typename Tag2 = typename geometry::tag::type> +struct is_generic +{ + static const bool value = is_supported_by_generic::value + && is_supported_by_generic::value; +}; + + +template +struct is_generic +{ + static const bool value = is_supported_by_generic::value; +}; + +template +struct is_generic +{ + static const bool value = is_supported_by_generic::value; +}; + +template +struct is_generic +{ + static const bool value = false; +}; + + +}} // namespace detail::relate + +#ifndef DOXYGEN_NO_DISPATCH +namespace detail_dispatch { namespace relate { + + +template ::type, + typename Tag2 = typename geometry::tag::type, + int TopDim1 = geometry::topological_dimension::value, + int TopDim2 = geometry::topological_dimension::value, + bool IsGeneric = detail::relate::is_generic::value +> +struct relate : not_implemented +{}; + + +template +struct relate + : detail::relate::point_point +{}; + +template +struct relate + : detail::relate::point_multipoint +{}; + +template +struct relate + : detail::relate::multipoint_point +{}; + +template +struct relate + : detail::relate::multipoint_multipoint +{}; + +//template +//struct relate +// : detail::relate::point_box +//{}; +// +//template +//struct relate +// : detail::relate::box_point +//{}; + + +template +struct relate + : detail::relate::point_geometry +{}; + +template +struct relate + : detail::relate::geometry_point +{}; + + +template +struct relate + : detail::relate::linear_linear +{}; + + +template +struct relate + : detail::relate::linear_areal +{}; + +template +struct relate + : detail::relate::areal_linear +{}; + + +template +struct relate + : detail::relate::areal_areal +{}; + + +}} // namespace detail_dispatch::relate +#endif // DOXYGEN_NO_DISPATCH + +namespace detail { namespace relate { + +template +struct interruption_enabled +{ + static const bool value = + detail_dispatch::relate::relate::interruption_enabled; +}; + +template ::value> +struct result_handler_type + : not_implemented +{}; + +template +struct result_handler_type +{ + typedef matrix_handler type; +}; + +template +struct result_handler_type +{ + typedef mask_handler + < + mask9, + interruption_enabled + < + Geometry1, + Geometry2 + >::value + > type; +}; + +template +struct result_handler_type, false> +{ + typedef mask_handler + < + boost::tuples::cons, + interruption_enabled + < + Geometry1, + Geometry2 + >::value + > type; +}; + +template +struct result_handler_type, false> +{ + typedef static_mask_handler + < + static_mask, + interruption_enabled + < + Geometry1, + Geometry2 + >::value + > type; +}; + +template +struct result_handler_type +{ + typedef static_mask_handler + < + StaticSequence, + interruption_enabled + < + Geometry1, + Geometry2 + >::value + > type; +}; + +template +inline +typename result_handler_type + < + Geometry1, + Geometry2, + MatrixOrMask + >::type::result_type +relate(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MatrixOrMask const& matrix_or_mask = MatrixOrMask()) +{ + typedef typename result_handler_type + < + Geometry1, + Geometry2, + MatrixOrMask + >::type handler_type; + + handler_type handler(matrix_or_mask); + detail_dispatch::relate::relate::apply(geometry1, geometry2, handler); + return handler.result(); +} + +struct implemented_tag {}; + +template