diff --git a/circle.yml b/circle.yml index 19b083f81..20ec47d86 100644 --- a/circle.yml +++ b/circle.yml @@ -16,6 +16,8 @@ machine: environment: # define tests list, if parallelism is enabled they are run in parallel TESTS: test index/test + TESTS0: test/core test/concepts test/geometries test/arithmetic test/algorithms test/formulas test/iterators + TESTS1: test/strategies test/policies test/io test/util test/views index/test # this is not fully bulletproof, ideally one should check # if the current branch originates in master or develop @@ -109,7 +111,11 @@ test: #- cd $BOOST_ROOT/libs/geometry && index=0 ; run_tests() { for i in "$1"/* ; do if [ -f "$i"/Jamfile* ] ; then ((index++)) ; echo "$index - $i" ; ../../b2 cxxflags="--coverage" linkflags="--coverage" "$i" ; run_tests "$i" ; fi ; done } ; run_tests test # so for now just run the tests from the list - - cd $BOOST_ROOT/libs/geometry && status=0; index=0 ; for t in ${TESTS[@]} ; do if [ $(((index++)%CIRCLE_NODE_TOTAL)) == $CIRCLE_NODE_INDEX ] ; then ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t ; fi || status=$? ; done ; exit $status : + #- cd $BOOST_ROOT/libs/geometry && status=0; index=0 ; for t in ${TESTS[@]} ; do if [ $(((index++)%CIRCLE_NODE_TOTAL)) == $CIRCLE_NODE_INDEX ] ; then ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t ; fi || status=$? ; done ; exit $status : + # parallel: true + + # so for now just run the tests from 2 lists + - cd $BOOST_ROOT/libs/geometry && status=0; case $CIRCLE_NODE_INDEX in 0) for t in ${TESTS0[@]} ; do ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t || status=$? ; done ;; 1) for t in ${TESTS1[@]} ; do ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t || status=$? ; done ;; esac ; exit $status : parallel: true post: diff --git a/doc/design_rationale.qbk b/doc/design_rationale.qbk index 301a9ee81..93db7584d 100644 --- a/doc/design_rationale.qbk +++ b/doc/design_rationale.qbk @@ -546,7 +546,7 @@ where a strategy is specified explicitly and constructed with a radius. [heading Point Concept] The five traits classes mentioned in the previous sections form together the Point Concept. Any point type for which specializations are implemented in -the traits namespace should be accepted a as valid type. So the Point Concept +the traits namespace should be accepted as a valid type. So the Point Concept consists of: * a specialization for `traits::tag` @@ -637,6 +637,20 @@ and modify our distance function: Of course also the apply functions in the dispatch specializations will return a result like this. They have a strategy as a template parameter everywhere, making the less verbose version possible. +[heading Axis Order] + +This is an important note for users who develop GIS applications, require compliance with OGC +standards or use variety of coordinate reference systems (CRS). + +Boost.Geometry convention is that coordinate values of 2D tuple is always given according to +mathematical axis order: X,Y. Considering GIS applications, that always means easting,northing or, +in terms of geographic coordinate system: longitude,latitude. +Boost.Geometry does not allow or respect coordinate values listed in the axis order as +specified by coordinate reference system (CRS). + +In practice, users may easily adapt point types with alternate axis ordering and +specify coordinate access in order as expected by Boost.Geometry. + [heading Summary] In this design rationale, __boost_geometry__ is step by step designed using tag dispatching, diff --git a/doc/index/rtree/query.qbk b/doc/index/rtree/query.qbk index 75f40ec29..a2fa49594 100644 --- a/doc/index/rtree/query.qbk +++ b/doc/index/rtree/query.qbk @@ -69,14 +69,19 @@ Examples of some basic queries may be found in the tables below. The query regio ] [table -[[intersects(Ring)] [intersects(Polygon)] [intersects(MultiPolygon)] [intersects(Segment)] [intersects(Linestring)]] -[[[$img/index/rtree/intersects_ring.png]] [[$img/index/rtree/intersects_poly.png]] [[$img/index/rtree/intersects_mpoly.png]] [[$img/index/rtree/intersects_segment.png]] [[$img/index/rtree/intersects_linestring.png]]] +[[intersects(Segment)] [intersects(Box)] [disjoint(Box)] [intersects(Box)] [disjoint(Box)]] +[[[$img/index/rtree/intersects_segment.png]] [[$img/index/rtree/rtree_pt_intersects_box.png]] [[$img/index/rtree/rtree_pt_disjoint_box.png]] [[$img/index/rtree/rtree_seg_intersects_box.png]] [[$img/index/rtree/rtree_seg_disjoint_box.png]]] ] -[table +[/table +[[intersects(Ring)] [intersects(Polygon)] [intersects(MultiPolygon)] [intersects(Segment)] [intersects(Linestring)]] +[[[$img/index/rtree/intersects_ring.png]] [[$img/index/rtree/intersects_poly.png]] [[$img/index/rtree/intersects_mpoly.png]] [[$img/index/rtree/intersects_segment.png]] [[$img/index/rtree/intersects_linestring.png]]] +/] + +[/table [[intersects(Box)] [disjoint(Box)] [intersects(Box)] [disjoint(Box)]] [[[$img/index/rtree/rtree_pt_intersects_box.png]] [[$img/index/rtree/rtree_pt_disjoint_box.png]] [[$img/index/rtree/rtree_seg_intersects_box.png]] [[$img/index/rtree/rtree_seg_disjoint_box.png]]] -] +/] Spatial predicates are generated by functions defined in `boost::geometry::index` namespace. diff --git a/doc/reference.qbk b/doc/reference.qbk index e84115ece..699712399 100644 --- a/doc/reference.qbk +++ b/doc/reference.qbk @@ -89,7 +89,10 @@ [include generated/clear.qbk] [include generated/convert.qbk] [include generated/convex_hull.qbk] + +[section:correct correct] [include generated/correct.qbk] +[endsect] [section:covered_by covered_by] [include generated/covered_by.qbk] diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 61ba87250..921360512 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -18,20 +18,48 @@ [section:release_notes Release Notes] +[/=================] +[heading Boost 1.66] +[/=================] + +[*Improvements] + +[*Solved issues] + +* [@https://svn.boost.org/trac10/ticket/12503 12503] Validity of complex polygon + +[*Bugfixes] + +* Fixes in validity of union/intersection/differece which were sometimes invalid. In most cases, results are valid now. +* Fixes in results of union/intersection/differece which could be incorrect in very complex cases + [/=================] [heading Boost 1.65] [/=================] +[*Improvements] + +* Add correct() overload taking area strategy +* Add is_convex() overload taking side strategy +* Add missing relational operations (covered_by, crosses, equals, etc.) for MultiPoint/AnyGeometry + [*Solved issues] * [@https://svn.boost.org/trac/boost/ticket/12410 12410] Lack of support of geographic coordinate system in correct() +* [@https://svn.boost.org/trac/boost/ticket/13035 13035] Wrong result of non-cartesian intersection strategies for segments near poles. +* [@https://svn.boost.org/trac/boost/ticket/13057 13057] Wrong result of intersects() for linestrings caused by numerical issue in disjoint() for boxes. [*Bugfixes] * Fix is_valid which could return false for multipolygons where an interior ring touches another interior ring -* Fix union which could produce invalid results (for some cases, this needs to define BOOST_GEOMETRY_INCLUDE_SELF_TURNS +* Fix is_valid which could return false for multipolygons where a polygon was located in an interior ring, all touching each other +* Fix union which could produce invalid results (for some cases, this needs to define BOOST_GEOMETRY_INCLUDE_SELF_TURNS) * Fix intersection (idem), but some cases are still not yet valid * Fix difference (idem), but some cases are still not yet valid +* Fix propagation of area strategy into the internals of various algorithms from intersection strategy +* Fix uninitialized variable in relate and reference to temporary in overlay +* Fix error in disjoint for geographic Segment/Box +* Fix handling of non-cartesian geometries in various algorithms [/=================] [heading Boost 1.64] diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 46163e0c4..c54f298fa 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -1,7 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2010-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2010-2017 Barend Gehrels, Amsterdam, the Netherlands. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -16,8 +16,10 @@ #include // To check results +#include #include #include +#include #include #include @@ -38,40 +40,280 @@ #if defined(TEST_WITH_SVG) # include # include +# include #endif -template -struct map_segment + +namespace { - map_segment(Mapper& m) - : m_mapper(&m) + // Simplex + std::string const dissolve_1 = "POLYGON((0 0,0 4,1.5 2.5,2.5 1.5,4 0,0 0))"; + + // Self intersecting + std::string const dissolve_2 = "POLYGON((1 2,1 1,2 1,2 2.25,3 2.25,3 0,0 0,0 3,3 3,2.75 2,1 2))"; + + // Self intersecting in last segment + std::string const dissolve_3 = "POLYGON((0 2,2 4,2 0,4 2,0 2))"; + + // Self tangent - polygons are now included twice + std::string const dissolve_4 = "POLYGON((0 0,0 4,4 4,4 0,2 4,0 0))"; + + // Self tangent in corner - polygons are now included twice + std::string const dissolve_5 = "POLYGON((0 0,0 4,4 4,4 0,0 4,2 0,0 0))"; + + // With spike + std::string const dissolve_6 = "POLYGON((0 0,0 4,4 4,4 2,6 2,4 2,4 0,0 0))"; + + // Many intersections + std::string const dissolve_7 = "POLYGON((1 3,0 9,9 5,1 7,9 8,2 5,10 10,9 2,1 3))"; + + // Pentagram + std::string const dissolve_8 = "POLYGON((5 0,2.5 9,9.5 3.5,0.5 3.5,7.5 9,5 0))"; + // CCW pentagram + std::string const dissolve_9 = "POLYGON((5 0,7.5 9,0.5 3.5,9.5 3.5,2.5 9,5 0))"; + // CW, one keyhole + std::string const dissolve_10 = "POLYGON((2 8,8 8,8 0,0 0,0 6,4 6,4 4,2 4,2 8))"; + // CCW, one keyhole + std::string const dissolve_11 = "POLYGON((2 8,2 4,4 4,4 6,0 6,0 0,8 0,8 8,2 8))"; + + // More pentagrams + // Source: http://upload.wikimedia.org/wikipedia/commons/8/83/Acute_heptagram.svg + std::string const dissolve_12 = "POLYGON((409 5,229 793.631528,733.348792 161.198146,4.543671 512.172194,813.456329 512.172194,84.651208 161.198146,589 793.631528))"; + // Source: http://upload.wikimedia.org/wikipedia/commons/a/a7/Obtuse_heptagram.svg + std::string const dissolve_13 = "POLYGON((409 5,813.456329 512.172194,229 793.631528,84.651208 161.198146,733.348792 161.198146,589 793.631528,4.543671 512.172194))"; + + std::string const dissolve_14 = "POLYGON((0 0,0 2,2 0,4 2,4 0,2 2,0 0))"; + std::string const dissolve_15 = "POLYGON((0 0,2 2,4 0,4 2,2 0,0 2,0 0))"; + + std::string const dissolve_16 = "POLYGON((1 3,4 5,7 3,4 1,1 3),(2 2,4 4,6 2,6 4,4 2,2 4,2 2))"; + + // Non intersection, but with duplicate + std::string const dissolve_d1 = "POLYGON((0 0,0 4,4 0,4 0,0 0))"; + + // With many duplicate points + std::string const dissolve_d2 = "POLYGON((0 0,0 1,0 1,0 1,0 2,0 2,0 3,0 3,0 3,0 3,0 4,2 4,2 4,4 4,4 0,4 0,3 0,3 0,3 0,3 0,3 0,0 0))"; + + // Case with touching reversed interior ring inside, which should be removed + std::string const dissolve_h1_a = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 2,2 4,3 2,1 2))"; + + // Case with correct interior ring inside, which should be stay + std::string const dissolve_h1_b = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 2,3 2,2 4,1 2))"; + + // Hole: interior intersecting exterior + std::string const dissolve_h2 = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 3,5 4,1 1))"; + + // Hole: two intersecting holes + std::string const dissolve_h3 = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 3,3 3,3 1,1 1),(2 2,2 3.5,3.5 3.5,3.5 2,2 2))"; + + // Hole: self-intersecting hole + std::string const dissolve_h4 = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,3 3,3 2.5,1 3.5,1.5 3.5,1 1))"; + + std::string const multi_three_triangles = "MULTIPOLYGON(((1 1,5 5,8 0,1 1)),((4 2,0 8,5 9,4 2)),((5 3,4 8,10 4,5 3)))"; + std::string const multi_simplex_two = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)))"; + std::string const multi_simplex_three = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)))"; + std::string const multi_simplex_four = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((5 5,7 7,8 4,5 5)))"; + std::string const multi_disjoint = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((1 6,2 10,5 7,1 6)),((3 4,5 6,6 2,3 4)),((6 5,8 7,9 4,6 5)))"; + std::string const multi_new_interior = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((3 1,5 4,8 4,3 1)))"; + + // Testcases sent on GGL mailing list - report Javier - 2011, March 7 + std::string const ggl_list_20110307_javier_01_a = "MULTIPOLYGON(((560 -400, 600 -400, 600 -440, 560 -440, 560 -400)), ((480 -400, 520 -400, 520 -440, 480 -440, 480 -400)), ((600 -320, 640 -320, 640 -360, 600 -360, 600 -320)), ((520 -400, 560 -400, 560 -440, 520 -440, 520 -400)))"; + std::string const ggl_list_20110307_javier_01_b = "POLYGON((0 0, 2000 0, 2000 -2000, 0 -2000, 0 0), (560 -400, 560 -440, 600 -440, 600 -400, 560 -400), (480 -400, 480 -440, 520 -440, 520 -400, 480 -400), (600 -320, 600 -360, 640 -360, 640 -320, 600 -320), (520 -400, 520 -440, 560 -440, 560 -400, 520 -400))"; + + std::string const ggl_list_denis = "POLYGON((55 10, 141 237, 249 23, 21 171, 252 169, 24 89, 266 73, 55 10))"; + + // Testcases sent by Johan Doré at September 24, 2017: + std::string const dissolve_mail_2017_09_24_a = "POLYGON((0 1, 1 0, 1 1, 0 0, 0 1))"; // two triangles + std::string const dissolve_mail_2017_09_24_b = "POLYGON((1 0, 0 0, 0 4, 4 4, 4 0))"; // input is not closed + std::string const dissolve_mail_2017_09_24_c = "POLYGON((0 0, 1 0, 0 -1, 0.0001 1))"; // spike and not closed + std::string const dissolve_mail_2017_09_24_d = "POLYGON((0 0, 1 0, 0 -1, 0 1))"; // spike and not closed + + // Testcases sent by Artem Pavlenko via gitter + // https://gitter.im/boostorg/geometry?at=58ef46408e4b63533dc49b48 + std::string const gitter_2013_04_a = "POLYGON((36.9121 2.03883,26.2052 54.353,60.0781 64.2202,96.2171 55.9826,71.1506 39.8365,5.72552 94.1523,4.06819 13.9054,59.7155 44.5877,60.9243 16.4597,48.8696 93.039,36.9121 2.03883))"; + std::string const gitter_2013_04_b = "POLYGON((337 176,602 377,294 372,581 166,453 449,337 176))"; + + // Large polygon with self-intersections + std::string const dissolve_mail_2017_09_24_e = "POLYGON((25.21475410461420 -1.19600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.21475410461420 -1.19600892066955))"; + + // Idem but moving closing point eastwards to view self-intersections better + std::string const dissolve_mail_2017_09_24_f = "POLYGON((25.35475410461420 -1.20600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.35475410461420 -1.20600892066955))"; + + // As d but with other spike + std::string const dissolve_mail_2017_09_24_g = "POLYGON((1 0, 0 -1, 0 1, 0 0,1 0))"; + std::string const dissolve_mail_2017_09_24_h = "POLYGON((0 0, 0 1, 2 1, 1 1,0 0))"; + + std::string const dissolve_ticket17 = "POLYGON ((-122.28139163 37.37319149,-122.28100699 37.37273669,-122.28002186 37.37303123,-122.27979681 37.37290072,-122.28007349 37.37240493,-122.27977334 37.37220360,-122.27819720 37.37288580,-122.27714184 37.37275161,-122.27678628 37.37253167,-122.27766437 37.37180973,-122.27804382 37.37121453,-122.27687664 37.37101354,-122.27645829 37.37203386,-122.27604423 37.37249110,-122.27632234 37.37343339,-122.27760980 37.37391082,-122.27812478 37.37800320,-122.26117222 37.39121007,-122.25572289 37.39566631,-122.25547269 37.39564971,-122.25366304 37.39552993,-122.24919976 37.39580268,-122.24417933 37.39366907,-122.24051443 37.39094143,-122.23246277 37.38100418,-122.23606766 37.38141338,-122.24001587 37.37738940,-122.23666848 37.37609347,-122.23057450 37.37882170,-122.22679803 37.37807143,-122.22525727 37.37448817,-122.22523229 37.37443000,-122.23083199 37.37609347,-122.23033486 37.37777891,-122.23169030 37.37732117,-122.23229178 37.37709687,-122.23237761 37.37631249,-122.23297776 37.37438834,-122.23872850 37.37165986,-122.24044511 37.36934068,-122.24671067 37.36865847,-122.24825570 37.36981819,-122.25151719 37.36947713,-122.25357721 37.36756706,-122.26001451 37.36579354,-122.25615213 37.36545239,-122.25486458 37.36245083,-122.25357721 37.36108651,-122.25194642 37.36013139,-122.24885652 37.35958557,-122.24911401 37.35849399,-122.25357721 37.35808470,-122.25675286 37.35897159,-122.25855539 37.35753887,-122.26181687 37.35828939,-122.26713837 37.35897159,-122.26782510 37.36108651,-122.26662339 37.36456559,-122.27288911 37.36722601,-122.27366159 37.36531602,-122.27168740 37.36470213,-122.27391900 37.36374701,-122.27074326 37.36245083,-122.27134408 37.35951742,-122.27426240 37.36135926,-122.27709482 37.36115474,-122.27966974 37.36231438,-122.27958391 37.36463382,-122.27572152 37.36463382,-122.27563569 37.36524779,-122.27700899 37.36593000,-122.27709482 37.36763529,-122.27554978 37.36838573,-122.27667254 37.36931478,-122.27677932 37.36932073,-122.27769362 37.36853987,-122.27942490 37.36830803,-122.28178776 37.36677917,-122.28509559 37.36443500,-122.28845129 37.36413744,-122.29194403 37.36695946,-122.29382577 37.36726817,-122.29600414 37.36898512,-122.29733083 37.36995398,-122.29593239 37.37141436,-122.29416649 37.37075898,-122.29325026 37.37108436,-122.29652910 37.37311697,-122.29584237 37.37374461,-122.29537583 37.37573372,-122.29487677 37.37752502,-122.30923212 37.37593011,-122.31122484 37.38230086,-122.31467994 37.38092472,-122.31715663 37.38252181,-122.32307970 37.38166978,-122.31985618 37.37667694,-122.32210304 37.37580220,-122.32581446 37.37589532,-122.32401730 37.37331839,-122.32960417 37.37189020,-122.33465527 37.37331906,-122.33425328 37.37623680,-122.33620676 37.37726132,-122.33397986 37.37822382,-122.33358918 37.38036590,-122.33202637 37.37986918,-122.33147954 37.38101784,-122.33394080 37.38198017,-122.33545239 37.38587943,-122.33478058 37.38785697,-122.33386050 37.38723721,-122.33350041 37.38571137,-122.33122003 37.38548891,-122.33140008 37.38650606,-122.33366042 37.38817490,-122.33244019 37.39157602,-122.33298157 37.39419201,-122.33164013 37.39477028,-122.33202017 37.39518351,-122.33358038 37.39499282,-122.33376050 37.39597811,-122.33550067 37.39734478,-122.33556069 37.39481797,-122.33344040 37.39292676,-122.33638094 37.38892189,-122.34240644 37.38852719,-122.34906293 37.38726898,-122.35072321 37.39338769,-122.34910291 37.39445252,-122.34796272 37.39410291,-122.34449043 37.39640534,-122.34500223 37.39729709,-122.34936291 37.39670910,-122.35098322 37.39531066,-122.35364623 37.39554510,-122.35434369 37.39612111,-122.35798429 37.39600988,-122.35768430 37.39478621,-122.36334519 37.39206871,-122.36604726 37.39203267,-122.36778592 37.39335592,-122.36518870 37.40022011,-122.36554552 37.40247752,-122.36370519 37.40331974,-122.36270506 37.40530591,-122.36320512 37.40670418,-122.36149849 37.40851392,-122.36730580 37.41054938,-122.37263720 37.41378932,-122.37161871 37.42076600,-122.36566153 37.42006292,-122.36520547 37.42742106,-122.37165953 37.43661157,-122.35943972 37.44459022,-122.35356359 37.44600810,-122.33792254 37.45796329,-122.35228518 37.47478091,-122.35127080 37.48181199,-122.34867342 37.48487322,-122.34359717 37.48801082,-122.33388431 37.48677650,-122.33142321 37.48429747,-122.32929580 37.48473149,-122.32609609 37.48291144,-122.32344850 37.48228229,-122.31924364 37.48410234,-122.31677299 37.48114051,-122.31431751 37.47848973,-122.31259201 37.47682190,-122.31515972 37.47568196,-122.31691389 37.47360309,-122.31292494 37.46960081,-122.31130153 37.46937743,-122.30889894 37.47124987,-122.30612839 37.47011613,-122.30149630 37.46568378,-122.30064277 37.46363784,-122.29283821 37.45922376,-122.28630141 37.45415497,-122.28883099 37.44629920,-122.28316717 37.44197138,-122.27554148 37.42297597,-122.25597410 37.40553692,-122.25196579 37.40129593,-122.25012043 37.40049143,-122.24823207 37.39897758,-122.24754551 37.39740941,-122.24778582 37.39621607,-122.24934787 37.39599102,-122.25005170 37.39871849,-122.25222328 37.39863668,-122.25342491 37.39737529,-122.25520162 37.39667289,-122.25528737 37.39522726,-122.27747460 37.37809616,-122.27977493 37.37858717,-122.28157729 37.37920106,-122.28322534 37.37952846,-122.28416939 37.38092656,-122.28621223 37.37984219,-122.28638389 37.37613857,-122.28382607 37.37843722,-122.27930278 37.37718220,-122.28196361 37.37652740,-122.28295058 37.37568167,-122.28216101 37.37523148,-122.28114822 37.37543608,-122.27934569 37.37528613,-122.27996369 37.37448121,-122.28104521 37.37454944,-122.28185197 37.37422883,-122.28290767 37.37474038,-122.28376597 37.37467224,-122.28428104 37.37399012,-122.28402346 37.37338989,-122.28610922 37.37364914,-122.28651264 37.37327388,-122.28672722 37.37207343,-122.28628398 37.37205448,-122.28574460 37.37166682,-122.28479711 37.37200981,-122.28327731 37.37137228,-122.28285511 37.37100700,-122.28279409 37.37125669,-122.28315527 37.37173756,-122.28321872 37.37220569,-122.28187007 37.37231918,-122.28193109 37.37294908,-122.28139163 37.37319149))"; + std::string const dissolve_reallife = "POLYGON((170718 605997,170718 605997,170776 606016,170773 606015,170786 606020,170778 606016,170787 606021,170781 606017,170795 606028,170795 606028,170829 606055,170939 606140,170933 605968,170933 605968,170932 605908,170929 605834,170920 605866,170961 605803,170739 605684,170699 605749,170691 605766,170693 605762,170686 605775,170688 605771,170673 605794,170676 605790,170668 605800,170672 605796,170651 605818,170653 605816,170639 605829,170568 605899,170662 605943,170633 605875,170603 605961,170718 605997))"; + + // https://svn.boost.org/trac/boost/ticket/10713 + std::string const dissolve_ticket10713 = "POLYGON((-0.7189743518829346 4.1308121681213379, 0.0831791982054710 4.1034231185913086, 0.1004156470298767 4.1107301712036133, 0.1044322624802589 4.1026973724365234, 0.0831791982054710 4.1034231185913086, -0.7711903452873230 3.7412264347076416, -0.7189743518829346 4.1308121681213379))"; +} + + +#if defined(TEST_WITH_SVG) +template +struct map_visitor +{ + map_visitor(Mapper& mapper) + : m_mapper(mapper) {} - map_segment& operator=(map_segment const& other) + void print(char const* header) + {} + + template + void print(char const* header, Turns const& turns, int turn_index) { - if(this != &other) + std::string style = "fill:rgb(0,0,0);font-family:Arial;font-size:6px"; + stream(turns, turns[turn_index], turns[turn_index].operations[0], header, style); + } + + template + void print(char const* header, Turns const& turns, int turn_index, int op_index) + { + std::string style = "fill:rgb(0,0,0);font-family:Arial;font-size:6px"; + stream(turns, turns[turn_index], turns[turn_index].operations[op_index], header, style); + } + + template + void visit_turns(int phase, Turns const& turns) + { + typedef typename boost::range_value::type turn_type; + std:size_t index = 0; + BOOST_FOREACH(turn_type const& turn, turns) { - this->m_mapper = other.m_mapper; + switch (phase) + { + case 1 : // after self_turns + m_mapper.map(turn.point, "fill:rgb(255,128,0);" + "stroke:rgb(0,0,0);stroke-width:1", 4); + break; + case 3 : // after enrich/traverse for union + label_turn(index, turn, -5, "fill:rgb(0,0,128);"); + break; + case 5 : // after enrich/traverse for intersection + label_turn(index, turn, 5, "fill:rgb(0,0,0);"); + break; + } + index++; } - return *this; } + template + void visit_clusters(Clusters const& , Turns const& ) {} - template - inline void operator()(Segment const& s) + template + void visit_traverse(Turns const& , Turn const& , Operation const& , char const*) + {} + + template + void visit_traverse_reject(Turns const& , Turn const& , Operation const& , + bg::detail::overlay::traverse_error_type ) + {} + +private : + + template + bool label_operation(Turn const& turn, std::size_t index, std::ostream& os) { - // create a little offset - m_mapper->map(s, "opacity:0.6;fill:none;stroke:rgb(0,0,0);stroke-width:2"); + os << bg::operation_char(turn.operations[index].operation); + bool result = false; + if (! turn.discarded) + { + if (turn.operations[index].enriched.next_ip_index != -1) + { + os << "->" << turn.operations[index].enriched.next_ip_index; + if (turn.operations[index].enriched.next_ip_index != -1) + { + result = true; + } + } + else + { + os << "->" << turn.operations[index].enriched.travels_to_ip_index; + if (turn.operations[index].enriched.travels_to_ip_index != -1) + { + result = true; + } + } + } + + return result; } - Mapper* m_mapper; + template + void label_turn(std::size_t index, Turn const& turn, int y_offset, std::string const& color) + { + std::ostringstream out; + out << index << " "; + if (turn.cluster_id != -1) + { + out << " c=" << turn.cluster_id << " "; + } + bool lab1 = label_operation(turn, 0, out); + out << " / "; + bool lab2 = label_operation(turn, 1, out); + if (turn.switch_source) + { + out << "#"; + } + if (turn.discarded) + { + out << "!"; + } + + std::string font8 = "font-family:Arial;font-size:6px"; + std::string font6 = "font-family:Arial;font-size:4px"; + std::string style = color + ";" + font8; + if (turn.discarded) + { + style = "fill:rgb(92,92,92);" + font6; + } + else if (turn.cluster_id != -1) + { + style = color + ";" + font8; + } + else if (! lab1 || ! lab2) + { + style = color + ";" + font6; + } + + m_mapper.text(turn.point, out.str(), style, 5, y_offset, 6); + } + + Mapper& m_mapper; }; +#endif + +//! Unittest settings: validity, tolerance +struct ut_settings +{ + double percentage; + bool test_validity; + + explicit ut_settings(double p = 0.001, bool tv = true) + : percentage(p) + , test_validity(tv) + {} + +}; + +template +std::string as_wkt(Geometry const& geometry) +{ + std::ostringstream out; + out << bg::wkt(geometry); + return out.str(); +} template void test_dissolve(std::string const& caseid, Geometry const& geometry, - std::size_t /*expected_hole_count*/, std::size_t expected_point_count, - double expected_length_or_area, double percentage) + double expected_area, + std::size_t expected_clip_count, + std::size_t expected_hole_count, + std::size_t expected_point_count, + ut_settings const& settings) { typedef typename bg::coordinate_type::type coordinate_type; @@ -79,21 +321,111 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, //std::cout << bg::area(geometry) << std::endl; - std::vector dissolved_vector; - bg::dissolve_inserter(geometry, std::back_inserter(dissolved_vector)); + std::vector dissolved1; - typename bg::default_area_result::type length_or_area = 0; - //std::size_t holes = 0; - std::size_t count = 0; + // Check dispatch::dissolve + { + typedef typename bg::strategy::intersection::services::default_strategy + < + typename bg::cs_tag::type + >::type strategy_type; - BOOST_FOREACH(GeometryOut& dissolved, dissolved_vector) + typedef typename bg::rescale_policy_type + < + typename bg::point_type::type + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = bg::get_rescale_policy(geometry); + + // This will optionally also create SVG with turn-debug information + strategy_type strategy; + + +#if ! defined(TEST_WITH_SVG) + bg::detail::overlay::overlay_null_visitor visitor; +#else + std::ostringstream filename; + filename << "dissolve_" << caseid << "_" + << string_from_type::name() + << ".svg"; + + std::ofstream svg(filename.str().c_str()); + + typedef bg::svg_mapper + < + typename bg::point_type::type + > mapper_type; + + mapper_type mapper(svg, 500, 500); + mapper.add(geometry); + + mapper.map(geometry, "fill-opacity:0.5;fill:rgb(153,204,0);" + "stroke:rgb(153,204,0);stroke-width:3;fill-rule:nonzero"); + + map_visitor visitor(mapper); +#endif + + bg::dispatch::dissolve + < + typename bg::tag::type, + typename bg::tag::type, + Geometry, + GeometryOut + >::apply(geometry, robust_policy, std::back_inserter(dissolved1), + strategy, visitor); + +#if defined(TEST_WITH_SVG) + BOOST_FOREACH(GeometryOut& dissolved, dissolved1) + { + mapper.map(dissolved, "fill:none;stroke-opacity:0.4;stroke:rgb(255,0,255);stroke-width:8;"); + } +#endif + + } + + // Check dissolve_inserter + std::vector dissolved2; + bg::dissolve_inserter(geometry, std::back_inserter(dissolved2)); + + // Check dissolve and validity, assuming GeometryOut is a single polygon + typedef bg::model::multi_polygon multi_polygon; + multi_polygon dissolved3; + bg::dissolve(geometry, dissolved3); + + if (settings.test_validity) + { + std::string message; + bool const valid = bg::is_valid(dissolved3, message); + BOOST_CHECK_MESSAGE(valid, + "dissolve: " << caseid + << " geometry is not valid: " << message); + } + + // Make output unique (TODO: this should probably be moved to dissolve itself) + BOOST_FOREACH(GeometryOut& dissolved, dissolved1) { bg::unique(dissolved); + } + BOOST_FOREACH(GeometryOut& dissolved, dissolved2) + { + bg::unique(dissolved); + } + BOOST_FOREACH(GeometryOut& dissolved, dissolved3) + { + bg::unique(dissolved); + } + typename bg::default_area_result::type length_or_area = 0; + std::size_t holes = 0; + std::size_t count = 0; + + BOOST_FOREACH(GeometryOut& dissolved, dissolved2) + { length_or_area += is_line ? bg::length(dissolved) : bg::area(dissolved); - //holes += bg::num_interior_rings(dissolved); + holes += bg::num_interior_rings(dissolved); count += bg::num_points(dissolved); } @@ -104,61 +436,52 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, << " type: " << string_from_type::name() ); + BOOST_CHECK_MESSAGE(dissolved1.size() == expected_clip_count, + "dissolve: " << caseid + << " #clips expected: " << expected_clip_count + << " detected: " << dissolved1.size() + << " type: " << string_from_type::name() + ); - //BOOST_CHECK_EQUAL(holes, expected_hole_count); - BOOST_CHECK_CLOSE(length_or_area, expected_length_or_area, percentage); + BOOST_CHECK_EQUAL(holes, expected_hole_count); + BOOST_CHECK_CLOSE(length_or_area, expected_area, settings.percentage); - // Compile check, it should also compile inplace, outputting to the same geometry + BOOST_CHECK_EQUAL(dissolved1.size(), dissolved2.size()); + BOOST_CHECK_EQUAL(dissolved1.size(), dissolved3.size()); + if (dissolved1.size() == dissolved2.size() + && dissolved1.size() == dissolved3.size()) { - std::vector dissolved; - bg::dissolve(geometry, dissolved); - } - - -#if defined(TEST_WITH_SVG) - { - std::ostringstream filename; - filename << "dissolve_" - << caseid << "_" - << string_from_type::name() - << ".svg"; - - std::ofstream svg(filename.str().c_str()); - - typedef - bg::svg_mapper - < - typename bg::point_type::type - > mapper_type; - - mapper_type mapper(svg, 500, 500); - mapper.add(geometry); - - mapper.map(geometry, "opacity:0.6;fill:rgb(0,0,255);stroke:rgb(0,0,0);stroke-width:1;fill-rule:nonzero"); - - bg::for_each_segment(geometry, map_segment(mapper)); - - - BOOST_FOREACH(GeometryOut& dissolved, dissolved_vector) + for (std::size_t i = 0; i < dissolved1.size(); i++) { - mapper.map(dissolved, "opacity:0.6;fill:none;stroke:rgb(255,0,0);stroke-width:5"); + std::string const wkt1 = as_wkt(dissolved1[i]); + std::string const wkt2 = as_wkt(dissolved2[i]); + std::string const wkt3 = as_wkt(dissolved3[i]); + BOOST_CHECK_MESSAGE(wkt1 == wkt2, caseid << " : output differs: " << wkt1 << " VERSUS " << wkt2); + BOOST_CHECK_MESSAGE(wkt1 == wkt3, caseid << " : output differs: " << wkt1 << " VERSUS " << wkt3); } } -#endif } template void test_one(std::string const& caseid, std::string const& wkt, - std::size_t expected_hole_count, std::size_t expected_point_count, - double expected_length_or_area, double percentage = 0.001) + double expected_area, + std::size_t expected_clip_count, + std::size_t expected_hole_count, + std::size_t expected_point_count, + ut_settings const& settings) { Geometry geometry; bg::read_wkt(wkt, geometry); + // If defined as closed, it should be closed. The algorithm itself + // cannot close it without making a copy. + bg::correct_closure(geometry); + test_dissolve(caseid, geometry, - expected_hole_count, expected_point_count, - expected_length_or_area, percentage); + expected_area, + expected_clip_count, expected_hole_count, expected_point_count, + settings); #ifdef BOOST_GEOMETRY_TEST_MULTI_PERMUTATIONS // Test different combinations of a multi-polygon @@ -183,195 +506,101 @@ void test_one(std::string const& caseid, std::string const& wkt, out << "_" << index; geometry2.push_back(geometry[index]); } - test_dissolve(out.str(), geometry2, expected_hole_count, - expected_point_count, expected_length_or_area, percentage); + test_dissolve(out.str(), geometry2, expected_area, + expected_clip_count, expected_hole_count, expected_point_count, settings); } while (std::next_permutation(indices.begin(), indices.end())); #endif } +#define TEST_DISSOLVE(caseid, area, clips, holes, points) { \ + ut_settings settings; \ + (test_one) ( #caseid, caseid, area, clips, holes, points, settings); } +#define TEST_DISSOLVE_IGNORE(caseid, area, clips, holes, points) { \ + ut_settings settings; settings.test_validity = false; \ + (test_one) ( #caseid, caseid, area, clips, holes, points, settings); } +#define TEST_MULTI(caseid, area, clips, holes, points) { \ + ut_settings settings; \ + (test_one) ( #caseid, caseid, area, clips, holes, points, settings); } template void test_all() { typedef bg::model::ring

ring; typedef bg::model::polygon

polygon; + typedef bg::model::multi_polygon multi_polygon; - // Simplex - test_one("1", - "POLYGON((0 0,0 4,1.5 2.5,2.5 1.5,4 0,0 0))", - 0, 6, 8); + TEST_DISSOLVE(dissolve_1, 8.0, 1, 0, 6); + TEST_DISSOLVE(dissolve_2, 7.9296875, 1, 1, 12); + TEST_DISSOLVE(dissolve_3, 4.0, 2, 0, 8); + TEST_DISSOLVE(dissolve_4, 8.0, 2, 0, 8); + TEST_DISSOLVE(dissolve_5, 12.0, 2, 0, 8); + TEST_DISSOLVE(dissolve_6, 16.0, 1, 0, 6); - // Self intersecting - test_one("2", - "POLYGON((1 2,1 1,2 1,2 2.25,3 2.25,3 0,0 0,0 3,3 3,2.75 2,1 2))", - 1, 12, 7.9296875); - -#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - // Self intersecting in last segment - test_one("3", - "POLYGON((0 2,2 4,2 0,4 2,0 2))", - 0, 8, 4.0); - - // Self tangent - polygons are now included twice - test_one("4", - "POLYGON((0 0,0 4,4 4,4 0,2 4,0 0))", - 0, 8, 8.0); - - - // Self tangent in corner - polygons are now included twice - test_one("5", - "POLYGON((0 0,0 4,4 4,4 0,0 4,2 0,0 0))", - 0, 8, 12.0); -#endif - - - // With spike - test_one("6", - "POLYGON((0 0,0 4,4 4,4 2,6 2,4 2,4 0,0 0))", - 0, 6, 16); - - - // Non intersection, but with duplicate - test_one("d1", - "POLYGON((0 0,0 4,4 0,4 0,0 0))", - 0, 4, 8); - - - // With many duplicates - test_one("d2", - "POLYGON((0 0,0 1,0 1,0 1,0 2,0 2,0 3,0 3,0 3,0 3,0 4,2 4,2 4,4 4,4 0,4 0,3 0,3 0,3 0,3 0,3 0,0 0))", - 0, 10, 16); - - // Hole: interior tangent to exterior - test_one("h1", - "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 2,2 4,3 2,1 2))", - 0, 6, 16); - - // Hole: interior intersecting exterior - test_one("h2", - "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 3,5 4,1 1))", - 0, 8, 16.25); - - // Hole: two intersecting holes - test_one("h3", - "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 3,3 3,3 1,1 1),(2 2,2 3.5,3.5 3.5,3.5 2,2 2))", - 0, 5, 16); - - // Hole: self-intersecting hole - test_one("h4", - "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,3 3,3 2.5,1 3.5,1.5 3.5,1 1))", - 1, 9, 14.484848484848484); - - // See power point - test_one("case_1", - "POLYGON((1 3,0 9,9 5,1 7,9 8,2 5,10 10,9 2,1 3))", - 0, 7, 50.48056402439); - - // See power point, and http://en.wikipedia.org/wiki/Pentagram - test_one("pentagram", - "POLYGON((5 0,2.5 9,9.5 3.5,0.5 3.5,7.5 9,5 0))", - 0, 11, 25.6158412); - -#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - test_one("pentagram_rev", - "POLYGON((5 0,7.5 9,0.5 3.5,9.5 3.5,2.5 9,5 0))", - 0, 11, 25.6158412); - - // Poygons contain too many polygons + TEST_DISSOLVE(dissolve_7, 50.48056402439, 1, 0, 7); + TEST_DISSOLVE(dissolve_8, 25.6158412, 1, 0, 11); // CCW polygons should turn CW after dissolve - test_one("cw", - "POLYGON((2 8,8 8,8 0,0 0,0 6,4 6,4 4,2 4,2 8))", - 0, 7, 60); - test_one("ccw", - "POLYGON((2 8,2 4,4 4,4 6,0 6,0 0,8 0,8 8,2 8))", - 0, 12, 64); // TODO: should have the same, 7, 60. The polygon is dissolved (splitted) but the extra polygon is still on top of the other + TEST_DISSOLVE(dissolve_9, 25.6158412, 1, 0, 11); + TEST_DISSOLVE(dissolve_10, 60.0, 1, 0, 7); + TEST_DISSOLVE(dissolve_11, 60.0, 1, 0, 7); - // https://svn.boost.org/trac/boost/ticket/10713 - test_one("ticket_10713", - "POLYGON((-0.7189743518829346 4.1308121681213379, 0.0831791982054710 4.1034231185913086, 0.1004156470298767 4.1107301712036133, 0.1044322624802589 4.1026973724365234, 0.0831791982054710 4.1034231185913086, -0.7711903452873230 3.7412264347076416, -0.7189743518829346 4.1308121681213379))", - 0, 8, 0.157052766); + // More pentagrams + TEST_DISSOLVE(dissolve_12, 186556.84077318, 1, 0, 15); + TEST_DISSOLVE(dissolve_13, 361733.91651, 1, 0, 15); - // Reported by Artem Pavlenko at gitter - // https://gitter.im/boostorg/geometry?at=58ef46408e4b63533dc49b48 - test_one("artem1", - "POLYGON((36.9121 2.03883,26.2052 54.353,60.0781 64.2202,96.2171 55.9826,71.1506 39.8365,5.72552 94.1523,4.06819 13.9054,59.7155 44.5877,60.9243 16.4597,48.8696 93.039,36.9121 2.03883))", - 0, 0, 0); // TODO: expected - // https://gitter.im/boostorg/geometry?at=58ef6a2b69a692963ea83a6d - test_one("artem2", - "POLYGON((337 176,602 377,294 372,581 166,453 449,337 176))", - 0, 0, 0); // TODO: expected + TEST_DISSOLVE(dissolve_14, 4.0, 3, 0, 13); + TEST_DISSOLVE(dissolve_15, 4.0, 3, 0, 13); +#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS + TEST_DISSOLVE(dissolve_16, 99999.0, 9, 99, 999); #endif - // Multi-geometries - { - typedef bg::model::multi_polygon multi_polygon; + TEST_DISSOLVE(dissolve_d1, 8.0, 1, 0, 4); + TEST_DISSOLVE(dissolve_d2, 16.0, 1, 0, 10); - test_one("three_triangles", - "MULTIPOLYGON(((1 1,5 5,8 0,1 1)),((4 2,0 8,5 9,4 2)),((5 3,4 8,10 4,5 3)))" , - 1, 13, 42.614078674948232); + TEST_DISSOLVE(dissolve_h1_a, 16.0, 1, 0, 6); + TEST_DISSOLVE(dissolve_h1_b, 14.0, 1, 1, 10); + TEST_DISSOLVE(dissolve_h2, 16.25, 1, 0, 8); + TEST_DISSOLVE(dissolve_h3, 16.0, 1, 0, 5); // no generated hole (yet) + TEST_DISSOLVE(dissolve_h4, 14.484848, 1, 1, 9); - test_one("simplex_two", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)))", - 0, 8, 14.7); - test_one("simplex_three", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)))", - 0, 14, 16.7945); - test_one("simplex_four", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((5 5,7 7,8 4,5 5)))", - 0, 18, 20.7581); + TEST_DISSOLVE(dissolve_mail_2017_09_24_a, 0.5, 2, 0, 8); - // disjoint - test_one("simplex_disjoint", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((1 6,2 10,5 7,1 6)),((3 4,5 6,6 2,3 4)),((6 5,8 7,9 4,6 5)))", - 0, 16, 24.0); + TEST_DISSOLVE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 6); + TEST_DISSOLVE(dissolve_mail_2017_09_24_c, 0.5, 2, 0, 8); + TEST_DISSOLVE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 4); + TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.001801138128, 5, 0, 69); + TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 5, 0, 69); + TEST_DISSOLVE(dissolve_mail_2017_09_24_g, 0.5, 1, 0, 4); + TEST_DISSOLVE(dissolve_mail_2017_09_24_h, 0.5, 1, 0, 4); - // new hole of four - test_one("new_hole", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((3 1,5 4,8 4,3 1)))", - 1, 18, 19.5206); + TEST_DISSOLVE(dissolve_ticket10713, 0.157052766, 2, 0, 8); - // GGL mailing list - report Javier - 2011, March 7 - test_one("ggl_list_20110307_javier_01_a", - "MULTIPOLYGON(((560 -400, 600 -400, 600 -440, 560 -440, 560 -400)), ((480 -400, 520 -400, 520 -440, 480 -440, 480 -400)), ((600 -320, 640 -320, 640 -360, 600 -360, 600 -320)), ((520 -400, 560 -400, 560 -440, 520 -440, 520 -400)))", - 1, 14, 6400); - test_one("ggl_list_20110307_javier_01_b", - "POLYGON((0 0, 2000 0, 2000 -2000, 0 -2000, 0 0), (560 -400, 560 -440, 600 -440, 600 -400, 560 -400), (480 -400, 480 -440, 520 -440, 520 -400, 480 -400), (600 -320, 600 -360, 640 -360, 640 -320, 600 -320), (520 -400, 520 -440, 560 -440, 560 -400, 520 -400))", - 1, 19, 3993600); - } + TEST_MULTI(multi_three_triangles, 42.614078674948232, 1, 1, 13); + TEST_MULTI(multi_simplex_two, 14.7, 1, 0, 8); + TEST_MULTI(multi_simplex_three, 16.7945, 1, 0, 14); + TEST_MULTI(multi_simplex_four, 20.7581, 1, 0, 18); + TEST_MULTI(multi_disjoint, 24.0, 4, 0, 16); + TEST_MULTI(multi_new_interior, 19.5206, 1, 1, 18); + TEST_MULTI(ggl_list_20110307_javier_01_a, 6400.0, 2, 0, 14); + TEST_DISSOLVE(ggl_list_20110307_javier_01_b, 3993600.0, 1, 2, 19); + TEST_DISSOLVE(dissolve_ticket17, 0.00920834633689, 1, 1, 228); + TEST_DISSOLVE(dissolve_reallife, 91756.916526794434, 1, 0, 25); +#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS + // To fix this, and ggl_list_denis, it is necessary to follow in both + // positive AND negative direction, which is not supported yet. + TEST_DISSOLVE(gitter_2013_04_a, 99999.0, 9, 99, 999); +#endif -/* - //Should be solved (completely) differently - // From mail on the ggl-mailing list - test_one("mail_denis_1", - "POLYGON((55 10, 141 237, 249 23, 21 171, 252 169, 24 89, 266 73, 55 10))", - 0, 7, 50.48056402439); - // Source: http://upload.wikimedia.org/wikipedia/commons/8/83/Acute_heptagram.svg - test_one("acute_heptagram", - "POLYGON((409 5,229 793.631528,733.348792 161.198146,4.543671 512.172194,813.456329 512.172194,84.651208 161.198146,589 793.631528))", - 0, 11, 25.6158412); - - // Source: http://upload.wikimedia.org/wikipedia/commons/a/a7/Obtuse_heptagram.svg - test_one("obtuse_heptagram", - "POLYGON((409 5,813.456329 512.172194,229 793.631528,84.651208 161.198146,733.348792 161.198146,589 793.631528,4.543671 512.172194))", - 0, 11, 25.6158412); -*/ - - - std::string const ticket17 = "POLYGON ((-122.28139163 37.37319149,-122.28100699 37.37273669,-122.28002186 37.37303123,-122.27979681 37.37290072,-122.28007349 37.37240493,-122.27977334 37.37220360,-122.27819720 37.37288580,-122.27714184 37.37275161,-122.27678628 37.37253167,-122.27766437 37.37180973,-122.27804382 37.37121453,-122.27687664 37.37101354,-122.27645829 37.37203386,-122.27604423 37.37249110,-122.27632234 37.37343339,-122.27760980 37.37391082,-122.27812478 37.37800320,-122.26117222 37.39121007,-122.25572289 37.39566631,-122.25547269 37.39564971,-122.25366304 37.39552993,-122.24919976 37.39580268,-122.24417933 37.39366907,-122.24051443 37.39094143,-122.23246277 37.38100418,-122.23606766 37.38141338,-122.24001587 37.37738940,-122.23666848 37.37609347,-122.23057450 37.37882170,-122.22679803 37.37807143,-122.22525727 37.37448817,-122.22523229 37.37443000,-122.23083199 37.37609347,-122.23033486 37.37777891,-122.23169030 37.37732117,-122.23229178 37.37709687,-122.23237761 37.37631249,-122.23297776 37.37438834,-122.23872850 37.37165986,-122.24044511 37.36934068,-122.24671067 37.36865847,-122.24825570 37.36981819,-122.25151719 37.36947713,-122.25357721 37.36756706,-122.26001451 37.36579354,-122.25615213 37.36545239,-122.25486458 37.36245083,-122.25357721 37.36108651,-122.25194642 37.36013139,-122.24885652 37.35958557,-122.24911401 37.35849399,-122.25357721 37.35808470,-122.25675286 37.35897159,-122.25855539 37.35753887,-122.26181687 37.35828939,-122.26713837 37.35897159,-122.26782510 37.36108651,-122.26662339 37.36456559,-122.27288911 37.36722601,-122.27366159 37.36531602,-122.27168740 37.36470213,-122.27391900 37.36374701,-122.27074326 37.36245083,-122.27134408 37.35951742,-122.27426240 37.36135926,-122.27709482 37.36115474,-122.27966974 37.36231438,-122.27958391 37.36463382,-122.27572152 37.36463382,-122.27563569 37.36524779,-122.27700899 37.36593000,-122.27709482 37.36763529,-122.27554978 37.36838573,-122.27667254 37.36931478,-122.27677932 37.36932073,-122.27769362 37.36853987,-122.27942490 37.36830803,-122.28178776 37.36677917,-122.28509559 37.36443500,-122.28845129 37.36413744,-122.29194403 37.36695946,-122.29382577 37.36726817,-122.29600414 37.36898512,-122.29733083 37.36995398,-122.29593239 37.37141436,-122.29416649 37.37075898,-122.29325026 37.37108436,-122.29652910 37.37311697,-122.29584237 37.37374461,-122.29537583 37.37573372,-122.29487677 37.37752502,-122.30923212 37.37593011,-122.31122484 37.38230086,-122.31467994 37.38092472,-122.31715663 37.38252181,-122.32307970 37.38166978,-122.31985618 37.37667694,-122.32210304 37.37580220,-122.32581446 37.37589532,-122.32401730 37.37331839,-122.32960417 37.37189020,-122.33465527 37.37331906,-122.33425328 37.37623680,-122.33620676 37.37726132,-122.33397986 37.37822382,-122.33358918 37.38036590,-122.33202637 37.37986918,-122.33147954 37.38101784,-122.33394080 37.38198017,-122.33545239 37.38587943,-122.33478058 37.38785697,-122.33386050 37.38723721,-122.33350041 37.38571137,-122.33122003 37.38548891,-122.33140008 37.38650606,-122.33366042 37.38817490,-122.33244019 37.39157602,-122.33298157 37.39419201,-122.33164013 37.39477028,-122.33202017 37.39518351,-122.33358038 37.39499282,-122.33376050 37.39597811,-122.33550067 37.39734478,-122.33556069 37.39481797,-122.33344040 37.39292676,-122.33638094 37.38892189,-122.34240644 37.38852719,-122.34906293 37.38726898,-122.35072321 37.39338769,-122.34910291 37.39445252,-122.34796272 37.39410291,-122.34449043 37.39640534,-122.34500223 37.39729709,-122.34936291 37.39670910,-122.35098322 37.39531066,-122.35364623 37.39554510,-122.35434369 37.39612111,-122.35798429 37.39600988,-122.35768430 37.39478621,-122.36334519 37.39206871,-122.36604726 37.39203267,-122.36778592 37.39335592,-122.36518870 37.40022011,-122.36554552 37.40247752,-122.36370519 37.40331974,-122.36270506 37.40530591,-122.36320512 37.40670418,-122.36149849 37.40851392,-122.36730580 37.41054938,-122.37263720 37.41378932,-122.37161871 37.42076600,-122.36566153 37.42006292,-122.36520547 37.42742106,-122.37165953 37.43661157,-122.35943972 37.44459022,-122.35356359 37.44600810,-122.33792254 37.45796329,-122.35228518 37.47478091,-122.35127080 37.48181199,-122.34867342 37.48487322,-122.34359717 37.48801082,-122.33388431 37.48677650,-122.33142321 37.48429747,-122.32929580 37.48473149,-122.32609609 37.48291144,-122.32344850 37.48228229,-122.31924364 37.48410234,-122.31677299 37.48114051,-122.31431751 37.47848973,-122.31259201 37.47682190,-122.31515972 37.47568196,-122.31691389 37.47360309,-122.31292494 37.46960081,-122.31130153 37.46937743,-122.30889894 37.47124987,-122.30612839 37.47011613,-122.30149630 37.46568378,-122.30064277 37.46363784,-122.29283821 37.45922376,-122.28630141 37.45415497,-122.28883099 37.44629920,-122.28316717 37.44197138,-122.27554148 37.42297597,-122.25597410 37.40553692,-122.25196579 37.40129593,-122.25012043 37.40049143,-122.24823207 37.39897758,-122.24754551 37.39740941,-122.24778582 37.39621607,-122.24934787 37.39599102,-122.25005170 37.39871849,-122.25222328 37.39863668,-122.25342491 37.39737529,-122.25520162 37.39667289,-122.25528737 37.39522726,-122.27747460 37.37809616,-122.27977493 37.37858717,-122.28157729 37.37920106,-122.28322534 37.37952846,-122.28416939 37.38092656,-122.28621223 37.37984219,-122.28638389 37.37613857,-122.28382607 37.37843722,-122.27930278 37.37718220,-122.28196361 37.37652740,-122.28295058 37.37568167,-122.28216101 37.37523148,-122.28114822 37.37543608,-122.27934569 37.37528613,-122.27996369 37.37448121,-122.28104521 37.37454944,-122.28185197 37.37422883,-122.28290767 37.37474038,-122.28376597 37.37467224,-122.28428104 37.37399012,-122.28402346 37.37338989,-122.28610922 37.37364914,-122.28651264 37.37327388,-122.28672722 37.37207343,-122.28628398 37.37205448,-122.28574460 37.37166682,-122.28479711 37.37200981,-122.28327731 37.37137228,-122.28285511 37.37100700,-122.28279409 37.37125669,-122.28315527 37.37173756,-122.28321872 37.37220569,-122.28187007 37.37231918,-122.28193109 37.37294908,-122.28139163 37.37319149))"; - test_one("ticket17", ticket17, - 1, 228, 0.00920834633689); - - // Real-life - std::string const toolkit = "POLYGON((170718 605997,170718 605997,170776 606016,170773 606015,170786 606020,170778 606016,170787 606021,170781 606017,170795 606028,170795 606028,170829 606055,170939 606140,170933 605968,170933 605968,170932 605908,170929 605834,170920 605866,170961 605803,170739 605684,170699 605749,170691 605766,170693 605762,170686 605775,170688 605771,170673 605794,170676 605790,170668 605800,170672 605796,170651 605818,170653 605816,170639 605829,170568 605899,170662 605943,170633 605875,170603 605961,170718 605997))"; - test_one("toolkit", toolkit, - 0, 25, 91756.916526794434); + TEST_DISSOLVE(gitter_2013_04_b, 31210.429356259738, 1, 0, 11); +#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS + TEST_DISSOLVE(ggl_list_denis, 99999.0, 9, 99, 999); +#endif } diff --git a/include/boost/geometry/algorithms/correct.hpp b/include/boost/geometry/algorithms/correct.hpp index 5d3b6939a..07e012faf 100644 --- a/include/boost/geometry/algorithms/correct.hpp +++ b/include/boost/geometry/algorithms/correct.hpp @@ -5,6 +5,10 @@ // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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. @@ -28,6 +32,7 @@ #include #include +#include #include #include @@ -41,7 +46,6 @@ #include #include -#include #include #include @@ -61,7 +65,8 @@ namespace detail { namespace correct template struct correct_nop { - static inline void apply(Geometry& ) + template + static inline void apply(Geometry& , Strategy const& ) {} }; @@ -104,8 +109,8 @@ struct correct_box_loop template struct correct_box { - - static inline void apply(Box& box) + template + static inline void apply(Box& box, Strategy const& ) { // Currently only for Cartesian coordinates // (or spherical without crossing dateline) @@ -119,18 +124,12 @@ struct correct_box // Close a ring, if not closed -template +template class Predicate> struct correct_ring { typedef typename point_type::type point_type; typedef typename coordinate_type::type coordinate_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, @@ -138,30 +137,17 @@ struct correct_ring > ring_area_type; - static inline void apply(Ring& r) + template + static inline void apply(Ring& r, Strategy const& strategy) { - // Check close-ness - if (boost::size(r) > 2) - { - // check if closed, if not, close it - bool const disjoint = geometry::disjoint(*boost::begin(r), *(boost::end(r) - 1)); - closure_selector const s = geometry::closure::value; + // Correct closure if necessary + detail::correct_closure::close_or_open_ring::apply(r); - if (disjoint && (s == closed)) - { - geometry::append(r, *boost::begin(r)); - } - if (! disjoint && s != closed) - { - // Open it by removing last point - geometry::traits::resize::apply(r, boost::size(r) - 1); - } - } // Check area - Predicate predicate; - typedef typename default_area_result::type area_result_type; - area_result_type const zero = area_result_type(); - if (predicate(ring_area_type::apply(r, strategy_type()), zero)) + typedef typename Strategy::return_type area_result_type; + Predicate predicate; + area_result_type const zero = 0; + if (predicate(ring_area_type::apply(r, strategy), zero)) { std::reverse(boost::begin(r), boost::end(r)); } @@ -174,15 +160,15 @@ template struct correct_polygon { typedef typename ring_type::type ring_type; - typedef typename default_area_result::type area_result_type; - - static inline void apply(Polygon& poly) + + template + static inline void apply(Polygon& poly, Strategy const& strategy) { correct_ring < ring_type, - std::less - >::apply(exterior_ring(poly)); + std::less + >::apply(exterior_ring(poly), strategy); typename interior_return_type::type rings = interior_rings(poly); @@ -192,8 +178,8 @@ struct correct_polygon correct_ring < ring_type, - std::greater - >::apply(*it); + std::greater + >::apply(*it, strategy); } } }; @@ -237,7 +223,7 @@ struct correct : detail::correct::correct_ring < Ring, - std::less::type> + std::less > {}; @@ -281,29 +267,36 @@ namespace resolve_variant { template struct correct { - static inline void apply(Geometry& geometry) + template + static inline void apply(Geometry& geometry, Strategy const& strategy) { concepts::check(); - dispatch::correct::apply(geometry); + dispatch::correct::apply(geometry, strategy); } }; template struct correct > { + template struct visitor: boost::static_visitor { + Strategy const& m_strategy; + + visitor(Strategy const& strategy): m_strategy(strategy) {} + template void operator()(Geometry& geometry) const { - correct::apply(geometry); + correct::apply(geometry, m_strategy); } }; + template static inline void - apply(boost::variant& geometry) + apply(boost::variant& geometry, Strategy const& strategy) { - boost::apply_visitor(visitor(), geometry); + boost::apply_visitor(visitor(strategy), geometry); } }; @@ -325,7 +318,37 @@ struct correct > template inline void correct(Geometry& geometry) { - resolve_variant::correct::apply(geometry); + typedef typename point_type::type point_type; + + typedef typename strategy::area::services::default_strategy + < + typename cs_tag::type, + point_type + >::type strategy_type; + + resolve_variant::correct::apply(geometry, strategy_type()); +} + +/*! +\brief Corrects a geometry +\details Corrects a geometry: all rings which are wrongly oriented with respect + to their expected orientation are reversed. To all rings which do not have a + closing point and are typed as they should have one, the first point is + appended. Also boxes can be corrected. +\ingroup correct +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Area} +\param geometry \param_geometry which will be corrected if necessary +\param strategy \param_strategy{area} + +\qbk{distinguish,with strategy} + +\qbk{[include reference/algorithms/correct.qbk]} +*/ +template +inline void correct(Geometry& geometry, Strategy const& strategy) +{ + resolve_variant::correct::apply(geometry, strategy); } #if defined(_MSC_VER) diff --git a/include/boost/geometry/algorithms/correct_closure.hpp b/include/boost/geometry/algorithms/correct_closure.hpp new file mode 100644 index 000000000..323107f23 --- /dev/null +++ b/include/boost/geometry/algorithms/correct_closure.hpp @@ -0,0 +1,235 @@ +// Boost.Geometry + +// Copyright (c) 2017 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_CORRECT_CLOSURE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_CORRECT_CLOSURE_HPP + +#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_closure +{ + +template +struct nop +{ + static inline void apply(Geometry& ) + {} +}; + + +// Close a ring, if not closed, or open it +template +struct close_or_open_ring +{ + static inline void apply(Ring& r) + { + if (boost::size(r) <= 2) + { + return; + } + + bool const disjoint = geometry::disjoint(*boost::begin(r), + *(boost::end(r) - 1)); + closure_selector const s = geometry::closure::value; + + if (disjoint && s == closed) + { + // Close it by adding first point + geometry::append(r, *boost::begin(r)); + } + else if (! disjoint && s != closed) + { + // Open it by removing last point + geometry::traits::resize::apply(r, boost::size(r) - 1); + } + } +}; + +// Close/open exterior ring and all its interior rings +template +struct close_or_open_polygon +{ + typedef typename ring_type::type ring_type; + + static inline void apply(Polygon& poly) + { + close_or_open_ring::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) + { + close_or_open_ring::apply(*it); + } + } +}; + +}} // namespace detail::correct_closure +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template ::type> +struct correct_closure: not_implemented +{}; + +template +struct correct_closure + : detail::correct_closure::nop +{}; + +template +struct correct_closure + : detail::correct_closure::nop +{}; + +template +struct correct_closure + : detail::correct_closure::nop +{}; + + +template +struct correct_closure + : detail::correct_closure::nop +{}; + +template +struct correct_closure + : detail::correct_closure::close_or_open_ring +{}; + +template +struct correct_closure + : detail::correct_closure::close_or_open_polygon +{}; + + +template +struct correct_closure + : detail::correct_closure::nop +{}; + + +template +struct correct_closure + : detail::correct_closure::nop +{}; + + +template +struct correct_closure + : detail::multi_modify + < + Geometry, + detail::correct_closure::close_or_open_polygon + < + typename boost::range_value::type + > + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_variant +{ + +template +struct correct_closure +{ + static inline void apply(Geometry& geometry) + { + concepts::check(); + dispatch::correct_closure::apply(geometry); + } +}; + +template +struct correct_closure > +{ + struct visitor: boost::static_visitor + { + template + void operator()(Geometry& geometry) const + { + correct_closure::apply(geometry); + } + }; + + static inline void + apply(boost::variant& geometry) + { + visitor vis; + boost::apply_visitor(vis, geometry); + } +}; + +} // namespace resolve_variant + + +/*! +\brief Closes or opens a geometry, according to its type +\details Corrects a geometry w.r.t. closure points to all rings which do not + have a closing point and are typed as they should have one, the first point + is appended. +\ingroup correct_closure +\tparam Geometry \tparam_geometry +\param geometry \param_geometry which will be corrected if necessary +*/ +template +inline void correct_closure(Geometry& geometry) +{ + resolve_variant::correct_closure::apply(geometry); +} + + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CORRECT_CLOSURE_HPP diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index 029053dda..a149f1dd4 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -122,7 +122,8 @@ struct buffer_range typename DistanceStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void add_join(Collection& collection, @@ -133,18 +134,19 @@ struct buffer_range Point const& input, output_point_type const& perp1, output_point_type const& perp2, - strategy::buffer::buffer_side_selector side, + geometry::strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, - RobustPolicy const& ) + RobustPolicy const& , + Strategy const& strategy) // side strategy { output_point_type intersection_point; geometry::assign_zero(intersection_point); - strategy::buffer::join_selector join - = get_join_type(penultimate_input, previous_input, input); - if (join == strategy::buffer::join_convex) + geometry::strategy::buffer::join_selector join + = get_join_type(penultimate_input, previous_input, input, strategy); + if (join == geometry::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 @@ -157,23 +159,23 @@ struct buffer_range switch(join) { - case strategy::buffer::join_continue : + case geometry::strategy::buffer::join_continue : // No join, we get two consecutive sides break; - case strategy::buffer::join_concave : + case geometry::strategy::buffer::join_concave : { std::vector range_out; range_out.push_back(prev_perp2); range_out.push_back(previous_input); - collection.add_piece(strategy::buffer::buffered_concave, previous_input, range_out); + collection.add_piece(geometry::strategy::buffer::buffered_concave, previous_input, range_out); range_out.clear(); range_out.push_back(previous_input); range_out.push_back(perp1); - collection.add_piece(strategy::buffer::buffered_concave, previous_input, range_out); + collection.add_piece(geometry::strategy::buffer::buffered_concave, previous_input, range_out); } break; - case strategy::buffer::join_spike : + case geometry::strategy::buffer::join_spike : { // For linestrings, only add spike at one side to avoid // duplicates @@ -183,7 +185,7 @@ struct buffer_range collection.set_current_ring_concave(); } break; - case strategy::buffer::join_convex : + case geometry::strategy::buffer::join_convex : { // The corner is convex, we create a join // TODO (future) - avoid a separate vector, add the piece directly @@ -193,7 +195,7 @@ struct buffer_range distance.apply(previous_input, input, side), range_out)) { - collection.add_piece(strategy::buffer::buffered_join, + collection.add_piece(geometry::strategy::buffer::buffered_join, previous_input, range_out); } } @@ -201,27 +203,24 @@ struct buffer_range } } - static inline strategy::buffer::join_selector get_join_type( + template + static inline geometry::strategy::buffer::join_selector get_join_type( output_point_type const& p0, output_point_type const& p1, - output_point_type const& p2) + output_point_type const& p2, + Strategy const& strategy) // side strategy { - 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 + int const side = strategy.apply(p0, p1, p2); + return side == -1 ? geometry::strategy::buffer::join_convex + : side == 1 ? geometry::strategy::buffer::join_concave : parallel_continue ( get<0>(p2) - get<0>(p1), get<1>(p2) - get<1>(p1), get<0>(p1) - get<0>(p0), get<1>(p1) - get<1>(p0) - ) ? strategy::buffer::join_continue - : strategy::buffer::join_spike; + ) ? geometry::strategy::buffer::join_continue + : geometry::strategy::buffer::join_spike; } template @@ -232,16 +231,18 @@ struct buffer_range typename SideStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code iterate(Collection& collection, + static inline geometry::strategy::buffer::result_code iterate(Collection& collection, Iterator begin, Iterator end, - strategy::buffer::buffer_side_selector side, + geometry::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, + Strategy const& strategy, // side strategy output_point_type& first_p1, output_point_type& first_p2, output_point_type& last_p1, @@ -273,7 +274,7 @@ struct buffer_range * pup: penultimate_point */ - strategy::buffer::result_code result = strategy::buffer::result_no_output; + geometry::strategy::buffer::result_code result = geometry::strategy::buffer::result_no_output; bool first = true; Iterator it = begin; @@ -284,25 +285,25 @@ struct buffer_range for (Iterator prev = it++; it != end; ++it) { generated_side.clear(); - strategy::buffer::result_code error_code + geometry::strategy::buffer::result_code error_code = side_strategy.apply(*prev, *it, side, distance_strategy, generated_side); - if (error_code == strategy::buffer::result_no_output) + if (error_code == geometry::strategy::buffer::result_no_output) { // Because input is simplified, this is improbable, // but it can happen for degenerate geometries // Further handling of this side is skipped continue; } - else if (error_code == strategy::buffer::result_error_numerical) + else if (error_code == geometry::strategy::buffer::result_error_numerical) { return error_code; } BOOST_GEOMETRY_ASSERT(! generated_side.empty()); - result = strategy::buffer::result_normal; + result = geometry::strategy::buffer::result_normal; if (! first) { @@ -312,7 +313,7 @@ struct buffer_range *it, generated_side.front(), generated_side.back(), side, distance_strategy, join_strategy, end_strategy, - robust_policy); + robust_policy, strategy); } collection.add_side_piece(*prev, *it, generated_side, first); @@ -350,7 +351,8 @@ struct buffer_multi typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply(Multi const& multi, Collection& collection, @@ -359,7 +361,8 @@ struct buffer_multi JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { for (typename boost::range_iterator::type it = boost::begin(multi); @@ -369,7 +372,7 @@ struct buffer_multi Policy::apply(*it, collection, distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); } } }; @@ -396,9 +399,9 @@ inline void buffer_point(Point const& point, Collection& collection, 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.add_piece(geometry::strategy::buffer::buffered_point, range_out, false); collection.set_piece_center(point); - collection.finish_ring(strategy::buffer::result_normal); + collection.finish_ring(geometry::strategy::buffer::result_normal); } @@ -436,7 +439,8 @@ struct buffer_inserter typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply(Point const& point, Collection& collection, DistanceStrategy const& distance_strategy, @@ -444,7 +448,8 @@ struct buffer_inserter JoinStrategy const& , EndStrategy const& , PointStrategy const& point_strategy, - RobustPolicy const& ) + RobustPolicy const& , + Strategy const& ) // side strategy { detail::buffer::buffer_point < @@ -472,29 +477,32 @@ struct buffer_inserter_ring typename SideStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code iterate(Collection& collection, + static inline geometry::strategy::buffer::result_code iterate(Collection& collection, Iterator begin, Iterator end, - strategy::buffer::buffer_side_selector side, + geometry::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) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { output_point_type first_p1, first_p2, last_p1, last_p2; typedef detail::buffer::buffer_range buffer_range; - strategy::buffer::result_code result + geometry::strategy::buffer::result_code result = buffer_range::iterate(collection, begin, end, side, - distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy, + distance_strategy, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1, first_p2, last_p1, last_p2); // Generate closing join - if (result == strategy::buffer::result_normal) + if (result == geometry::strategy::buffer::result_normal) { buffer_range::add_join(collection, *(end - 2), @@ -502,7 +510,7 @@ struct buffer_inserter_ring *(begin + 1), first_p1, first_p2, side, distance_strategy, join_strategy, end_strategy, - robust_policy); + robust_policy, strategy); } // Buffer is closed automatically by last closing corner @@ -517,21 +525,23 @@ struct buffer_inserter_ring typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code apply(RingInput const& ring, + static inline geometry::strategy::buffer::result_code 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) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { RingInput simplified; detail::buffer::simplify_input(ring, distance, simplified); - strategy::buffer::result_code code = strategy::buffer::result_no_output; + geometry::strategy::buffer::result_code code = geometry::strategy::buffer::result_no_output; std::size_t n = boost::size(simplified); std::size_t const min_points = core_detail::closure::minimum_ring_size @@ -546,18 +556,20 @@ struct buffer_inserter_ring { // Walk backwards (rings will be reversed afterwards) code = iterate(collection, boost::rbegin(view), boost::rend(view), - strategy::buffer::buffer_side_right, - distance, side_strategy, join_strategy, end_strategy, robust_policy); + geometry::strategy::buffer::buffer_side_right, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy); } else { code = iterate(collection, boost::begin(view), boost::end(view), - strategy::buffer::buffer_side_left, - distance, side_strategy, join_strategy, end_strategy, robust_policy); + geometry::strategy::buffer::buffer_side_left, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy); } } - if (code == strategy::buffer::result_no_output && n >= 1) + if (code == geometry::strategy::buffer::result_no_output && n >= 1) { // Use point_strategy to buffer degenerated ring detail::buffer::buffer_point @@ -586,23 +598,25 @@ struct buffer_inserter typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code apply(RingInput const& ring, + static inline geometry::strategy::buffer::result_code 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) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { collection.start_new_ring(); - strategy::buffer::result_code const code + geometry::strategy::buffer::result_code const code = buffer_inserter_ring::apply(ring, collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); collection.finish_ring(code); return code; } @@ -627,16 +641,18 @@ struct buffer_inserter typename SideStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code iterate(Collection& collection, + static inline geometry::strategy::buffer::result_code iterate(Collection& collection, Iterator begin, Iterator end, - strategy::buffer::buffer_side_selector side, + geometry::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, + Strategy const& strategy, // side strategy output_point_type& first_p1) { input_point_type const& ultimate_point = *(end - 1); @@ -647,18 +663,18 @@ struct buffer_inserter // 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) + if (side == geometry::strategy::buffer::buffer_side_right) { reverse_p1 = first_p1; } else { std::vector generated_side; - strategy::buffer::result_code code + geometry::strategy::buffer::result_code code = side_strategy.apply(ultimate_point, penultimate_point, - strategy::buffer::buffer_side_right, + geometry::strategy::buffer::buffer_side_right, distance_strategy, generated_side); - if (code != strategy::buffer::result_normal) + if (code != geometry::strategy::buffer::result_normal) { // No output or numerical error return code; @@ -668,16 +684,18 @@ struct buffer_inserter output_point_type first_p2, last_p1, last_p2; - strategy::buffer::result_code result + geometry::strategy::buffer::result_code result = detail::buffer::buffer_range::iterate(collection, begin, end, side, - distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy, + distance_strategy, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1, first_p2, last_p1, last_p2); - if (result == strategy::buffer::result_normal) + if (result == geometry::strategy::buffer::result_normal) { std::vector range_out; - end_strategy.apply(penultimate_point, last_p2, ultimate_point, reverse_p1, side, distance_strategy, 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 result; @@ -691,20 +709,23 @@ struct buffer_inserter typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code apply(Linestring const& linestring, Collection& collection, + static inline geometry::strategy::buffer::result_code 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) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { Linestring simplified; detail::buffer::simplify_input(linestring, distance, simplified); - strategy::buffer::result_code code = strategy::buffer::result_no_output; + geometry::strategy::buffer::result_code code = geometry::strategy::buffer::result_no_output; std::size_t n = boost::size(simplified); if (n > 1) { @@ -712,21 +733,23 @@ struct buffer_inserter output_point_type first_p1; code = iterate(collection, boost::begin(simplified), boost::end(simplified), - strategy::buffer::buffer_side_left, - distance, side_strategy, join_strategy, end_strategy, robust_policy, + geometry::strategy::buffer::buffer_side_left, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1); - if (code == strategy::buffer::result_normal) + if (code == geometry::strategy::buffer::result_normal) { code = iterate(collection, boost::rbegin(simplified), boost::rend(simplified), - strategy::buffer::buffer_side_right, - distance, side_strategy, join_strategy, end_strategy, robust_policy, + geometry::strategy::buffer::buffer_side_right, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1); } collection.finish_ring(code); } - if (code == strategy::buffer::result_no_output && n >= 1) + if (code == geometry::strategy::buffer::result_no_output && n >= 1) { // Use point_strategy to buffer degenerated linestring detail::buffer::buffer_point @@ -763,7 +786,8 @@ private: typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void iterate(Iterator begin, Iterator end, @@ -774,15 +798,16 @@ private: EndStrategy const& end_strategy, PointStrategy const& point_strategy, RobustPolicy const& robust_policy, + Strategy const& strategy, // side strategy bool is_interior) { for (Iterator it = begin; it != end; ++it) { collection.start_new_ring(); - strategy::buffer::result_code const code + geometry::strategy::buffer::result_code const code = policy::apply(*it, collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); collection.finish_ring(code, is_interior); } @@ -797,7 +822,8 @@ private: typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply_interior_rings(InteriorRings const& interior_rings, @@ -807,12 +833,13 @@ private: JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { iterate(boost::begin(interior_rings), boost::end(interior_rings), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy, true); + robust_policy, strategy, true); } public: @@ -824,7 +851,8 @@ public: typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply(PolygonInput const& polygon, Collection& collection, @@ -833,16 +861,17 @@ public: JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { { collection.start_new_ring(); - strategy::buffer::result_code const code + geometry::strategy::buffer::result_code const code = policy::apply(exterior_ring(polygon), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); collection.finish_ring(code, false, geometry::num_interior_rings(polygon) > 0u); @@ -851,7 +880,7 @@ public: apply_interior_rings(interior_rings(polygon), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); } }; @@ -945,7 +974,7 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator >::apply(geometry_input, collection, distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, intersection_strategy.get_side_strategy()); collection.get_turns(); collection.classify_turns(linear); diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index aa6302f39..f78f11f19 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -142,10 +143,20 @@ struct buffered_piece_collection robust_point_type >::type robust_comparable_radius_type; - typedef typename strategy::side::services::default_strategy + typedef typename IntersectionStrategy::side_strategy_type side_strategy_type; + + typedef typename IntersectionStrategy::template area_strategy < - typename cs_tag::type - >::type side_strategy; + point_type + >::type area_strategy_type; + + typedef typename IntersectionStrategy::template area_strategy + < + robust_point_type + >::type robust_area_strategy_type; + + typedef typename area_strategy_type::return_type area_result_type; + typedef typename robust_area_strategy_type::return_type robust_area_result_type; typedef typename geometry::rescale_policy_type < @@ -306,7 +317,10 @@ struct buffered_piece_collection cluster_type m_clusters; - IntersectionStrategy const& m_intersection_strategy; + IntersectionStrategy m_intersection_strategy; + side_strategy_type m_side_strategy; + area_strategy_type m_area_strategy; + robust_area_strategy_type m_robust_area_strategy; RobustPolicy const& m_robust_policy; struct redundant_turn @@ -321,6 +335,9 @@ struct buffered_piece_collection RobustPolicy const& robust_policy) : m_first_piece_index(-1) , m_intersection_strategy(intersection_strategy) + , m_side_strategy(intersection_strategy.get_side_strategy()) + , m_area_strategy(intersection_strategy.template get_area_strategy()) + , m_robust_area_strategy(intersection_strategy.template get_area_strategy()) , m_robust_policy(robust_policy) {} @@ -478,7 +495,7 @@ struct buffered_piece_collection for (typename occupation_map_type::iterator it = occupation_map.begin(); it != occupation_map.end(); ++it) { - it->second.get_left_turns(it->first, m_turns); + it->second.get_left_turns(it->first, m_turns, m_side_strategy); } } @@ -699,7 +716,7 @@ struct buffered_piece_collection ++it) { piece& pc = *it; - if (geometry::area(pc.robust_ring) < 0) + if (geometry::area(pc.robust_ring, m_robust_area_strategy) < 0) { // Rings can be ccw: // - in a concave piece @@ -1220,14 +1237,9 @@ struct buffered_piece_collection inline void enrich() { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy_type; - enrich_intersection_points(m_turns, m_clusters, offsetted_rings, offsetted_rings, - m_robust_policy, side_strategy_type()); + m_robust_policy, m_side_strategy); } // Discards all rings which do have not-OK intersection points only. @@ -1314,7 +1326,7 @@ struct buffered_piece_collection buffered_ring& ring = *it; if (! ring.has_intersections() && boost::size(ring) > 0u - && geometry::area(ring) < 0) + && geometry::area(ring, m_area_strategy) < 0) { if (! point_coveredby_original(geometry::range::front(ring))) { @@ -1358,12 +1370,14 @@ struct buffered_piece_collection overlay_buffer, backtrack_for_buffer > traverser; + std::map turn_info_per_ring; traversed_rings.clear(); buffer_overlay_visitor visitor; traverser::apply(offsetted_rings, offsetted_rings, m_intersection_strategy, m_robust_policy, m_turns, traversed_rings, + turn_info_per_ring, m_clusters, visitor); } @@ -1391,7 +1405,7 @@ struct buffered_piece_collection template inline OutputIterator assign(OutputIterator out) const { - typedef detail::overlay::ring_properties properties; + typedef detail::overlay::ring_properties properties; std::map selected; @@ -1407,7 +1421,7 @@ struct buffered_piece_collection if (! it->has_intersections() && ! it->is_untouched_outside_original) { - properties p = properties(*it); + properties p = properties(*it, m_area_strategy); if (p.valid) { ring_identifier id(0, index, -1); @@ -1423,7 +1437,7 @@ struct buffered_piece_collection it != boost::end(traversed_rings); ++it, ++index) { - properties p = properties(*it); + properties p = properties(*it, m_area_strategy); if (p.valid) { ring_identifier id(2, index, -1); @@ -1431,7 +1445,10 @@ struct buffered_piece_collection } } - detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected, m_intersection_strategy, true); + // Assign parents, checking orientation but NOT discarding double + // negative rings (negative child with negative parent) + detail::overlay::assign_parents(offsetted_rings, traversed_rings, + selected, m_intersection_strategy, true, false); return detail::overlay::add_rings(selected, offsetted_rings, traversed_rings, out); } diff --git a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp index 94dfddd44..5c012e715 100644 --- a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp @@ -127,26 +127,31 @@ class piece_turn_visitor template inline void move_begin_iterator(Iterator& it_begin, Iterator it_beyond, - signed_size_type& index, int dir, Box const& other_bounding_box) + signed_size_type& index, int dir, + Box const& this_bounding_box, + Box const& other_bounding_box) { for(; it_begin != it_beyond && it_begin + 1 != it_beyond && detail::section::preceding(dir, *(it_begin + 1), - other_bounding_box, m_robust_policy); + this_bounding_box, + other_bounding_box, + m_robust_policy); ++it_begin, index++) {} } template inline void move_end_iterator(Iterator it_begin, Iterator& it_beyond, - int dir, Box const& other_bounding_box) + int dir, Box const& this_bounding_box, + Box const& other_bounding_box) { while (it_beyond != it_begin && it_beyond - 1 != it_begin && it_beyond - 2 != it_begin) { if (detail::section::exceeding(dir, *(it_beyond - 2), - other_bounding_box, m_robust_policy)) + this_bounding_box, other_bounding_box, m_robust_policy)) { --it_beyond; } @@ -192,23 +197,23 @@ class piece_turn_visitor // Set begin/end of monotonic ranges, in both x/y directions signed_size_type index1 = sec1_first_index; move_begin_iterator<0>(it1_first, it1_beyond, index1, - section1.directions[0], section2.bounding_box); + section1.directions[0], section1.bounding_box, section2.bounding_box); move_end_iterator<0>(it1_first, it1_beyond, - section1.directions[0], section2.bounding_box); + section1.directions[0], section1.bounding_box, section2.bounding_box); move_begin_iterator<1>(it1_first, it1_beyond, index1, - section1.directions[1], section2.bounding_box); + section1.directions[1], section1.bounding_box, section2.bounding_box); move_end_iterator<1>(it1_first, it1_beyond, - section1.directions[1], section2.bounding_box); + section1.directions[1], section1.bounding_box, section2.bounding_box); signed_size_type index2 = sec2_first_index; move_begin_iterator<0>(it2_first, it2_beyond, index2, - section2.directions[0], section1.bounding_box); + section2.directions[0], section2.bounding_box, section1.bounding_box); move_end_iterator<0>(it2_first, it2_beyond, - section2.directions[0], section1.bounding_box); + section2.directions[0], section2.bounding_box, section1.bounding_box); move_begin_iterator<1>(it2_first, it2_beyond, index2, - section2.directions[1], section1.bounding_box); + section2.directions[1], section2.bounding_box, section1.bounding_box); move_end_iterator<1>(it2_first, it2_beyond, - section2.directions[1], section1.bounding_box); + section2.directions[1], section2.bounding_box, section1.bounding_box); turn_type the_model; the_model.operations[0].piece_index = piece1.index; diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index 26d53ab4e..3a7d3d878 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -2,10 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017 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 @@ -17,14 +18,22 @@ #include #include +#include +#include + +#include + namespace boost { namespace geometry { + #ifndef DOXYGEN_NO_DETAIL namespace detail { + +// TODO: remove template inline int sign_of_difference(Point1 const& point1, Point2 const& point2) { @@ -37,6 +46,193 @@ inline int sign_of_difference(Point1 const& point1, Point2 const& point2) } +template ::type> +struct direction_code_impl +{ + BOOST_MPL_ASSERT_MSG((false), NOT_IMPLEMENTED_FOR_THIS_CS, (CSTag)); +}; + +template +struct direction_code_impl +{ + template + static inline int apply(Point1 const& segment_a, Point1 const& segment_b, + Point2 const& p) + { + typedef typename geometry::select_coordinate_type + < + Point1, Point2 + >::type calc_t; + + if ( (math::equals(geometry::get<0>(segment_b), geometry::get<0>(segment_a)) + && math::equals(geometry::get<1>(segment_b), geometry::get<1>(segment_a))) + || (math::equals(geometry::get<0>(segment_b), geometry::get<0>(p)) + && math::equals(geometry::get<1>(segment_b), geometry::get<1>(p))) ) + { + return 0; + } + + calc_t x1 = geometry::get<0>(segment_b) - geometry::get<0>(segment_a); + calc_t y1 = geometry::get<1>(segment_b) - geometry::get<1>(segment_a); + calc_t x2 = geometry::get<0>(segment_b) - geometry::get<0>(p); + calc_t y2 = geometry::get<1>(segment_b) - geometry::get<1>(p); + + calc_t ax = (std::min)(math::abs(x1), math::abs(x2)); + calc_t ay = (std::min)(math::abs(y1), math::abs(y2)); + + int s1 = 0, s2 = 0; + if (ax >= ay) + { + s1 = x1 > 0 ? 1 : -1; + s2 = x2 > 0 ? 1 : -1; + } + else + { + s1 = y1 > 0 ? 1 : -1; + s2 = y2 > 0 ? 1 : -1; + } + + return s1 == s2 ? -1 : 1; + } +}; + +template +struct direction_code_impl +{ + template + static inline int apply(Point1 const& segment_a, Point1 const& segment_b, + Point2 const& p) + { + typedef typename coordinate_type::type coord1_t; + typedef typename coordinate_type::type coord2_t; + typedef typename coordinate_system::type::units units_t; + typedef typename coordinate_system::type::units units2_t; + BOOST_MPL_ASSERT_MSG((boost::is_same::value), + NOT_IMPLEMENTED_FOR_DIFFERENT_UNITS, + (units_t, units2_t)); + + typedef typename geometry::select_coordinate_type ::type calc_t; + typedef math::detail::constants_on_spheroid constants1; + typedef math::detail::constants_on_spheroid constants2; + static coord1_t const pi_half1 = constants1::max_latitude(); + static coord2_t const pi_half2 = constants2::max_latitude(); + static calc_t const c0 = 0; + + coord1_t const a0 = geometry::get<0>(segment_a); + coord1_t const a1 = geometry::get<1>(segment_a); + coord1_t const b0 = geometry::get<0>(segment_b); + coord1_t const b1 = geometry::get<1>(segment_b); + coord2_t const p0 = geometry::get<0>(p); + coord2_t const p1 = geometry::get<1>(p); + + if ( (math::equals(b0, a0) && math::equals(b1, a1)) + || (math::equals(b0, p0) && math::equals(b1, p1)) ) + { + return 0; + } + + bool const is_a_pole = math::equals(pi_half1, math::abs(a1)); + bool const is_b_pole = math::equals(pi_half1, math::abs(b1)); + bool const is_p_pole = math::equals(pi_half2, math::abs(p1)); + + if ( is_b_pole && ((is_a_pole && math::sign(b1) == math::sign(a1)) + || (is_p_pole && math::sign(b1) == math::sign(p1))) ) + { + return 0; + } + + // NOTE: as opposed to the implementation for cartesian CS + // here point b is the origin + + calc_t const dlon1 = math::longitude_distance_signed(b0, a0); + calc_t const dlon2 = math::longitude_distance_signed(b0, p0); + + bool is_antilon1 = false, is_antilon2 = false; + calc_t const dlat1 = latitude_distance_signed(b1, a1, dlon1, is_antilon1); + calc_t const dlat2 = latitude_distance_signed(b1, p1, dlon2, is_antilon2); + + calc_t mx = is_a_pole || is_b_pole || is_p_pole ? + c0 : + (std::min)(is_antilon1 ? c0 : math::abs(dlon1), + is_antilon2 ? c0 : math::abs(dlon2)); + calc_t my = (std::min)(math::abs(dlat1), + math::abs(dlat2)); + + int s1 = 0, s2 = 0; + if (mx >= my) + { + s1 = dlon1 > 0 ? 1 : -1; + s2 = dlon2 > 0 ? 1 : -1; + } + else + { + s1 = dlat1 > 0 ? 1 : -1; + s2 = dlat2 > 0 ? 1 : -1; + } + + return s1 == s2 ? -1 : 1; + } + + template + static inline T latitude_distance_signed(T const& lat1, T const& lat2, T const& lon_ds, bool & is_antilon) + { + typedef math::detail::constants_on_spheroid constants; + static T const pi = constants::half_period(); + static T const c0 = 0; + + T res = lat2 - lat1; + + is_antilon = math::equals(math::abs(lon_ds), pi); + if (is_antilon) + { + res = lat2 + lat1; + if (res >= c0) + res = pi - res; + else + res = -pi - res; + } + + return res; + } +}; + +template +struct direction_code_impl +{ + template + static inline int apply(Point1 segment_a, Point1 segment_b, + Point2 p) + { + typedef math::detail::constants_on_spheroid + < + typename coordinate_type::type, + typename coordinate_system::type::units + > constants1; + typedef math::detail::constants_on_spheroid + < + typename coordinate_type::type, + typename coordinate_system::type::units + > constants2; + + geometry::set<1>(segment_a, + constants1::max_latitude() - geometry::get<1>(segment_a)); + geometry::set<1>(segment_b, + constants1::max_latitude() - geometry::get<1>(segment_b)); + geometry::set<1>(p, + constants2::max_latitude() - geometry::get<1>(p)); + + return direction_code_impl + < + Point, spherical_equatorial_tag + >::apply(segment_a, segment_b, p); + } +}; + +template +struct direction_code_impl + : direction_code_impl +{}; + // Gives sense of direction for point p, collinear w.r.t. segment (a,b) // Returns -1 if p goes backward w.r.t (a,b), so goes from b in direction of a // Returns 1 if p goes forward, so extends (a,b) @@ -44,28 +240,9 @@ inline int sign_of_difference(Point1 const& point1, Point2 const& point2) // Note that it does not do any collinearity test, that should be done before template inline int direction_code(Point1 const& segment_a, Point1 const& segment_b, - const Point2& p) + Point2 const& p) { - // Suppose segment = (4 3,4 4) and p =(4 2) - // Then sign_a1 = 1 and sign_p1 = 1 -> goes backward -> return -1 - - int const sign_a0 = sign_of_difference<0>(segment_b, segment_a); - int const sign_a1 = sign_of_difference<1>(segment_b, segment_a); - - if (sign_a0 == 0 && sign_a1 == 0) - { - return 0; - } - - int const sign_p0 = sign_of_difference<0>(segment_b, p); - int const sign_p1 = sign_of_difference<1>(segment_b, p); - - if (sign_p0 == 0 && sign_p1 == 0) - { - return 0; - } - - return sign_a0 == sign_p0 && sign_a1 == sign_p1 ? -1 : 1; + return direction_code_impl::apply(segment_a, segment_b, p); } @@ -73,7 +250,6 @@ inline int direction_code(Point1 const& segment_a, Point1 const& segment_b, #endif //DOXYGEN_NO_DETAIL - }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECITON_CODE_HPP diff --git a/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp b/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp index f830f8161..87618939b 100644 --- a/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp +++ b/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp @@ -121,9 +121,18 @@ struct box_box // calculate positive longitude translation with b1_min as origin calc_t const diff_min = math::longitude_distance_unsigned(b1_min, b2_min); calc_t const b2_min_transl = b1_min + diff_min; // always right of b1_min + calc_t b2_max_transl = b2_min_transl - constants::period() + diff2; - if (b2_min_transl > b1_max // b2_min right of b1_max - && b2_min_transl - constants::period() + diff2 < b1_min) // b2_max left of b1_min + // if the translation is too close then use the original point + // note that math::abs(b2_max_transl - b2_max) takes values very + // close to k*2*constants::period() for k=0,1,2,... + if (math::abs(b2_max_transl - b2_max) < constants::period() / 2) + { + b2_max_transl = b2_max; + } + + if (b2_min_transl > b1_max // b2_min right of b1_max + && b2_max_transl < b1_min) // b2_max left of b1_min { return true; } diff --git a/include/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp b/include/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp index f8d3e3c59..d7aa9089e 100644 --- a/include/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp @@ -36,10 +36,11 @@ #include #include #include -#include #include +#include + namespace boost { namespace geometry { @@ -56,10 +57,10 @@ class multipoint_multipoint private: template class unary_disjoint_predicate - : detail::relate::less + : geometry::less<> { private: - typedef detail::relate::less base_type; + typedef geometry::less<> base_type; public: unary_disjoint_predicate(Iterator first, Iterator last) @@ -90,7 +91,7 @@ public: std::vector points1(boost::begin(multipoint1), boost::end(multipoint1)); - std::sort(points1.begin(), points1.end(), detail::relate::less()); + std::sort(points1.begin(), points1.end(), geometry::less<>()); typedef unary_disjoint_predicate < diff --git a/include/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp b/include/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp index ab5de3d9b..9596799cb 100644 --- a/include/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp @@ -391,7 +391,7 @@ struct distance template struct distance < - Point, Polygon, Strategy, point_tag, polygon_tag, + Point, Polygon, Strategy, point_tag, polygon_tag, strategy_tag_distance_point_segment, false > : detail::distance::point_to_polygon < diff --git a/include/boost/geometry/algorithms/detail/envelope/box.hpp b/include/boost/geometry/algorithms/detail/envelope/box.hpp index 795f51392..33b43da25 100644 --- a/include/boost/geometry/algorithms/detail/envelope/box.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/box.hpp @@ -4,11 +4,12 @@ // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -146,12 +147,18 @@ namespace dispatch { -template -struct envelope +template +struct envelope : detail::envelope::envelope_box {}; +template +struct envelope + : detail::envelope::envelope_box_on_spheroid +{}; + + template struct envelope : detail::envelope::envelope_box_on_spheroid diff --git a/include/boost/geometry/algorithms/detail/envelope/multipoint.hpp b/include/boost/geometry/algorithms/detail/envelope/multipoint.hpp index efee4701c..eef756379 100644 --- a/include/boost/geometry/algorithms/detail/envelope/multipoint.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/multipoint.hpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015-2016, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -336,8 +337,6 @@ public: { detail::expand::point_loop < - strategy::compare::default_strategy, - strategy::compare::default_strategy, 2, dimension::value >::apply(mbr, *it, strategy); } diff --git a/include/boost/geometry/algorithms/detail/envelope/point.hpp b/include/boost/geometry/algorithms/detail/envelope/point.hpp index ee0559bf5..86e73f3aa 100644 --- a/include/boost/geometry/algorithms/detail/envelope/point.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/point.hpp @@ -4,11 +4,12 @@ // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2015, 2016. +// This file was modified by Oracle on 2015, 2016, 2017. // Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -102,12 +103,18 @@ namespace dispatch { -template -struct envelope +template +struct envelope : detail::envelope::envelope_one_point<0, dimension::value> {}; +template +struct envelope + : detail::envelope::envelope_point_on_spheroid +{}; + + template struct envelope : detail::envelope::envelope_point_on_spheroid diff --git a/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp b/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp index f61fc422d..9b9e2f85d 100644 --- a/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015-2016, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -194,8 +195,6 @@ struct envelope_range_of_boxes_by_expansion { detail::expand::indexed_loop < - strategy::compare::default_strategy, - strategy::compare::default_strategy, min_corner, Dimension, DimensionCount @@ -203,8 +202,6 @@ struct envelope_range_of_boxes_by_expansion detail::expand::indexed_loop < - strategy::compare::default_strategy, - strategy::compare::default_strategy, max_corner, Dimension, DimensionCount @@ -243,9 +240,15 @@ struct envelope_range_of_boxes RangeOfBoxes const >::type iterator_type; + static const bool is_equatorial = ! boost::is_same + < + typename cs_tag::type, + spherical_polar_tag + >::value; + typedef math::detail::constants_on_spheroid < - coordinate_type, units_type + coordinate_type, units_type, is_equatorial > constants; typedef longitude_interval interval_type; diff --git a/include/boost/geometry/algorithms/detail/envelope/segment.hpp b/include/boost/geometry/algorithms/detail/envelope/segment.hpp index 7e3719496..f635fa611 100644 --- a/include/boost/geometry/algorithms/detail/envelope/segment.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/segment.hpp @@ -80,6 +80,35 @@ struct envelope_segment_call_vertex_latitude } }; +template +struct envelope_segment_convert_polar +{ + template + static inline void pre(T & , T & ) {} + + template + static inline void post(T & , T & ) {} +}; + +template +struct envelope_segment_convert_polar +{ + template + static inline void pre(T & lat1, T & lat2) + { + lat1 = math::latitude_convert_ep(lat1); + lat2 = math::latitude_convert_ep(lat2); + } + + template + static inline void post(T & lat1, T & lat2) + { + lat1 = math::latitude_convert_ep(lat1); + lat2 = math::latitude_convert_ep(lat2); + std::swap(lat1, lat2); + } +}; + template class envelope_segment_impl { @@ -266,29 +295,29 @@ private: Box, box_coordinate_type, Units >::type helper_box_type; - helper_box_type radian_mbr; + helper_box_type helper_mbr; geometry::set < min_corner, 0 - >(radian_mbr, boost::numeric_cast(lon1)); + >(helper_mbr, boost::numeric_cast(lon1)); geometry::set < min_corner, 1 - >(radian_mbr, boost::numeric_cast(lat1)); + >(helper_mbr, boost::numeric_cast(lat1)); geometry::set < max_corner, 0 - >(radian_mbr, boost::numeric_cast(lon2)); + >(helper_mbr, boost::numeric_cast(lon2)); geometry::set < max_corner, 1 - >(radian_mbr, boost::numeric_cast(lat2)); + >(helper_mbr, boost::numeric_cast(lat2)); - transform_units(radian_mbr, mbr); + transform_units(helper_mbr, mbr); } @@ -347,7 +376,14 @@ public: Box& mbr, Strategy const& strategy) { + typedef envelope_segment_convert_polar::type> convert_polar; + + convert_polar::pre(lat1, lat2); + apply(lon1, lat1, lon2, lat2, strategy); + + convert_polar::post(lat1, lat2); + create_box(lon1, lat1, lon2, lat2, mbr); } @@ -366,7 +402,14 @@ public: Strategy const& strategy, CalculationType alp1) { + typedef envelope_segment_convert_polar::type> convert_polar; + + convert_polar::pre(lat1, lat2); + apply(lon1, lat1, lon2, lat2, strategy, alp1); + + convert_polar::post(lat1, lat2); + create_box(lon1, lat1, lon2, lat2, mbr); } }; @@ -383,8 +426,6 @@ struct envelope_one_segment envelope_one_point::apply(p1, mbr, strategy); detail::expand::point_loop < - strategy::compare::default_strategy, - strategy::compare::default_strategy, Dimension, DimensionCount >::apply(mbr, p2, strategy); @@ -409,13 +450,14 @@ struct envelope_segment envelope_one_segment<2, DimensionCount>::apply(p1, p2, mbr, strategy); } - template - static inline void apply(Segment const& segment, Box& mbr) + template + static inline void apply(Segment const& segment, Box& mbr, + Strategy const& strategy) { typename point_type::type p[2]; detail::assign_point_from_index<0>(segment, p[0]); detail::assign_point_from_index<1>(segment, p[1]); - apply(p[0], p[1], mbr); + apply(p[0], p[1], mbr, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/expand/box.hpp b/include/boost/geometry/algorithms/detail/expand/box.hpp index 3edb23f5a..485f4d25e 100644 --- a/include/boost/geometry/algorithms/detail/expand/box.hpp +++ b/include/boost/geometry/algorithms/detail/expand/box.hpp @@ -5,11 +5,12 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -73,48 +74,54 @@ namespace dispatch template < typename BoxOut, typename BoxIn, - typename StrategyLess, typename StrategyGreater, typename CSTagOut, typename CSTag > struct expand < BoxOut, BoxIn, - StrategyLess, StrategyGreater, box_tag, box_tag, CSTagOut, CSTag - > : detail::expand::expand_indexed - < - 0, dimension::value, StrategyLess, StrategyGreater - > + > { - BOOST_MPL_ASSERT_MSG((boost::is_same::value), - COORDINATE_SYSTEMS_MUST_BE_THE_SAME, + BOOST_MPL_ASSERT_MSG((false), + NOT_IMPLEMENTED_FOR_THESE_COORDINATE_SYSTEMS, (types())); }; -template -< - typename BoxOut, typename BoxIn, - typename StrategyLess, typename StrategyGreater -> +template +struct expand + < + BoxOut, BoxIn, + box_tag, box_tag, + cartesian_tag, cartesian_tag + > : detail::expand::expand_indexed + < + 0, dimension::value + > +{}; + +template struct expand < BoxOut, BoxIn, - StrategyLess, StrategyGreater, box_tag, box_tag, spherical_equatorial_tag, spherical_equatorial_tag > : detail::expand::box_on_spheroid {}; -template -< - typename BoxOut, typename BoxIn, - typename StrategyLess, typename StrategyGreater -> +template +struct expand + < + BoxOut, BoxIn, + box_tag, box_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::expand::box_on_spheroid +{}; + +template struct expand < BoxOut, BoxIn, - StrategyLess, StrategyGreater, box_tag, box_tag, geographic_tag, geographic_tag > : detail::expand::box_on_spheroid diff --git a/include/boost/geometry/algorithms/detail/expand/indexed.hpp b/include/boost/geometry/algorithms/detail/expand/indexed.hpp index 28cf0e2e4..fe7ee4f78 100644 --- a/include/boost/geometry/algorithms/detail/expand/indexed.hpp +++ b/include/boost/geometry/algorithms/detail/expand/indexed.hpp @@ -5,11 +5,12 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // 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. @@ -22,15 +23,13 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXPAND_INDEXED_HPP #include +#include #include #include #include -#include -#include - #include @@ -44,7 +43,6 @@ namespace detail { namespace expand template < - typename StrategyLess, typename StrategyGreater, std::size_t Index, std::size_t Dimension, std::size_t DimensionCount > @@ -53,27 +51,17 @@ struct indexed_loop template static inline void apply(Box& box, Geometry const& source, Strategy const& strategy) { - typedef typename strategy::compare::detail::select_strategy - < - StrategyLess, 1, Box, Dimension - >::type less_type; - - typedef typename strategy::compare::detail::select_strategy - < - StrategyGreater, -1, Box, Dimension - >::type greater_type; - typedef typename select_coordinate_type < Box, Geometry >::type coordinate_type; - less_type less; - greater_type greater; - coordinate_type const coord = get(source); + std::less less; + std::greater greater; + if (less(coord, get(box))) { set(box, coord); @@ -86,21 +74,15 @@ struct indexed_loop indexed_loop < - StrategyLess, StrategyGreater, Index, Dimension + 1, DimensionCount >::apply(box, source, strategy); } }; -template -< - typename StrategyLess, typename StrategyGreater, - std::size_t Index, std::size_t DimensionCount -> +template struct indexed_loop < - StrategyLess, StrategyGreater, Index, DimensionCount, DimensionCount > { @@ -111,11 +93,7 @@ struct indexed_loop // Changes a box such that the other box is also contained by the box -template -< - std::size_t Dimension, std::size_t DimensionCount, - typename StrategyLess, typename StrategyGreater -> +template struct expand_indexed { template @@ -125,13 +103,11 @@ struct expand_indexed { indexed_loop < - StrategyLess, StrategyGreater, 0, Dimension, DimensionCount >::apply(box, geometry, strategy); indexed_loop < - StrategyLess, StrategyGreater, 1, Dimension, DimensionCount >::apply(box, geometry, strategy); } diff --git a/include/boost/geometry/algorithms/detail/expand/point.hpp b/include/boost/geometry/algorithms/detail/expand/point.hpp index f0cbd1db0..2d8b0feff 100644 --- a/include/boost/geometry/algorithms/detail/expand/point.hpp +++ b/include/boost/geometry/algorithms/detail/expand/point.hpp @@ -5,11 +5,12 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // 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. @@ -23,6 +24,7 @@ #include #include +#include #include #include @@ -36,9 +38,6 @@ #include #include -#include -#include - #include #include @@ -53,33 +52,19 @@ namespace detail { namespace expand { -template -< - typename StrategyLess, typename StrategyGreater, - std::size_t Dimension, std::size_t DimensionCount -> +template struct point_loop { template static inline void apply(Box& box, Point const& source, Strategy const& strategy) { - typedef typename strategy::compare::detail::select_strategy - < - StrategyLess, 1, Point, Dimension - >::type less_type; - - typedef typename strategy::compare::detail::select_strategy - < - StrategyGreater, -1, Point, Dimension - >::type greater_type; - typedef typename select_coordinate_type < Point, Box >::type coordinate_type; - less_type less; - greater_type greater; + std::less less; + std::greater greater; coordinate_type const coord = get(source); @@ -93,37 +78,21 @@ struct point_loop set(box, coord); } - point_loop - < - StrategyLess, StrategyGreater, Dimension + 1, DimensionCount - >::apply(box, source, strategy); + point_loop::apply(box, source, strategy); } }; -template -< - typename StrategyLess, - typename StrategyGreater, - std::size_t DimensionCount -> -struct point_loop - < - StrategyLess, StrategyGreater, DimensionCount, DimensionCount - > +template +struct point_loop { template static inline void apply(Box&, Point const&, Strategy const&) {} }; -// implementation for the spherical equatorial and geographic coordinate systems -template -< - typename StrategyLess, - typename StrategyGreater, - std::size_t DimensionCount -> +// implementation for the spherical and geographic coordinate systems +template struct point_loop_on_spheroid { template @@ -133,11 +102,12 @@ struct point_loop_on_spheroid { typedef typename point_type::type box_point_type; typedef typename coordinate_type::type box_coordinate_type; + typedef typename coordinate_system::type::units units_type; typedef math::detail::constants_on_spheroid < box_coordinate_type, - typename coordinate_system::type::units + units_type > constants; // normalize input point and input box @@ -157,7 +127,7 @@ struct point_loop_on_spheroid b_lon_max = geometry::get(box), b_lat_max = geometry::get(box); - if (math::equals(math::abs(p_lat), constants::max_latitude())) + if (math::is_latitude_pole(p_lat)) { // the point of expansion is the either the north or the // south pole; the only important coordinate here is the @@ -169,7 +139,7 @@ struct point_loop_on_spheroid } if (math::equals(b_lat_min, b_lat_max) - && math::equals(math::abs(b_lat_min), constants::max_latitude())) + && math::is_latitude_pole(b_lat_min)) { // the box degenerates to either the north or the south pole; // the only important coordinate here is the pole's latitude, @@ -228,7 +198,7 @@ struct point_loop_on_spheroid point_loop < - StrategyLess, StrategyGreater, 2, DimensionCount + 2, DimensionCount >::apply(box, point, strategy); } }; @@ -246,56 +216,70 @@ namespace dispatch template < typename BoxOut, typename Point, - typename StrategyLess, typename StrategyGreater, typename CSTagOut, typename CSTag > struct expand < BoxOut, Point, - StrategyLess, StrategyGreater, box_tag, point_tag, CSTagOut, CSTag - > : detail::expand::point_loop - < - StrategyLess, StrategyGreater, 0, dimension::value - > + > { - BOOST_MPL_ASSERT_MSG((boost::is_same::value), - COORDINATE_SYSTEMS_MUST_BE_THE_SAME, + BOOST_MPL_ASSERT_MSG((false), + NOT_IMPLEMENTED_FOR_THESE_COORDINATE_SYSTEMS, (types())); }; -template -< - typename BoxOut, typename Point, - typename StrategyLess, typename StrategyGreater -> + +template +struct expand + < + BoxOut, Point, + box_tag, point_tag, + cartesian_tag, cartesian_tag + > : detail::expand::point_loop + < + 0, dimension::value + > +{}; + +template struct expand < BoxOut, Point, - StrategyLess, StrategyGreater, box_tag, point_tag, spherical_equatorial_tag, spherical_equatorial_tag > : detail::expand::point_loop_on_spheroid < - StrategyLess, StrategyGreater, dimension::value + dimension::value + > +{}; + +template +struct expand + < + BoxOut, Point, + box_tag, point_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::expand::point_loop_on_spheroid + < + dimension::value, + false > {}; template < - typename BoxOut, typename Point, - typename StrategyLess, typename StrategyGreater + typename BoxOut, typename Point > struct expand < BoxOut, Point, - StrategyLess, StrategyGreater, box_tag, point_tag, geographic_tag, geographic_tag > : detail::expand::point_loop_on_spheroid < - StrategyLess, StrategyGreater, dimension::value + dimension::value > {}; diff --git a/include/boost/geometry/algorithms/detail/expand/segment.hpp b/include/boost/geometry/algorithms/detail/expand/segment.hpp index 0570e944d..dddd3d2c7 100644 --- a/include/boost/geometry/algorithms/detail/expand/segment.hpp +++ b/include/boost/geometry/algorithms/detail/expand/segment.hpp @@ -5,11 +5,12 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -77,48 +78,57 @@ namespace dispatch template < typename Box, typename Segment, - typename StrategyLess, typename StrategyGreater, typename CSTagOut, typename CSTag > struct expand < Box, Segment, - StrategyLess, StrategyGreater, box_tag, segment_tag, CSTagOut, CSTag - > : detail::expand::expand_indexed - < - 0, dimension::value, StrategyLess, StrategyGreater - > + > { - BOOST_MPL_ASSERT_MSG((boost::is_same::value), - COORDINATE_SYSTEMS_MUST_BE_THE_SAME, + BOOST_MPL_ASSERT_MSG((false), + NOT_IMPLEMENTED_FOR_THESE_COORDINATE_SYSTEMS, (types())); }; template < - typename Box, typename Segment, - typename StrategyLess, typename StrategyGreater + typename Box, typename Segment > struct expand < Box, Segment, - StrategyLess, StrategyGreater, + box_tag, segment_tag, + cartesian_tag, cartesian_tag + > : detail::expand::expand_indexed + < + 0, dimension::value + > +{}; + +template +struct expand + < + Box, Segment, + box_tag, segment_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::expand::segment +{}; + +template +struct expand + < + Box, Segment, box_tag, segment_tag, spherical_equatorial_tag, spherical_equatorial_tag > : detail::expand::segment {}; -template -< - typename Box, typename Segment, - typename StrategyLess, typename StrategyGreater -> +template struct expand < Box, Segment, - StrategyLess, StrategyGreater, box_tag, segment_tag, geographic_tag, geographic_tag > : detail::expand::segment diff --git a/include/boost/geometry/algorithms/detail/extreme_points.hpp b/include/boost/geometry/algorithms/detail/extreme_points.hpp index 65795cd05..61e984ee3 100644 --- a/include/boost/geometry/algorithms/detail/extreme_points.hpp +++ b/include/boost/geometry/algorithms/detail/extreme_points.hpp @@ -5,6 +5,11 @@ // Copyright (c) 2009-2013 Mateusz Loskot, London, UK. // Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -117,12 +122,6 @@ struct extreme_points_on_ring 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, @@ -214,10 +213,11 @@ struct extreme_points_on_ring return true; } - template + template static inline void get_intruders(Ring const& ring, CirclingIterator left, CirclingIterator right, Extremes const& extremes, - Intruders& intruders) + Intruders& intruders, + SideStrategy const& strategy) { if (boost::size(extremes) < 3) { @@ -238,8 +238,8 @@ struct extreme_points_on_ring 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; + int const first_side = strategy.apply(*right, extremes.front(), *(extremes.begin() + 1)) * factor; + int const last_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) @@ -263,10 +263,11 @@ struct extreme_points_on_ring } } - template + template static inline void get_intruders(Ring const& ring, Extremes const& extremes, - Intruders& intruders) + Intruders& intruders, + SideStrategy const& strategy) { std::size_t const n = boost::size(ring); if (n >= 3) @@ -275,12 +276,12 @@ struct extreme_points_on_ring geometry::ever_circling_range_iterator right(ring); ++right; - get_intruders(ring, left, right, extremes, intruders); + get_intruders(ring, left, right, extremes, intruders, strategy); } } - template - static inline bool right_turn(Ring const& ring, Iterator it) + template + static inline bool right_turn(Ring const& ring, Iterator it, SideStrategy const& strategy) { typename std::iterator_traits::difference_type const index = std::distance(boost::begin(ring), it); @@ -295,8 +296,8 @@ struct extreme_points_on_ring } 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; + int const first_side = strategy.apply(*(right - 1), *right, *left) * factor; + int const last_side = strategy.apply(*left, *(left + 1), *right) * factor; //std::cout << "Candidate at " << geometry::wkt(*it) << " first=" << first_side << " last=" << last_side << std::endl; @@ -306,8 +307,11 @@ struct extreme_points_on_ring // 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) + template + static inline bool apply(Ring const& ring, + Extremes& extremes, + Intruders& intruders, + SideStrategy const& strategy) { std::size_t const n = boost::size(ring); if (n < 3) @@ -321,7 +325,7 @@ struct extreme_points_on_ring compare smaller; for (range_iterator it = max_it + 1; it != boost::end(ring); ++it) { - if (smaller(*max_it, *it) && right_turn(ring, it)) + if (smaller(*max_it, *it) && right_turn(ring, it, strategy)) { max_it = it; } @@ -365,7 +369,7 @@ struct extreme_points_on_ring std::copy(points.begin(), points.end(), std::back_inserter(extremes)); - get_intruders(ring, left, right, extremes, intruders); + get_intruders(ring, left, right, extremes, intruders, strategy); return true; } @@ -403,8 +407,9 @@ struct extreme_points template struct extreme_points { - template - static inline bool apply(Polygon const& polygon, Extremes& extremes, Intruders& intruders) + template + static inline bool apply(Polygon const& polygon, Extremes& extremes, Intruders& intruders, + SideStrategy const& strategy) { typedef typename geometry::ring_type::type ring_type; typedef detail::extreme_points::extreme_points_on_ring @@ -412,7 +417,8 @@ struct extreme_points ring_type, Dimension > ring_implementation; - if (! ring_implementation::apply(geometry::exterior_ring(polygon), extremes, intruders)) + if (! ring_implementation::apply(geometry::exterior_ring(polygon), + extremes, intruders, strategy)) { return false; } @@ -423,7 +429,7 @@ struct extreme_points for (typename detail::interior_iterator::type it = boost::begin(rings); it != boost::end(rings); ++it) { - ring_implementation::get_intruders(*it, extremes, intruders); + ring_implementation::get_intruders(*it, extremes, intruders, strategy); } return true; @@ -433,8 +439,9 @@ struct extreme_points template struct extreme_points { - template - static inline bool apply(Box const& box, Extremes& extremes, Intruders& ) + template + static inline bool apply(Box const& box, Extremes& extremes, Intruders& , + SideStrategy const& ) { extremes.resize(4); geometry::detail::assign_box_corners_oriented(box, extremes); @@ -446,8 +453,9 @@ struct extreme_points template struct extreme_points { - template - static inline bool apply(Box const& box, Extremes& extremes, Intruders& ) + template + static inline bool apply(Box const& box, Extremes& extremes, Intruders& , + SideStrategy const& ) { extremes.resize(4); geometry::detail::assign_box_corners_oriented(box, extremes); @@ -460,8 +468,9 @@ struct extreme_points template struct extreme_points { - template - static inline bool apply(MultiPolygon const& multi, Extremes& extremes, Intruders& intruders) + template + static inline bool apply(MultiPolygon const& multi, Extremes& extremes, + Intruders& intruders, SideStrategy const& strategy) { // 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 @@ -473,7 +482,7 @@ struct extreme_points typename boost::range_value::type, Dimension, polygon_tag - >::apply(*boost::begin(multi), extremes, intruders); + >::apply(*boost::begin(multi), extremes, intruders, strategy); } return false; @@ -489,8 +498,18 @@ struct extreme_points 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) +template +< + std::size_t Edge, + typename Geometry, + typename Extremes, + typename Intruders, + typename SideStrategy +> +inline bool extreme_points(Geometry const& geometry, + Extremes& extremes, + Intruders& intruders, + SideStrategy const& strategy) { concepts::check(); @@ -509,7 +528,11 @@ inline bool extreme_points(Geometry const& geometry, Extremes& extremes, Intrude const >(); - return dispatch::extreme_points::apply(geometry, extremes, intruders); + return dispatch::extreme_points + < + Geometry, + Edge + >::apply(geometry, extremes, intruders, strategy); } diff --git a/include/boost/geometry/algorithms/detail/get_left_turns.hpp b/include/boost/geometry/algorithms/detail/get_left_turns.hpp index 95ab98c23..e9f6a5085 100644 --- a/include/boost/geometry/algorithms/detail/get_left_turns.hpp +++ b/include/boost/geometry/algorithms/detail/get_left_turns.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, 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) @@ -60,17 +65,14 @@ inline int squared_length(Vector const& vector) } -template +template struct angle_less { typedef Point vector_type; - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy_type; - angle_less(Point const& origin) + angle_less(Point const& origin, SideStrategy const& strategy) : m_origin(origin) + , m_strategy(strategy) {} template @@ -89,8 +91,7 @@ struct angle_less 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); + int const side = m_strategy.apply(m_origin, q.point, p.point); if (side != 0) { return side == 1; @@ -114,19 +115,17 @@ struct angle_less private: Point m_origin; + SideStrategy m_strategy; }; -template +template struct angle_equal_to { 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) + + inline angle_equal_to(Point const& origin, SideStrategy const& strategy) : m_origin(origin) + , m_strategy(strategy) {} template @@ -143,13 +142,13 @@ struct angle_equal_to return false; } // Same quadrant, check if p/q are collinear - int const side = side_strategy_type::apply(m_origin, q.point, - p.point); + int const side = m_strategy.apply(m_origin, q.point, p.point); return side == 0; } private: Point m_origin; + SideStrategy m_strategy; }; template @@ -193,13 +192,14 @@ inline void get_left_turns(AngleCollection const& sorted_angles, //! Returns the number of clusters -template -inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin) +template +inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin, + SideStrategy const& strategy) { // Assign same cluster_index for all turns in same direction BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u); - angle_equal_to comparator(origin); + angle_equal_to comparator(origin, strategy); typename boost::range_iterator::type it = sorted.begin(); std::size_t cluster_index = 0; diff --git a/include/boost/geometry/algorithms/detail/has_self_intersections.hpp b/include/boost/geometry/algorithms/detail/has_self_intersections.hpp index c34bb217a..1289d946a 100644 --- a/include/boost/geometry/algorithms/detail/has_self_intersections.hpp +++ b/include/boost/geometry/algorithms/detail/has_self_intersections.hpp @@ -81,7 +81,11 @@ inline bool has_self_intersections(Geometry const& geometry, std::deque turns; detail::disjoint::disjoint_interrupt_policy policy; - detail::self_get_turn_points::self_turns(geometry, strategy, robust_policy, turns, policy); + detail::self_get_turn_points::self_turns + < + false, + detail::overlay::assign_null_policy + >(geometry, strategy, robust_policy, turns, policy, 0, false); #ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS bool first = true; diff --git a/include/boost/geometry/algorithms/detail/intersection/multi.hpp b/include/boost/geometry/algorithms/detail/intersection/multi.hpp index 88b49b016..92ce89bb2 100644 --- a/include/boost/geometry/algorithms/detail/intersection/multi.hpp +++ b/include/boost/geometry/algorithms/detail/intersection/multi.hpp @@ -237,7 +237,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, multi_linestring_tag, multi_linestring_tag, point_tag, - false, false, false + linear_tag, linear_tag, pointlike_tag > : detail::intersection::intersection_multi_linestring_multi_linestring_point < GeometryOut @@ -259,7 +259,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, linestring_tag, multi_linestring_tag, point_tag, - false, false, false + linear_tag, linear_tag, pointlike_tag > : detail::intersection::intersection_linestring_multi_linestring_point < GeometryOut @@ -281,7 +281,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, multi_linestring_tag, box_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::clip_multi_linestring < GeometryOut @@ -303,7 +303,7 @@ struct intersection_insert OverlayType, ReverseLinestring, ReverseMultiPolygon, ReverseOut, linestring_tag, multi_polygon_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_linestring_with_areal < ReverseMultiPolygon, @@ -329,7 +329,7 @@ struct intersection_insert OverlayType, ReversePolygon, ReverseMultiLinestring, ReverseOut, polygon_tag, multi_linestring_tag, linestring_tag, - true, false, false + areal_tag, linear_tag, linear_tag > : detail::intersection::intersection_of_areal_with_multi_linestring < ReversePolygon, @@ -353,7 +353,7 @@ struct intersection_insert OverlayType, ReverseMultiLinestring, ReverseRing, ReverseOut, multi_linestring_tag, ring_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_multi_linestring_with_areal < ReverseRing, @@ -376,7 +376,7 @@ struct intersection_insert OverlayType, ReverseMultiLinestring, ReverseRing, ReverseOut, multi_linestring_tag, polygon_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_multi_linestring_with_areal < ReverseRing, @@ -401,7 +401,7 @@ struct intersection_insert OverlayType, ReverseMultiLinestring, ReverseMultiPolygon, ReverseOut, multi_linestring_tag, multi_polygon_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_multi_linestring_with_areal < ReverseMultiPolygon, diff --git a/include/boost/geometry/algorithms/detail/intersects/implementation.hpp b/include/boost/geometry/algorithms/detail/intersects/implementation.hpp index 2379168e8..f9b5402d0 100644 --- a/include/boost/geometry/algorithms/detail/intersects/implementation.hpp +++ b/include/boost/geometry/algorithms/detail/intersects/implementation.hpp @@ -72,10 +72,11 @@ struct self_intersects rescale_policy_type robust_policy; detail::disjoint::disjoint_interrupt_policy policy; + // TODO: skip_adjacent should be set to false detail::self_get_turn_points::get_turns < false, turn_policy - >::apply(geometry, strategy, robust_policy, turns, policy, 0); + >::apply(geometry, strategy, robust_policy, turns, policy, 0, true); return policy.has_intersections; } }; diff --git a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp index 5852ed071..e36049a96 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -217,6 +217,7 @@ inline bool has_self_intersections(Linear const& linear, Strategy const& strateg is_acceptable_turn > interrupt_policy(predicate); + // TODO: skip_adjacent should be set to false detail::self_get_turn_points::get_turns < false, turn_policy @@ -224,7 +225,7 @@ inline bool has_self_intersections(Linear const& linear, Strategy const& strateg strategy, detail::no_rescale_policy(), turns, - interrupt_policy, 0); + interrupt_policy, 0, true); detail::is_valid::debug_print_turns(turns.begin(), turns.end()); debug_print_boundary_points(linear); @@ -236,7 +237,9 @@ inline bool has_self_intersections(Linear const& linear, Strategy const& strateg template struct is_simple_linestring { - static inline bool apply(Linestring const& linestring) + template + static inline bool apply(Linestring const& linestring, + Strategy const& strategy) { simplicity_failure_policy policy; return ! boost::empty(linestring) @@ -247,7 +250,7 @@ struct is_simple_linestring && ! detail::is_valid::has_spikes < Linestring, closed - >::apply(linestring, policy); + >::apply(linestring, policy, strategy.get_side_strategy()); } }; @@ -258,7 +261,10 @@ struct is_simple_linestring static inline bool apply(Linestring const& linestring, Strategy const& strategy) { - return is_simple_linestring::apply(linestring) + return is_simple_linestring + < + Linestring, false + >::apply(linestring, strategy) && ! has_self_intersections(linestring, strategy); } }; @@ -267,23 +273,44 @@ struct is_simple_linestring template struct is_simple_multilinestring { +private: + template + struct per_linestring + { + per_linestring(Strategy const& strategy) + : m_strategy(strategy) + {} + + template + inline bool apply(Linestring const& linestring) const + { + return detail::is_simple::is_simple_linestring + < + Linestring, + false // do not compute self-intersections + >::apply(linestring, m_strategy); + } + + Strategy const& m_strategy; + }; + +public: template static inline bool apply(MultiLinestring const& multilinestring, Strategy const& strategy) { + typedef per_linestring per_ls; + // check each of the linestrings for simplicity // but do not compute self-intersections yet; these will be // computed for the entire multilinestring if ( ! detail::check_iterator_range < - is_simple_linestring - < - typename boost::range_value::type, - false // do not compute self-intersections - >, + per_ls, // do not compute self-intersections true // allow empty multilinestring >::apply(boost::begin(multilinestring), - boost::end(multilinestring)) + boost::end(multilinestring), + per_ls(strategy)) ) { return false; diff --git a/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp b/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp index aa90e52db..96efec79c 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, 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 // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -91,8 +92,9 @@ struct has_spikes return std::find_if(second, last, not_equal(*first)); } - template - static inline bool apply(Range const& range, VisitPolicy& visitor) + template + static inline bool apply(Range const& range, VisitPolicy& visitor, + SideStrategy const& strategy) { boost::ignore_unused(visitor); @@ -124,9 +126,8 @@ struct has_spikes while (next != boost::end(view)) { - if ( geometry::detail::point_is_spike_or_equal(*prev, - *next, - *cur) ) + if ( geometry::detail::point_is_spike_or_equal(*prev, *next, *cur, + strategy) ) { return ! visitor.template apply(is_linear, *cur); @@ -146,7 +147,7 @@ struct has_spikes boost::rend(view)); iterator next = find_different_from_first(cur, boost::end(view)); - if (detail::point_is_spike_or_equal(*prev, *next, *cur)) + if (detail::point_is_spike_or_equal(*prev, *next, *cur, strategy)) { return ! visitor.template apply(is_linear, *cur); diff --git a/include/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp b/include/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp index bac541219..fccc0ffdb 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp @@ -125,34 +125,6 @@ private: typedef typename boost::range_value::type polygon; typedef is_acceptable_turn base; - template - static inline - bool check_int_ext(Operation const& op1, - detail::overlay::operation_type optype1, - Operation const& op2, - detail::overlay::operation_type optype2) - { - // u/i is acceptable for touch of interior ring with another exterior ring - // (but only if there is a colocated uu-turn of its exterior, TODO) - return op1.seg_id.ring_index == -1 - && op2.seg_id.ring_index >= 0 - && op1.operation == optype1 - && op2.operation == optype2; - } - - template - static inline - bool check_int_int(Operation const& op1, - Operation const& op2, - detail::overlay::operation_type optype) - { - // i/i is acceptable for touching interior/interior rings - return op1.seg_id.ring_index >= 0 - && op2.seg_id.ring_index >= 0 - && op1.operation == optype - && op2.operation == optype; - } - public: template static inline bool apply(Turn const& turn) @@ -172,26 +144,11 @@ public: return true; } - if (turn.method != method_touch) - { - return false; - } - - operation_type const reverse_op - = op == operation_union - ? operation_intersection - : operation_union; - - if ( check_int_int(turn.operations[0], turn.operations[1], reverse_op) - || check_int_ext(turn.operations[0], reverse_op, - turn.operations[1], op) - || check_int_ext(turn.operations[1], reverse_op, - turn.operations[0], op)) - { - return true; - } - - return false; + // Turn is acceptable only in case of a touch(interior) and both lines + // (polygons) do not cross + return (turn.method == method_touch + || turn.method == method_touch_interior) + && turn.touch_only; } }; diff --git a/include/boost/geometry/algorithms/detail/is_valid/linear.hpp b/include/boost/geometry/algorithms/detail/is_valid/linear.hpp index 6bc6b86cf..39cb36ef5 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/linear.hpp @@ -43,9 +43,10 @@ namespace detail { namespace is_valid template struct is_valid_linestring { - template + template static inline bool apply(Linestring const& linestring, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { if (has_invalid_coordinate::apply(linestring, visitor)) { @@ -75,15 +76,12 @@ struct is_valid_linestring { return visitor.template apply(); } - return ! has_spikes::apply(linestring, visitor); - } - template - static inline bool apply(Linestring const& linestring, - VisitPolicy& visitor, - Strategy const&) - { - return apply(linestring, visitor); + return ! has_spikes + < + Linestring, closed + >::apply(linestring, visitor, + strategy.get_side_strategy()); } }; @@ -132,10 +130,13 @@ class is_valid > { private: - template + template struct per_linestring { - per_linestring(VisitPolicy& policy) : m_policy(policy) {} + per_linestring(VisitPolicy& policy, Strategy const& strategy) + : m_policy(policy) + , m_strategy(strategy) + {} template inline bool apply(Linestring const& linestring) const @@ -143,17 +144,18 @@ private: return detail::is_valid::is_valid_linestring < Linestring - >::apply(linestring, m_policy); + >::apply(linestring, m_policy, m_strategy); } VisitPolicy& m_policy; + Strategy const& m_strategy; }; public: template static inline bool apply(MultiLinestring const& multilinestring, VisitPolicy& visitor, - Strategy const&) + Strategy const& strategy) { if (BOOST_GEOMETRY_CONDITION( AllowEmptyMultiGeometries && boost::empty(multilinestring))) @@ -161,13 +163,15 @@ public: return visitor.template apply(); } + typedef per_linestring per_ls; + return detail::check_iterator_range < - per_linestring, + per_ls, false // do not check for empty multilinestring (done above) >::apply(boost::begin(multilinestring), boost::end(multilinestring), - per_linestring(visitor)); + per_ls(visitor, strategy)); } }; diff --git a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp index caacda8a3..ed24b1381 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp @@ -89,12 +89,15 @@ private: { boost::ignore_unused(visitor); - // collect all polygons that have turns + // collect all polygons that have crossing 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); + if (! tit->touch_only) + { + multi_indices.insert(tit->operations[0].seg_id.multi_index); + multi_indices.insert(tit->operations[1].seg_id.multi_index); + } } typedef geometry::model::box::type> box_type; diff --git a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp index 7c8a5d58c..5c6229b79 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -216,14 +217,26 @@ protected: , m_strategy(strategy) {} + template + inline bool is_within(Item const& first, Item const& second) + { + typename point_type::type point; + typedef detail::point_on_border::point_on_range pob; + + // TODO: this should check for a point on the interior, instead + // of on border. Or it should check using the overlap function. + + return pob::apply(point, points_begin(first), points_end(first)) + && geometry::within(point, second, m_strategy); + } + template inline bool apply(partition_item const& item1, partition_item const& item2) { if (! items_overlap - && (geometry::within(*points_begin(*item1.get()), *item2.get(), m_strategy) - || geometry::within(*points_begin(*item2.get()), *item1.get(), m_strategy)) - ) + && (is_within(*item1.get(), *item2.get()) + || is_within(*item2.get(), *item1.get()))) { items_overlap = true; return false; // interrupt diff --git a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp index 9ab68fdc4..0b9595043 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp @@ -115,7 +115,10 @@ struct is_properly_oriented geometry::closure::value > ring_area_type; - typedef typename default_area_result::type area_result_type; + typedef typename Strategy::template area_strategy + < + point_type + >::type::return_type area_result_type; typename ring_area_predicate < @@ -195,7 +198,7 @@ struct is_valid_ring return is_topologically_closed::apply(ring, visitor) && ! has_duplicates::apply(ring, visitor) - && ! has_spikes::apply(ring, visitor) + && ! has_spikes::apply(ring, visitor, strategy.get_side_strategy()) && (! CheckSelfIntersections || has_valid_self_turns::apply(ring, visitor, strategy)) && is_properly_oriented::apply(ring, visitor, strategy); diff --git a/include/boost/geometry/algorithms/detail/multi_modify.hpp b/include/boost/geometry/algorithms/detail/multi_modify.hpp index f0b9ddd3e..23187f932 100644 --- a/include/boost/geometry/algorithms/detail/multi_modify.hpp +++ b/include/boost/geometry/algorithms/detail/multi_modify.hpp @@ -4,6 +4,10 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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. @@ -40,6 +44,18 @@ struct multi_modify Policy::apply(*it); } } + + template + static inline void apply(MultiGeometry& multi, Strategy const& strategy) + { + typedef typename boost::range_iterator::type iterator_type; + for (iterator_type it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + Policy::apply(*it, strategy); + } + } }; diff --git a/include/boost/geometry/algorithms/detail/normalize.hpp b/include/boost/geometry/algorithms/detail/normalize.hpp index 913fe324b..7a761d42b 100644 --- a/include/boost/geometry/algorithms/detail/normalize.hpp +++ b/include/boost/geometry/algorithms/detail/normalize.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, 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 // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -120,7 +121,7 @@ struct assign_loop<1, DimensionCount> }; -template +template struct normalize_point { static inline void apply(PointIn const& point_in, PointOut& point_out) @@ -133,6 +134,7 @@ struct normalize_point math::normalize_spheroidal_coordinates < typename coordinate_system::type::units, + IsEquatorial, in_coordinate_type >(longitude, latitude); @@ -144,7 +146,7 @@ struct normalize_point }; -template +template class normalize_box { template @@ -193,6 +195,7 @@ public: math::normalize_spheroidal_box_coordinates < typename coordinate_system::type::units, + IsEquatorial, in_coordinate_type >(lon_min, lat_min, lon_max, lat_max); @@ -234,6 +237,15 @@ struct normalize {}; +template +struct normalize + < + PointIn, PointOut, point_tag, point_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::normalization::normalize_point +{}; + + template struct normalize < @@ -251,6 +263,15 @@ struct normalize {}; +template +struct normalize + < + BoxIn, BoxOut, box_tag, box_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::normalization::normalize_box +{}; + + template struct normalize < diff --git a/include/boost/geometry/algorithms/detail/occupation_info.hpp b/include/boost/geometry/algorithms/detail/occupation_info.hpp index 4048d59d7..fc74f0cc7 100644 --- a/include/boost/geometry/algorithms/detail/occupation_info.hpp +++ b/include/boost/geometry/algorithms/detail/occupation_info.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, 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) @@ -99,12 +104,18 @@ public : } } - template - inline void get_left_turns(RobustPoint const& origin, Turns& turns) + template + inline void get_left_turns(RobustPoint const& origin, Turns& turns, + SideStrategy const& strategy) { + typedef detail::left_turns::angle_less + < + typename AngleInfo::point_type, + SideStrategy + > angle_less; + // Sort on angle - std::sort(m_angles.begin(), m_angles.end(), - detail::left_turns::angle_less(origin)); + std::sort(m_angles.begin(), m_angles.end(), angle_less(origin, strategy)); // Group same-angled elements std::size_t cluster_size = detail::left_turns::assign_cluster_indices(m_angles, origin); diff --git a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp index 106ecaad0..3f2aea1b1 100644 --- a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp @@ -25,7 +25,7 @@ struct ring_with_direction ring_identifier ring_id; direction_type direction; - std::size_t turn_index; + signed_size_type turn_index; int operation_index; operation_type operation; signed_size_type region_id; @@ -50,8 +50,12 @@ struct ring_with_direction struct rank_with_rings { + // Define a set having a ring, with its direction (from/to). Each ring + // arrive at / leaves a cluster only once. TODO: this is not true for + // invalid ring. The rank needs to be considered too. + typedef std::set container_type; std::size_t rank; - std::set rings; + container_type rings; rank_with_rings() : rank(0) @@ -60,7 +64,7 @@ struct rank_with_rings inline bool all_equal(direction_type dir_type) const { - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { if (it->direction != dir_type) @@ -83,7 +87,7 @@ struct rank_with_rings inline bool has_only(operation_type op) const { - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -100,7 +104,7 @@ struct rank_with_rings { bool has1 = false; bool has2 = false; - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -114,7 +118,7 @@ struct rank_with_rings inline bool is_isolated() const { - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -128,8 +132,8 @@ struct rank_with_rings inline bool has_unique_region_id() const { - int region_id = -1; - for (std::set::const_iterator it = rings.begin(); + signed_size_type region_id = -1; + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -145,10 +149,10 @@ struct rank_with_rings return true; } - inline int region_id() const + inline signed_size_type region_id() const { - int region_id = -1; - for (std::set::const_iterator it = rings.begin(); + signed_size_type region_id = -1; + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -170,7 +174,7 @@ struct rank_with_rings typedef typename boost::range_value::type turn_type; typedef typename turn_type::turn_operation_type turn_operation_type; - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; diff --git a/include/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp b/include/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp index 03c06c28d..fb7384079 100644 --- a/include/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp @@ -2,8 +2,8 @@ // 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. +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -63,8 +63,9 @@ inline bool points_equal_or_close(Point1 const& point1, } -template +template inline void append_no_dups_or_spikes(Range& range, Point const& point, + SideStrategy const& strategy, RobustPolicy const& robust_policy) { #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION @@ -92,6 +93,7 @@ inline void append_no_dups_or_spikes(Range& range, Point const& point, && point_is_spike_or_equal(point, *(boost::end(range) - 3), *(boost::end(range) - 2), + strategy, robust_policy)) { // Use the Concept/traits, so resize and append again @@ -100,8 +102,9 @@ inline void append_no_dups_or_spikes(Range& range, Point const& point, } } -template +template inline void clean_closing_dups_and_spikes(Range& range, + SideStrategy const& strategy, RobustPolicy const& robust_policy) { std::size_t const minsize @@ -135,7 +138,7 @@ inline void clean_closing_dups_and_spikes(Range& range, // 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)) + if (point_is_spike_or_equal(*second, *ultimate, *first, strategy, robust_policy)) { range::erase(range, first); if (BOOST_GEOMETRY_CONDITION(closed)) diff --git a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index e8bb2c1ac..c8ce65100 100644 --- a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -107,21 +107,19 @@ static inline bool within_selected_input(Item const& item2, } -template +template struct ring_info_helper { - typedef typename geometry::default_area_result::type area_type; - ring_identifier id; - area_type real_area; - area_type abs_area; + AreaType real_area; + AreaType abs_area; model::box envelope; inline ring_info_helper() : real_area(0), abs_area(0) {} - inline ring_info_helper(ring_identifier i, area_type a) + inline ring_info_helper(ring_identifier i, AreaType const& a) : id(i), real_area(a), abs_area(geometry::math::abs(a)) {} }; @@ -226,7 +224,8 @@ inline void assign_parents(Geometry1 const& geometry1, RingCollection const& collection, RingMap& ring_map, Strategy const& strategy, - bool check_for_orientation = false) + bool check_for_orientation = false, + bool discard_double_negative = false) { typedef typename geometry::tag::type tag1; typedef typename geometry::tag::type tag2; @@ -234,11 +233,15 @@ inline void assign_parents(Geometry1 const& geometry1, typedef typename RingMap::mapped_type ring_info_type; typedef typename ring_info_type::point_type point_type; typedef model::box box_type; + typedef typename Strategy::template area_strategy + < + point_type + >::type::return_type area_result_type; typedef typename RingMap::iterator map_iterator_type; { - typedef ring_info_helper helper; + typedef ring_info_helper helper; typedef std::vector vector_type; typedef typename boost::range_iterator::type vector_iterator_type; @@ -332,28 +335,38 @@ inline void assign_parents(Geometry1 const& geometry1, for (map_iterator_type it = boost::begin(ring_map); it != boost::end(ring_map); ++it) { - if (geometry::math::equals(it->second.get_area(), 0)) + ring_info_type& info = it->second; + if (geometry::math::equals(info.get_area(), 0)) { - it->second.discarded = true; + info.discarded = true; } - else if (it->second.parent.source_index >= 0 - && math::larger(it->second.get_area(), 0)) + else if (info.parent.source_index >= 0) { - const ring_info_type& parent = ring_map[it->second.parent]; + const ring_info_type& parent = ring_map[info.parent]; + bool const pos = math::larger(info.get_area(), 0); + bool const parent_pos = math::larger(parent.area, 0); - if (math::larger(parent.area, 0)) + bool const double_neg = discard_double_negative && ! pos && ! parent_pos; + + if ((pos && parent_pos) || double_neg) { // Discard positive inner ring with positive parent - it->second.discarded = true; + // Also, for some cases (dissolve), negative inner ring + // with negative parent shouild be discarded + info.discarded = true; + } + + if (pos || info.discarded) + { + // Remove parent ID from any positive or discarded inner rings + info.parent.source_index = -1; } - // Remove parent ID from any positive inner ring - it->second.parent.source_index = -1; } - else if (it->second.parent.source_index < 0 - && math::smaller(it->second.get_area(), 0)) + else if (info.parent.source_index < 0 + && math::smaller(info.get_area(), 0)) { // Reverse negative ring without parent - it->second.reversed = true; + info.reversed = true; } } } @@ -370,7 +383,7 @@ inline void assign_parents(Geometry1 const& geometry1, } -// Version for one geometry (called by buffer) +// Version for one geometry (called by buffer/dissolve) template < typename Geometry, @@ -382,13 +395,15 @@ inline void assign_parents(Geometry const& geometry, RingCollection const& collection, RingMap& ring_map, Strategy const& strategy, - bool check_for_orientation) + bool check_for_orientation, + bool discard_double_negative) { // Call it with an empty geometry as second geometry (source_id == 1) // (ring_map should be empty for source_id==1) Geometry empty; - assign_parents(geometry, empty, collection, ring_map, strategy, check_for_orientation); + assign_parents(geometry, empty, collection, ring_map, strategy, + check_for_orientation, discard_double_negative); } diff --git a/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp b/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp index a056d4fa3..c6f540a97 100644 --- a/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp @@ -2,8 +2,8 @@ // 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. +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 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 @@ -57,12 +57,14 @@ struct copy_segments_ring < typename Ring, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(Ring const& ring, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -109,7 +111,7 @@ struct copy_segments_ring for (signed_size_type i = 0; i < count; ++i, ++it) { - detail::overlay::append_no_dups_or_spikes(current_output, *it, robust_policy); + detail::overlay::append_no_dups_or_spikes(current_output, *it, strategy, robust_policy); } } }; @@ -119,20 +121,23 @@ class copy_segments_linestring { private: // remove spikes - template + template static inline void append_to_output(RangeOut& current_output, Point const& point, + SideStrategy const& strategy, RobustPolicy const& robust_policy, boost::true_type const&) { detail::overlay::append_no_dups_or_spikes(current_output, point, + strategy, robust_policy); } // keep spikes - template + template static inline void append_to_output(RangeOut& current_output, Point const& point, + SideStrategy const&, RobustPolicy const&, boost::false_type const&) { @@ -144,12 +149,14 @@ public: < typename LineString, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(LineString const& ls, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -170,7 +177,7 @@ public: for (signed_size_type i = 0; i < count; ++i, ++it) { - append_to_output(current_output, *it, robust_policy, + append_to_output(current_output, *it, strategy, robust_policy, boost::integral_constant()); } } @@ -183,12 +190,14 @@ struct copy_segments_polygon < typename Polygon, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(Polygon const& polygon, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -199,6 +208,7 @@ struct copy_segments_polygon ? geometry::exterior_ring(polygon) : range::at(geometry::interior_rings(polygon), seg_id.ring_index), seg_id, to_index, + strategy, robust_policy, current_output ); @@ -213,12 +223,14 @@ struct copy_segments_box < typename Box, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(Box const& box, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -239,7 +251,7 @@ struct copy_segments_box for (signed_size_type i = 0; i < count; i++, index++) { detail::overlay::append_no_dups_or_spikes(current_output, - bp[index % 5], robust_policy); + bp[index % 5], strategy, robust_policy); } } @@ -253,12 +265,14 @@ struct copy_segments_multi < typename MultiGeometry, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(MultiGeometry const& multi_geometry, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -272,6 +286,7 @@ struct copy_segments_multi // Call the single-version Policy::apply(range::at(multi_geometry, seg_id.multi_index), seg_id, to_index, + strategy, robust_policy, current_output); } @@ -341,12 +356,14 @@ template bool Reverse, typename Geometry, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > inline void copy_segments(Geometry const& geometry, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& range_out) { @@ -356,7 +373,7 @@ inline void copy_segments(Geometry const& geometry, < typename tag::type, Reverse - >::apply(geometry, seg_id, to_index, robust_policy, range_out); + >::apply(geometry, seg_id, to_index, strategy, robust_policy, range_out); } diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index c7b464cfa..e25445651 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -59,7 +64,7 @@ template typename Turns, typename Geometry1, typename Geometry2, typename RobustPolicy, - typename Strategy + typename SideStrategy > inline void enrich_sort(Operations& operations, Turns const& turns, @@ -67,7 +72,7 @@ inline void enrich_sort(Operations& operations, Geometry1 const& geometry1, Geometry2 const& geometry2, RobustPolicy const& robust_policy, - Strategy const& /*strategy*/) + SideStrategy const& strategy) { std::sort(boost::begin(operations), boost::end(operations), @@ -77,8 +82,9 @@ inline void enrich_sort(Operations& operations, typename boost::range_value::type, Geometry1, Geometry2, RobustPolicy, + SideStrategy, Reverse1, Reverse2 - >(turns, for_operation, geometry1, geometry2, robust_policy)); + >(turns, for_operation, geometry1, geometry2, robust_policy, strategy)); } @@ -112,7 +118,7 @@ inline void enrich_assign(Operations& operations, Turns& turns) // Cluster behaviour: next should point after cluster, unless // their seg_ids are not the same - while (turn.cluster_id != -1 + while (turn.is_clustered() && it->turn_index != next->turn_index && turn.cluster_id == turns[next->turn_index].cluster_id && op.seg_id == turns[next->turn_index].operations[next->operation_index].seg_id) @@ -239,10 +245,6 @@ inline void calculate_remaining_distance(Turns& turns) ++it) { turn_type& turn = *it; - if (! turn.both(detail::overlay::operation_continue)) - { - continue; - } op_type& op0 = turn.operations[0]; op_type& op1 = turn.operations[1]; @@ -255,7 +257,7 @@ inline void calculate_remaining_distance(Turns& turns) int const to_index0 = op0.enriched.get_next_turn_index(); int const to_index1 = op1.enriched.get_next_turn_index(); - if (to_index1 >= 0 + if (to_index0 >= 0 && to_index1 >= 0 && to_index0 != to_index1) { @@ -279,7 +281,7 @@ inline void calculate_remaining_distance(Turns& turns) \tparam Clusters type of cluster container \tparam Geometry1 \tparam_geometry \tparam Geometry2 \tparam_geometry -\tparam Strategy side strategy type +\tparam SideStrategy side strategy type \param turns container containing intersection points \param clusters container containing clusters \param geometry1 \param_geometry @@ -295,13 +297,13 @@ template typename Clusters, typename Geometry1, typename Geometry2, typename RobustPolicy, - typename Strategy + typename SideStrategy > inline void enrich_intersection_points(Turns& turns, Clusters& clusters, Geometry1 const& geometry1, Geometry2 const& geometry2, RobustPolicy const& robust_policy, - Strategy const& strategy) + SideStrategy const& strategy) { static const detail::overlay::operation_type target_operation = detail::overlay::operation_from_overlay::value; @@ -353,12 +355,11 @@ inline void enrich_intersection_points(Turns& turns, } if (detail::overlay::is_self_turn(turn) - && turn.cluster_id < 0 + && ! turn.is_clustered() && ! turn.both(target_operation)) { // Only keep self-uu-turns or self-ii-turns turn.discarded = true; - turn.cluster_id = -1; continue; } @@ -373,12 +374,12 @@ inline void enrich_intersection_points(Turns& turns, < OverlayType, target_operation - >::apply(turns, geometry1, geometry2); + >::apply(turns, clusters, geometry1, geometry2); detail::overlay::discard_open_turns < OverlayType, target_operation - >::apply(turns, geometry1, geometry2); + >::apply(turns, clusters, geometry1, geometry2); // Create a map of vectors of indexed operation-types to be able // to sort intersection points PER RING @@ -415,6 +416,9 @@ inline void enrich_intersection_points(Turns& turns, detail::overlay::enrich_assign(mit->second, turns); } + // Check some specific type of self-turns (after getting enriched info) + detail::overlay::discard_self_turns_which_loop(turns); + if (has_colocations) { // First gather cluster properties (using even clusters with @@ -424,7 +428,8 @@ inline void enrich_intersection_points(Turns& turns, Reverse1, Reverse2, OverlayType - >(clusters, turns, target_operation, geometry1, geometry2); + >(clusters, turns, target_operation, + geometry1, geometry2, strategy); detail::overlay::cleanup_clusters(turns, clusters); } diff --git a/include/boost/geometry/algorithms/detail/overlay/follow.hpp b/include/boost/geometry/algorithms/detail/overlay/follow.hpp index e2de429af..4a5993ea3 100644 --- a/include/boost/geometry/algorithms/detail/overlay/follow.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -26,6 +26,7 @@ #include #include +#include namespace boost { namespace geometry @@ -157,6 +158,7 @@ struct action_selector typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void enter(LineStringOut& current_piece, @@ -164,6 +166,7 @@ struct action_selector segment_identifier& segment_id, signed_size_type , Point const& point, Operation const& operation, + SideStrategy const& , RobustPolicy const& , OutputIterator& ) { @@ -180,6 +183,7 @@ struct action_selector typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void leave(LineStringOut& current_piece, @@ -187,6 +191,7 @@ struct action_selector segment_identifier& segment_id, signed_size_type index, Point const& point, Operation const& , + SideStrategy const& strategy, RobustPolicy const& robust_policy, OutputIterator& out) { @@ -195,7 +200,7 @@ struct action_selector detail::copy_segments::copy_segments_linestring < false, RemoveSpikes - >::apply(linestring, segment_id, index, robust_policy, current_piece); + >::apply(linestring, segment_id, index, strategy, robust_policy, current_piece); detail::overlay::append_no_duplicates(current_piece, point); if (::boost::size(current_piece) > 1) { @@ -254,6 +259,7 @@ struct action_selector typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void enter(LineStringOut& current_piece, @@ -261,11 +267,12 @@ struct action_selector segment_identifier& segment_id, signed_size_type index, Point const& point, Operation const& operation, + SideStrategy const& strategy, RobustPolicy const& robust_policy, OutputIterator& out) { normal_action::leave(current_piece, linestring, segment_id, index, - point, operation, robust_policy, out); + point, operation, strategy, robust_policy, out); } template @@ -275,6 +282,7 @@ struct action_selector typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void leave(LineStringOut& current_piece, @@ -282,11 +290,12 @@ struct action_selector segment_identifier& segment_id, signed_size_type index, Point const& point, Operation const& operation, + SideStrategy const& strategy, RobustPolicy const& robust_policy, OutputIterator& out) { normal_action::enter(current_piece, linestring, segment_id, index, - point, operation, robust_policy, out); + point, operation, strategy, robust_policy, out); } template @@ -335,6 +344,7 @@ template class follow { +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR template struct sort_on_segment { @@ -381,7 +391,7 @@ class follow } }; - +#endif // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR public : @@ -425,7 +435,15 @@ public : // Sort intersection points on segments-along-linestring, and distance // (like in enrich is done for poly/poly) + // sort turns by Linear seg_id, then by fraction, then + // for same ring id: x, u, i, c + // for different ring id: c, i, u, x +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR std::sort(boost::begin(turns), boost::end(turns), sort_on_segment()); +#else + typedef relate::turns::less<0, relate::turns::less_op_linear_areal_single<0> > turn_less; + std::sort(boost::begin(turns), boost::end(turns), turn_less()); +#endif LineStringOut current_piece; geometry::segment_identifier current_segment_id(0, -1, -1, -1); @@ -456,7 +474,7 @@ public : entered = true; action::enter(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, - robust_policy, + strategy, robust_policy, out); } else if (following::is_leaving(*it, *iit, entered, first, linestring, polygon, pt_in_poly_strategy)) @@ -466,7 +484,7 @@ public : entered = false; action::leave(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, - robust_policy, + strategy, robust_policy, out); } first = false; @@ -480,7 +498,7 @@ public : >::apply(linestring, current_segment_id, static_cast(boost::size(linestring) - 1), - robust_policy, + strategy, robust_policy, current_piece); } diff --git a/include/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp b/include/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp index c249ff57f..2a374bf0b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp @@ -2,12 +2,14 @@ // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, 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 // 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 @@ -183,7 +185,8 @@ protected: typename TurnIterator, typename TurnOperationIterator, typename SegmentIdentifier, - typename OutputIterator + typename OutputIterator, + typename SideStrategy > static inline OutputIterator process_turn(TurnIterator it, @@ -193,7 +196,8 @@ protected: Linestring const& linestring, LinestringOut& current_piece, SegmentIdentifier& current_segment_id, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { // We don't rescale linear/linear detail::no_rescale_policy robust_policy; @@ -208,7 +212,7 @@ protected: action::enter(current_piece, linestring, current_segment_id, op_it->seg_id.segment_index, - it->point, *op_it, robust_policy, oit); + it->point, *op_it, strategy, robust_policy, oit); } ++enter_count; } @@ -223,7 +227,7 @@ protected: action::leave(current_piece, linestring, current_segment_id, op_it->seg_id.segment_index, - it->point, *op_it, robust_policy, oit); + it->point, *op_it, strategy, robust_policy, oit); } } else if ( FollowIsolatedPoints @@ -249,14 +253,16 @@ protected: template < typename SegmentIdentifier, - typename OutputIterator + typename OutputIterator, + typename SideStrategy > static inline OutputIterator process_end(bool entered, Linestring const& linestring, SegmentIdentifier const& current_segment_id, LinestringOut& current_piece, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { if ( action::is_entered(entered) ) { @@ -269,6 +275,7 @@ protected: >::apply(linestring, current_segment_id, static_cast(boost::size(linestring) - 1), + strategy, robust_policy, current_piece); } @@ -283,11 +290,12 @@ protected: } public: - template + template static inline OutputIterator apply(Linestring const& linestring, Linear const&, TurnIterator first, TurnIterator beyond, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { // Iterate through all intersection points (they are // ordered along the each line) @@ -304,7 +312,8 @@ public: entered, enter_count, linestring, current_piece, current_segment_id, - oit); + oit, + strategy); } #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) @@ -318,7 +327,8 @@ public: return process_end(entered, linestring, current_segment_id, current_piece, - oit); + oit, + strategy); } }; @@ -413,11 +423,12 @@ protected: }; public: - template + template static inline OutputIterator apply(MultiLinestring const& multilinestring, Linear const& linear, TurnIterator first, TurnIterator beyond, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { BOOST_GEOMETRY_ASSERT( first != beyond ); @@ -447,7 +458,7 @@ public: has_other_multi_id(current_multi_id)); oit = Base::apply(*(ls_first + current_multi_id), - linear, per_ls_current, per_ls_next, oit); + linear, per_ls_current, per_ls_next, oit, strategy); signed_size_type next_multi_id = -1; linestring_iterator ls_next = ls_beyond; diff --git a/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp b/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp index ea9aa29f1..2eec6af66 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -31,20 +36,15 @@ namespace detail { namespace overlay but we still need to know which comes first. Therefore, it is useful that using sides we are able to discover this. */ -template struct get_relative_order { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type strategy; - - template + template static inline int value_via_product(Point const& ti, Point const& tj, - Point const& ui, Point const& uj, int factor) + Point const& ui, Point const& uj, int factor, + SideStrategy const& strategy) { - int const side_ti_u = strategy::apply(ti, tj, ui); - int const side_tj_u = strategy::apply(ti, tj, uj); + int const side_ti_u = strategy.apply(ti, tj, ui); + int const side_tj_u = strategy.apply(ti, tj, uj); #ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER std::cout << (factor == 1 ? " r//s " : " s//r ") @@ -57,13 +57,15 @@ struct get_relative_order } + template static inline int apply( Point1 const& pi, Point1 const& pj, Point1 const& ri, Point1 const& rj, - Point1 const& si, Point1 const& sj) + Point1 const& si, Point1 const& sj, + SideStrategy const& strategy) { - int const side_ri_p = strategy::apply(pi, pj, ri); - int const side_si_p = strategy::apply(pi, pj, si); + int const side_ri_p = strategy.apply(pi, pj, ri); + int const side_si_p = strategy.apply(pi, pj, si); #ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER int const side_rj_p = strategy::apply(pi, pj, rj); @@ -72,10 +74,10 @@ struct get_relative_order std::cout << " s//p: " << side_si_p << " / " << side_sj_p; #endif - int value = value_via_product(si, sj, ri, rj, 1); + int value = value_via_product(si, sj, ri, rj, 1, strategy); if (value == 0) { - value = value_via_product(ri, rj, si, sj, -1); + value = value_via_product(ri, rj, si, sj, -1, strategy); } int const order = side_ri_p * side_ri_p * side_si_p * value; diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index 08bc34218..895952c8f 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -190,6 +190,7 @@ struct touch_interior : public base_turn_handler // Q turns left on the right side of P (test "MR3") // Both directions for "intersection" both(ti, operation_intersection); + ti.touch_only = true; } else if (side_qi_p == 1 && side_qk_p == 1 && side_qk_q == -1) { @@ -197,6 +198,7 @@ struct touch_interior : public base_turn_handler // Union: take both operation // Intersection: skip both(ti, operation_union); + ti.touch_only = true; } else if (side_qi_p == side_qk_p && side_qi_p == side_qk_q) { @@ -207,6 +209,7 @@ struct touch_interior : public base_turn_handler unsigned int index = side_qk_q == 1 ? index_q : index_p; ti.operations[index].operation = operation_union; ti.operations[1 - index].operation = operation_intersection; + ti.touch_only = true; } else if (side_qk_p == 0) { @@ -346,6 +349,7 @@ struct touch : public base_turn_handler if (side_pk_q2 == -side_qk_q) { ui_else_iu(! q_turns_left, ti); + ti.touch_only = true; return; } @@ -358,6 +362,10 @@ struct touch : public base_turn_handler { ti.operations[1].operation = operation_blocked; } + else + { + ti.touch_only = true; + } //block_second(block_q, ti); return; } @@ -373,6 +381,10 @@ struct touch : public base_turn_handler : side_qi_p1 == 1 || side_qk_p1 == 1 ? operation_union : operation_intersection; + if (! block_q) + { + ti.touch_only = true; + } return; } @@ -400,6 +412,7 @@ struct touch : public base_turn_handler if (side_pk_q1 == side_qk_p1) { uu_else_ii(right_to_left, ti); + ti.touch_only = true; return; } } @@ -418,6 +431,7 @@ struct touch : public base_turn_handler if (side_pk_q2 == side_qk_p1) { ui_else_iu(right_to_left, ti); + ti.touch_only = true; return; } } diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp index 5f2cb07fa..f8247cd24 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -66,7 +66,7 @@ struct side_calculator Qj const& m_qj; Qk const& m_qk; - SideStrategy const& m_side_strategy; + SideStrategy m_side_strategy; }; template diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 6b6458dd7..fd1e49ca2 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -144,7 +144,7 @@ class get_turns_in_sections template - static inline bool neighbouring(Section const& section, + static inline bool adjacent(Section const& section, signed_size_type index1, signed_size_type index2) { // About n-2: @@ -152,7 +152,7 @@ class get_turns_in_sections // -> 0-3 are adjacent, don't check on intersections) // Also tested for open polygons, and/or duplicates // About first condition: will be optimized by compiler (static) - // It checks if it is areal (box,ring,(multi)polygon + // It checks if it is areal (box, ring, (multi)polygon) signed_size_type const n = static_cast(section.range_count); boost::ignore_unused_variable_warning(n); @@ -180,7 +180,7 @@ public : 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, + bool skip_larger, bool skip_adjacent, IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Turns& turns, @@ -214,11 +214,6 @@ public : signed_size_type index1 = sec1.begin_index; signed_size_type ndi1 = sec1.non_duplicate_index; - bool const same_source = - source_id1 == source_id2 - && sec1.ring_id.multi_index == sec2.ring_id.multi_index - && sec1.ring_id.ring_index == sec2.ring_id.ring_index; - range1_iterator prev1, it1, end1; get_start_point_iterator(sec1, view1, prev1, it1, end1, @@ -233,7 +228,7 @@ public : // section 2: [--------------] // section 1: |----|---|---|---|---| for (prev1 = it1++, next1++; - it1 != end1 && ! detail::section::exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); + it1 != end1 && ! detail::section::exceeding<0>(dir1, *prev1, sec1.bounding_box, sec2.bounding_box, robust_policy); ++prev1, ++it1, ++index1, ++next1, ++ndi1) { ever_circling_iterator nd_next1( @@ -251,25 +246,35 @@ public : next2++; for (prev2 = it2++, next2++; - it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); + it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec2.bounding_box, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { - bool skip = same_source; - if (skip) + bool skip = false; + + if (source_id1 == source_id2 + && sec1.ring_id.multi_index == sec2.ring_id.multi_index + && sec1.ring_id.ring_index == sec2.ring_id.ring_index) { - // If sources are the same (possibly self-intersecting): - // skip if it is a neighbouring segment. - // (including first-last segment - // and two segments with one or more degenerate/duplicate - // (zero-length) segments in between) + // Sources and rings are the same - // Also skip if index1 < index2 to avoid getting all - // intersections twice (only do this on same source!) + if (skip_larger && index1 >= index2) + { + // Skip to avoid getting all intersections twice + skip = true; + } + else if (skip_adjacent) + { + // In some cases (dissolve, has_self_intersections) + // neighbouring segments should be checked + // (for example to detect spikes properly) - skip = (skip_larger && index1 >= index2) - || ndi2 == ndi1 + 1 - || neighbouring(sec1, index1, index2) - ; + // skip if it is a neighbouring segment. + // (including, for areas, first-last segment + // and two segments with one or more degenerate/duplicate + // (zero-length) segments in between) + skip = ndi2 == ndi1 + 1 + || adjacent(sec1, index1, index2); + } } if (! skip) @@ -359,7 +364,7 @@ private : // skips to the begin-point, we loose the index or have to recalculate it) // So we mimic it here template - static inline void get_start_point_iterator(Section & section, + static inline void get_start_point_iterator(Section const& section, Range const& range, typename boost::range_iterator::type& it, typename boost::range_iterator::type& prev, @@ -373,7 +378,7 @@ private : // Mimic section-iterator: // Skip to point such that section interects other box prev = it++; - for(; it != end && detail::section::preceding<0>(dir, *it, other_bounding_box, robust_policy); + for(; it != end && detail::section::preceding<0>(dir, *it, section.bounding_box, other_bounding_box, robust_policy); prev = it++, index++, ndi++) {} // Go back one step because we want to start completely preceding @@ -431,7 +436,7 @@ struct section_visitor TurnPolicy >::apply(m_source_id1, m_geometry1, sec1, m_source_id2, m_geometry2, sec2, - false, + false, false, m_intersection_strategy, m_rescale_policy, m_turns, m_interrupt_policy); diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 9c0f190eb..47656ab7d 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -19,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -176,6 +182,7 @@ inline signed_size_type add_turn_to_cluster(Turn const& turn, if (cid0 == -1 && cid1 == -1) { + // Because of this, first cluster ID will be 1 ++cluster_id; add_cluster_id(turn.operations[0], cluster_per_segment, cluster_id); add_cluster_id(turn.operations[1], cluster_per_segment, cluster_id); @@ -309,7 +316,7 @@ inline void assign_cluster_to_turns(Turns& turns, if (it != cluster_per_segment.end()) { #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS) - if (turn.cluster_id != -1 + if (turn.is_clustered() && turn.cluster_id != it->second) { std::cout << " CONFLICT " << std::endl; @@ -498,6 +505,7 @@ inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters) template < + overlay_type OverlayType, typename Turns, typename Clusters > @@ -512,38 +520,65 @@ inline void set_colocation(Turns& turns, Clusters const& clusters) cluster_info const& cinfo = cit->second; std::set const& ids = cinfo.turn_indices; - bool has_ii = false; - bool has_uu = false; + bool both_target = false; for (set_iterator it = ids.begin(); it != ids.end(); ++it) { turn_type const& turn = turns[*it]; - if (turn.both(operation_intersection)) + if (turn.both(operation_from_overlay::value)) { - has_ii = true; - } - if (turn.both(operation_union) || turn.combination(operation_union, operation_blocked)) - { - has_uu = true; + both_target = true; + break; } } - if (has_ii || has_uu) + + if (both_target) { for (set_iterator it = ids.begin(); it != ids.end(); ++it) { turn_type& turn = turns[*it]; - if (has_ii) + + if (both_target) { - turn.colocated_ii = true; - } - if (has_uu) - { - turn.colocated_uu = true; + turn.has_colocated_both = true; } } } } } +template +< + typename Turns, + typename Clusters +> +inline void check_colocation(bool& has_blocked, + int cluster_id, Turns const& turns, Clusters const& clusters) +{ + typedef typename boost::range_value::type turn_type; + + has_blocked = false; + + typename Clusters::const_iterator mit = clusters.find(cluster_id); + if (mit == clusters.end()) + { + return; + } + + cluster_info const& cinfo = mit->second; + + for (std::set::const_iterator it + = cinfo.turn_indices.begin(); + it != cinfo.turn_indices.end(); ++it) + { + turn_type const& turn = turns[*it]; + if (turn.any_blocked()) + { + has_blocked = true; + } + } +} + + // Checks colocated turns and flags combinations of uu/other, possibly a // combination of a ring touching another geometry's interior ring which is // tangential to the exterior ring @@ -622,6 +657,10 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters, > cluster_per_segment_type; cluster_per_segment_type cluster_per_segment; + + // Assign to zero, because of pre-increment later the cluster_id + // effectively starts with 1 + // (and can later be negated to use uniquely with turn_index) signed_size_type cluster_id = 0; for (typename map_type::const_iterator it = map.begin(); @@ -635,7 +674,9 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters, } assign_cluster_to_turns(turns, clusters, cluster_per_segment); - set_colocation(turns, clusters); + // Get colocated information here and not later, to keep information + // on turns which are discarded afterwards + set_colocation(turns, clusters); discard_interior_exterior_turns < do_reverse::value>::value != Reverse1, @@ -697,11 +738,13 @@ template typename Turns, typename Clusters, typename Geometry1, - typename Geometry2 + typename Geometry2, + typename SideStrategy > inline void gather_cluster_properties(Clusters& clusters, Turns& turns, operation_type for_operation, - Geometry1 const& geometry1, Geometry2 const& geometry2) + Geometry1 const& geometry1, Geometry2 const& geometry2, + SideStrategy const& strategy) { typedef typename boost::range_value::type turn_type; typedef typename turn_type::point_type point_type; @@ -711,7 +754,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, // right side typedef sort_by_side::side_sorter < - Reverse1, Reverse2, OverlayType, point_type, std::less + Reverse1, Reverse2, OverlayType, point_type, SideStrategy, std::less > sbs_type; for (typename Clusters::iterator mit = clusters.begin(); @@ -724,7 +767,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, continue; } - sbs_type sbs; + sbs_type sbs(strategy); point_type turn_point; // should be all the same for all turns in cluster bool first = true; @@ -751,7 +794,9 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, cinfo.open_count = sbs.open_count(for_operation); - // Unset the startable flag for all 'closed' zones + // Unset the startable flag for all 'closed' zones. This does not + // apply for self-turns, because those counts are not from both + // polygons for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++) { const typename sbs_type::rp& ranked = sbs.m_ranked_points[i]; @@ -773,6 +818,13 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, op.enriched.rank = ranked.rank; op.enriched.zone = ranked.zone; + if (OverlayType != overlay_difference + && is_self_turn(turn)) + { + // Difference needs the self-turns, TODO: investigate + continue; + } + if ((for_operation == operation_union && ranked.count_left != 0) || (for_operation == operation_intersection diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp index 39c55db75..9c4a3094e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp @@ -11,6 +11,7 @@ #include +#include #include #include #include @@ -24,9 +25,9 @@ namespace detail { namespace overlay struct discard_turns { - template + template static inline - void apply(Turns& , Geometry0 const& , Geometry1 const& ) + void apply(Turns& , Clusters const& , Geometry0 const& , Geometry1 const& ) {} }; @@ -38,9 +39,9 @@ template <> struct discard_closed_turns { - template + template static inline - void apply(Turns& turns, + void apply(Turns& turns, Clusters const& clusters, Geometry0 const& geometry0, Geometry1 const& geometry1) { typedef typename boost::range_value::type turn_type; @@ -52,9 +53,7 @@ struct discard_closed_turns { turn_type& turn = *it; - if (turn.cluster_id >= 0 - || turn.discarded - || ! is_self_turn(turn)) + if (turn.discarded || ! is_self_turn(turn)) { continue; } @@ -75,11 +74,106 @@ struct discard_closed_turns struct discard_self_intersection_turns { - template +private : + + template static inline - void apply(Turns& turns, + bool any_blocked(signed_size_type cluster_id, + const Turns& turns, Clusters const& clusters) + { + typename Clusters::const_iterator cit = clusters.find(cluster_id); + if (cit == clusters.end()) + { + return false; + } + cluster_info const& cinfo = cit->second; + for (std::set::const_iterator it + = cinfo.turn_indices.begin(); + it != cinfo.turn_indices.end(); ++it) + { + typename boost::range_value::type const& turn = turns[*it]; + if (turn.any_blocked()) + { + return true; + } + } + return false; + } + + template + static inline + bool is_self_cluster(signed_size_type cluster_id, + const Turns& turns, Clusters const& clusters) + { + typename Clusters::const_iterator cit = clusters.find(cluster_id); + if (cit == clusters.end()) + { + return false; + } + + cluster_info const& cinfo = cit->second; + for (std::set::const_iterator it + = cinfo.turn_indices.begin(); + it != cinfo.turn_indices.end(); ++it) + { + if (! is_self_turn(turns[*it])) + { + return false; + } + } + + return true; + } + + template + static inline + bool within(Turn const& turn, Geometry0 const& geometry0, + Geometry1 const& geometry1) + { + return turn.operations[0].seg_id.source_index == 0 + ? geometry::within(turn.point, geometry1) + : geometry::within(turn.point, geometry0); + } + + template + static inline + void discard_clusters(Turns& turns, Clusters const& clusters, Geometry0 const& geometry0, Geometry1 const& geometry1) { + for (typename Clusters::const_iterator cit = clusters.begin(); + cit != clusters.end(); ++cit) + { + signed_size_type cluster_id = cit->first; + + // If there are only self-turns in the cluster, the cluster should + // be located within the other geometry, for intersection + if (is_self_cluster(cluster_id, turns, clusters)) + { + cluster_info const& cinfo = cit->second; + if (! within(turns[*cinfo.turn_indices.begin()], geometry0, geometry1)) + { + // Discard all turns in cluster + for (std::set::const_iterator sit = cinfo.turn_indices.begin(); + sit != cinfo.turn_indices.end(); ++sit) + { + turns[*sit].discarded = true; + } + } + } + } + } + +public : + + template + static inline + void apply(Turns& turns, Clusters const& clusters, + Geometry0 const& geometry0, Geometry1 const& geometry1) + { + discard_clusters(turns, clusters, geometry0, geometry1); + typedef typename boost::range_value::type turn_type; for (typename boost::range_iterator::type @@ -89,9 +183,7 @@ struct discard_self_intersection_turns { turn_type& turn = *it; - if (turn.cluster_id >= 0 - || turn.discarded - || ! is_self_turn(turn)) + if (turn.discarded || ! is_self_turn(turn)) { continue; } @@ -106,16 +198,22 @@ struct discard_self_intersection_turns continue; } - // It is a non co-located ii self-turn + if (turn.is_clustered() && turn.has_colocated_both) + { + // Don't delete a self-ii-turn colocated with another ii-turn + // (for example #case_recursive_boxes_70) + // But for some cases (#case_58_iet) they should be deleted, + // there are many self-turns there and also blocked turns there + if (! any_blocked(turn.cluster_id, turns, clusters)) + { + continue; + } + } + + // It is a ii self-turn // Check if it is within the other geometry // If not, it can be ignored - - bool const within = - turn.operations[0].seg_id.source_index == 0 - ? geometry::within(turn.point, geometry1) - : geometry::within(turn.point, geometry0); - - if (! within) + if (! within(turn, geometry0, geometry1)) { // It is not within another geometry, discard the turn turn.discarded = true; @@ -134,6 +232,61 @@ struct discard_open_turns // For difference, it should be done in a different way (TODO) + +template +inline void discard_self_turns_which_loop(Turns& turns) +{ + if (operation_from_overlay::value == operation_union) + { + // For union, self-turn i/u traveling to itself are allowed to form + // holes. #case_recursive_boxes_37 + // TODO: this can be finetuned by inspecting the cluster too, + // and if there are non-self-turns the polygons on their sides can + // be checked + return; + } + + typedef typename boost::range_value::type turn_type; + typedef typename turn_type::turn_operation_type op_type; + + signed_size_type turn_index = 0; + for (typename boost::range_iterator::type + it = boost::begin(turns); + it != boost::end(turns); + ++it, ++turn_index) + { + turn_type& turn = *it; + + if (! is_self_turn(turn)) + { + continue; + } + if (! turn.combination(operation_intersection, operation_union)) + { + // ii may travel to itself + continue; + } + + for (int i = 0; i < 2; i++) + { + op_type& op = turn.operations[i]; + + if (op.enriched.startable + && op.operation == operation_intersection + && op.enriched.get_next_turn_index() == turn_index) + { + // Self-turn i/u, i part traveling to itself. Discard it. + // (alternatively it might be made unstartable - but the + // intersection-operation may not be traveled anyway, and the + // union-operation is not traveled at all in intersections + // #case_recursive_boxes_77 + turn.discarded = true; + } + } + } + +} + }} // namespace detail::overlay #endif //DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index 7106e7b48..60255cd95 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -208,6 +208,8 @@ struct intersection_of_linestring_with_areal } #endif +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + class is_crossing_turn { // return true is the operation is intersection or blocked @@ -310,6 +312,91 @@ struct intersection_of_linestring_with_areal return 0; } +#else // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + + template + static inline bool simple_turns_analysis(Linestring const& linestring, + Areal const& areal, + Strategy const& strategy, + Turns const& turns, + int & inside_value) + { + using namespace overlay; + + bool found_continue = false; + bool found_intersection = false; + bool found_union = false; + bool found_front = false; + + for (typename Turns::const_iterator it = turns.begin(); + it != turns.end(); ++it) + { + method_type const method = it->method; + operation_type const op = it->operations[0].operation; + + if (method == method_crosses) + { + return false; + } + else if (op == operation_intersection) + { + found_intersection = true; + } + else if (op == operation_union) + { + found_union = true; + } + else if (op == operation_continue) + { + found_continue = true; + } + + if ((found_intersection || found_continue) && found_union) + { + return false; + } + + if (it->operations[0].position == position_front) + { + found_front = true; + } + } + + if (found_front) + { + if (found_intersection) + { + inside_value = 1; // inside + } + else if (found_union) + { + inside_value = -1; // outside + } + else // continue and blocked + { + inside_value = 0; + } + return true; + } + + // if needed analyse points of a linestring + // NOTE: range_in_geometry checks points of a linestring + // until a point inside/outside areal is found + // TODO: Could be replaced with point_in_geometry() because found_front is false + inside_value = range_in_geometry(linestring, areal, strategy); + + if ( (found_intersection && inside_value == -1) // going in from outside + || (found_continue && inside_value == -1) // going on boundary from outside + || (found_union && inside_value == 1) ) // going out from inside + { + return false; + } + + return true; + } + +#endif // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + template < typename LineString, typename Areal, @@ -336,14 +423,30 @@ struct intersection_of_linestring_with_areal > follower; typedef typename point_type::type point_type; +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR typedef detail::overlay::traversal_turn_info - < - point_type, - typename geometry::segment_ratio_type::type - > turn_info; + < + point_type, + typename geometry::segment_ratio_type::type + > turn_info; +#else + typedef detail::overlay::turn_info + < + point_type, + typename geometry::segment_ratio_type::type, + detail::overlay::turn_operation_linear + < + point_type, + typename geometry::segment_ratio_type::type + > + > turn_info; +#endif std::deque turns; detail::get_turns::no_interrupt_policy policy; + +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + geometry::get_turns < false, @@ -366,7 +469,7 @@ struct intersection_of_linestring_with_areal // until a point inside/outside areal is found inside_value = overlay::range_in_geometry(linestring, areal, strategy); } - // add point to the output if conditions are met + // add linestring to the output if conditions are met if (inside_value != 0 && follower::included(inside_value)) { LineStringOut copy; @@ -376,6 +479,47 @@ struct intersection_of_linestring_with_areal return out; } +#else // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + + typedef detail::overlay::get_turn_info_linear_areal + < + detail::overlay::assign_null_policy + > turn_policy; + + dispatch::get_turns + < + typename geometry::tag::type, + typename geometry::tag::type, + LineString, + Areal, + false, + (OverlayType == overlay_intersection ? ReverseAreal : !ReverseAreal), + turn_policy + >::apply(0, linestring, 1, areal, + strategy, robust_policy, + turns, policy); + + int inside_value = 0; + if (simple_turns_analysis(linestring, areal, strategy, turns, inside_value)) + { + // No crossing the boundary, it is either + // inside (interior + borders) + // or outside (exterior + borders) + // or on boundary + + // add linestring to the output if conditions are met + if (follower::included(inside_value)) + { + LineStringOut copy; + geometry::convert(linestring, copy); + *out++ = copy; + } + + return out; + } + +#endif // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) int index = 0; for(typename std::deque::const_iterator @@ -395,6 +539,135 @@ struct intersection_of_linestring_with_areal }; +template +inline OutputIterator intersection_output_turn_points(Turns const& turns, + OutputIterator out) +{ + for (typename Turns::const_iterator + it = turns.begin(); it != turns.end(); ++it) + { + *out++ = it->point; + } + + return out; +} + +template +struct intersection_areal_areal_point +{ + template + < + typename Geometry1, typename Geometry2, + typename RobustPolicy, + typename OutputIterator, + typename Strategy + > + static inline OutputIterator apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + 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, strategy, robust_policy, turns, policy); + + return intersection_output_turn_points(turns, out); + } +}; + +template +struct intersection_linear_areal_point +{ + template + < + typename Geometry1, typename Geometry2, + typename RobustPolicy, + typename OutputIterator, + typename Strategy + > + static inline OutputIterator apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + typedef typename geometry::segment_ratio_type + < + PointOut, RobustPolicy + >::type segment_ratio_type; + + typedef detail::overlay::turn_info + < + PointOut, + segment_ratio_type, + detail::overlay::turn_operation_linear + < + PointOut, + segment_ratio_type + > + > turn_info; + + typedef detail::overlay::get_turn_info_linear_areal + < + detail::overlay::assign_null_policy + > turn_policy; + + std::vector turns; + + detail::get_turns::no_interrupt_policy interrupt_policy; + + dispatch::get_turns + < + typename geometry::tag::type, + typename geometry::tag::type, + Geometry1, + Geometry2, + false, + false, + turn_policy + >::apply(0, geometry1, 1, geometry2, + strategy, robust_policy, + turns, interrupt_policy); + + return intersection_output_turn_points(turns, out); + } +}; + +template +struct intersection_areal_linear_point +{ + template + < + typename Geometry1, typename Geometry2, + typename RobustPolicy, + typename OutputIterator, + typename Strategy + > + static inline OutputIterator apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + return intersection_linear_areal_point + < + PointOut + >::apply(geometry2, geometry1, robust_policy, out, strategy); + } +}; + + }} // namespace detail::intersection #endif // DOXYGEN_NO_DETAIL @@ -420,9 +693,9 @@ template 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 + typename CastedTagIn1 = typename geometry::tag_cast::type, + typename CastedTagIn2 = typename geometry::tag_cast::type, + typename CastedTagOut = typename geometry::tag_cast::type > struct intersection_insert { @@ -449,7 +722,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, TagIn1, TagIn2, TagOut, - true, true, true + areal_tag, areal_tag, areal_tag > : detail::overlay::overlay {}; @@ -471,7 +744,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, TagIn, box_tag, TagOut, - true, true, true + areal_tag, areal_tag, areal_tag > : detail::overlay::overlay {}; @@ -491,7 +764,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, segment_tag, segment_tag, point_tag, - false, false, false + linear_tag, linear_tag, pointlike_tag > : detail::intersection::intersection_segment_segment_point {}; @@ -510,7 +783,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, linestring_tag, linestring_tag, point_tag, - false, false, false + linear_tag, linear_tag, pointlike_tag > : detail::intersection::intersection_linestring_linestring_point {}; @@ -528,7 +801,7 @@ struct intersection_insert overlay_intersection, Reverse1, Reverse2, ReverseOut, linestring_tag, box_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > { template @@ -559,7 +832,7 @@ struct intersection_insert OverlayType, ReverseLinestring, ReversePolygon, ReverseOut, linestring_tag, polygon_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_linestring_with_areal < ReversePolygon, @@ -583,7 +856,7 @@ struct intersection_insert OverlayType, ReverseLinestring, ReverseRing, ReverseOut, linestring_tag, ring_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_linestring_with_areal < ReverseRing, @@ -606,7 +879,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, segment_tag, box_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > { template @@ -630,8 +903,7 @@ template typename PointOut, overlay_type OverlayType, bool Reverse1, bool Reverse2, bool ReverseOut, - typename Tag1, typename Tag2, - bool Areal1, bool Areal2 + typename Tag1, typename Tag2 > struct intersection_insert < @@ -640,38 +912,59 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, Tag1, Tag2, point_tag, - Areal1, Areal2, false + areal_tag, areal_tag, pointlike_tag > -{ - template - static inline OutputIterator apply(Geometry1 const& geometry1, - Geometry2 const& geometry2, - RobustPolicy const& robust_policy, - OutputIterator out, Strategy const& strategy) - { + : public detail::intersection::intersection_areal_areal_point + < + PointOut + > +{}; - 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, strategy, robust_policy, turns, policy); - for (typename std::vector::const_iterator it - = turns.begin(); it != turns.end(); ++it) - { - *out++ = it->point; - } - - return out; - } -}; +template +< + typename Geometry1, typename Geometry2, + typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename Tag1, typename Tag2 +> +struct intersection_insert + < + Geometry1, Geometry2, + PointOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + Tag1, Tag2, point_tag, + linear_tag, areal_tag, pointlike_tag + > + : public detail::intersection::intersection_linear_areal_point + < + PointOut + > +{}; +template +< + typename Geometry1, typename Geometry2, + typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename Tag1, typename Tag2 +> +struct intersection_insert + < + Geometry1, Geometry2, + PointOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + Tag1, Tag2, point_tag, + areal_tag, linear_tag, pointlike_tag + > + : public detail::intersection::intersection_areal_linear_point + < + PointOut + > +{}; template < @@ -713,7 +1006,7 @@ struct intersection_insert overlay_intersection, Reverse1, Reverse2, ReverseOut, Tag1, Tag2, linestring_tag, - true, true, false + areal_tag, areal_tag, linear_tag > { template @@ -739,47 +1032,20 @@ struct intersection_insert } }; -// dispatch for non-areal geometries +// dispatch for difference/intersection of linear geometries template < - typename Geometry1, typename Geometry2, typename GeometryOut, + typename Linear1, typename Linear2, typename LineStringOut, 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 + TagIn1, TagIn2, linestring_tag, + linear_tag, linear_tag, linear_tag > : detail::overlay::linear_linear_linestring < Linear1, Linear2, LineStringOut, OverlayType @@ -800,7 +1066,7 @@ struct intersection_insert Point1, Point2, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, point_tag, point_tag, point_tag, - false, false, false + pointlike_tag, pointlike_tag, pointlike_tag > : detail::overlay::point_point_point < Point1, Point2, PointOut, OverlayType @@ -819,7 +1085,7 @@ struct intersection_insert MultiPoint, Point, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, multi_point_tag, point_tag, point_tag, - false, false, false + pointlike_tag, pointlike_tag, pointlike_tag > : detail::overlay::multipoint_point_point < MultiPoint, Point, PointOut, OverlayType @@ -838,7 +1104,7 @@ struct intersection_insert Point, MultiPoint, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, point_tag, multi_point_tag, point_tag, - false, false, false + pointlike_tag, pointlike_tag, pointlike_tag > : detail::overlay::point_multipoint_point < Point, MultiPoint, PointOut, OverlayType @@ -857,7 +1123,7 @@ struct intersection_insert MultiPoint1, MultiPoint2, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, multi_point_tag, multi_point_tag, point_tag, - false, false, false + pointlike_tag, pointlike_tag, pointlike_tag > : detail::overlay::multipoint_multipoint_point < MultiPoint1, MultiPoint2, PointOut, OverlayType @@ -878,7 +1144,7 @@ struct intersection_insert Point, Linear, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, point_tag, Tag, point_tag, - false, false, false + pointlike_tag, linear_tag, pointlike_tag > : detail_dispatch::overlay::pointlike_linear_point < Point, Linear, PointOut, OverlayType, @@ -899,7 +1165,7 @@ struct intersection_insert MultiPoint, Linear, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, multi_point_tag, Tag, point_tag, - false, false, false + pointlike_tag, linear_tag, pointlike_tag > : detail_dispatch::overlay::pointlike_linear_point < MultiPoint, Linear, PointOut, OverlayType, @@ -919,7 +1185,7 @@ struct intersection_insert Linestring, MultiPoint, PointOut, overlay_intersection, Reverse1, Reverse2, ReverseOut, linestring_tag, multi_point_tag, point_tag, - false, false, false + linear_tag, pointlike_tag, pointlike_tag > { template diff --git a/include/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp b/include/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp index 9cb7a0fca..9423a24b3 100644 --- a/include/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp @@ -41,7 +41,7 @@ struct is_self_turn_check }; template <> -struct is_self_turn_check +struct is_self_turn_check { template static inline bool apply(Turn const& turn) @@ -50,6 +50,15 @@ struct is_self_turn_check } }; +template <> +struct is_self_turn_check +{ + template + static inline bool apply(Turn const& turn) + { + return false; + } +}; template bool is_self_turn(Turn const& turn) diff --git a/include/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp b/include/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp index 21868a293..dd30635ee 100644 --- a/include/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -60,6 +65,7 @@ template typename Indexed, typename Geometry1, typename Geometry2, typename RobustPolicy, + typename SideStrategy, bool Reverse1, bool Reverse2 > struct less_by_segment_ratio @@ -68,12 +74,14 @@ struct less_by_segment_ratio , operation_type for_operation , Geometry1 const& geometry1 , Geometry2 const& geometry2 - , RobustPolicy const& robust_policy) + , RobustPolicy const& robust_policy + , SideStrategy const& strategy) : m_turns(turns) , m_for_operation(for_operation) , m_geometry1(geometry1) , m_geometry2(geometry2) , m_robust_policy(robust_policy) + , m_strategy(strategy) { } @@ -84,6 +92,7 @@ private : Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; RobustPolicy const& m_robust_policy; + SideStrategy const& m_strategy; typedef typename geometry::point_type::type point_type; @@ -108,13 +117,8 @@ private : *right.other_seg_id, si, sj); - typedef typename strategy::side::services::default_strategy - < - 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); + int const side_rj_p = m_strategy.apply(pi, pj, rj); + int const side_sj_p = m_strategy.apply(pi, pj, sj); // Put the one turning left (1; right == -1) as last if (side_rj_p != side_sj_p) @@ -122,8 +126,8 @@ private : 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); + int const side_sj_r = m_strategy.apply(ri, rj, sj); + int const side_rj_s = m_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 diff --git a/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp b/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp index a74bb33ba..21d079d95 100644 --- a/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp @@ -194,13 +194,15 @@ protected: typename Turns, typename LinearGeometry1, typename LinearGeometry2, - typename OutputIterator + typename OutputIterator, + typename IntersectionStrategy > static inline OutputIterator sort_and_follow_turns(Turns& turns, LinearGeometry1 const& linear1, LinearGeometry2 const& linear2, - OutputIterator oit) + OutputIterator oit, + IntersectionStrategy const& strategy) { // remove turns that have no added value turns::filter_continue_turns @@ -228,7 +230,7 @@ protected: FollowIsolatedPoints, !EnableFilterContinueTurns || OverlayType == overlay_intersection >::apply(linear1, linear2, boost::begin(turns), boost::end(turns), - oit); + oit, strategy.get_side_strategy()); } public: @@ -277,7 +279,7 @@ public: OverlayType, EnableFollowIsolatedPoints && OverlayType == overlay_intersection - >(turns, linear1, linear2, oit); + >(turns, linear1, linear2, oit, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/overlay/needs_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/needs_self_turns.hpp new file mode 100644 index 000000000..87fdd00d5 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/needs_self_turns.hpp @@ -0,0 +1,83 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2017-2017 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_NEEDS_SELF_TURNS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_NEEDS_SELF_TURNS_HPP + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template +< + typename Geometry, + typename Tag = typename tag::type +> +struct needs_self_turns +{ +}; + +template +struct needs_self_turns +{ + static inline bool apply(Geometry const&) + { + return false; + } +}; + +template +struct needs_self_turns +{ + static inline bool apply(Geometry const&) + { + return false; + } +}; + +template +struct needs_self_turns +{ + static inline bool apply(Geometry const& polygon) + { + return geometry::num_interior_rings(polygon) > 0; + } +}; + +template +struct needs_self_turns +{ + static inline bool apply(Geometry const& multi) + { + typedef typename boost::range_value::type polygon_type; + std::size_t const n = boost::size(multi); + return n > 1 || (n == 1 + && needs_self_turns + ::apply(*boost::begin(multi))); + } +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_NEEDS_SELF_TURNS_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 90258cc84..b415f97a0 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -99,89 +100,88 @@ template inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, Clusters const& clusters) { typedef typename boost::range_value::type turn_type; + typedef typename turn_type::turn_operation_type turn_operation_type; typedef typename turn_type::container_type container_type; static const operation_type target_operation = operation_from_overlay::value; static const operation_type opposite_operation - = target_operation == operation_union ? operation_intersection : operation_union; + = target_operation == operation_union + ? operation_intersection + : operation_union; - signed_size_type turn_index = 0; for (typename boost::range_iterator::type it = boost::begin(turns); it != boost::end(turns); - ++it, turn_index++) + ++it) { - typename boost::range_value::type const& turn = *it; + turn_type const& turn = *it; - bool const colocated_target = target_operation == operation_union - ? turn.colocated_uu : turn.colocated_ii; - bool const colocated_opp = target_operation == operation_union - ? turn.colocated_ii : turn.colocated_uu; - bool const both_opposite = turn.both(opposite_operation); - - bool const traversed - = turn.operations[0].visited.finalized() - || turn.operations[0].visited.rejected() - || turn.operations[1].visited.finalized() - || turn.operations[1].visited.rejected() - || turn.both(operation_blocked) - || turn.combination(opposite_operation, operation_blocked); - - bool is_closed = false; - if (turn.cluster_id >= 0 && target_operation == operation_union) - { - typename Clusters::const_iterator mit = clusters.find(turn.cluster_id); - BOOST_ASSERT(mit != clusters.end()); - - cluster_info const& cinfo = mit->second; - is_closed = cinfo.open_count == 0; - } + bool cluster_checked = false; + bool has_blocked = false; for (typename boost::range_iterator::type op_it = boost::begin(turn.operations); op_it != boost::end(turn.operations); ++op_it) { + turn_operation_type const& op = *op_it; ring_identifier const ring_id ( - op_it->seg_id.source_index, - op_it->seg_id.multi_index, - op_it->seg_id.ring_index + op.seg_id.source_index, + op.seg_id.multi_index, + op.seg_id.ring_index ); - if (traversed || is_closed || ! op_it->enriched.startable) + if (! is_self_turn(turn) + && ( + (target_operation == operation_union + && op.enriched.count_left > 0) + || (target_operation == operation_intersection + && op.enriched.count_right <= 2))) { - turn_info_map[ring_id].has_traversed_turn = true; + // Avoid including untraversed rings which have polygons on + // their left side (union) or not two on their right side (int) + // This can only be done for non-self-turns because of count + // information + turn_info_map[ring_id].has_blocked_turn = true; + continue; } - else if (both_opposite && colocated_target) - { - // For union: ii, colocated with a uu - // For example, two interior rings touch where two exterior rings also touch. - // The interior rings are not yet traversed, and should be taken from the input - // For intersection: uu, colocated with an ii - // unless it is two interior inner rings colocated with a uu + if (turn.any_blocked()) + { + turn_info_map[ring_id].has_blocked_turn = true; + } + if (turn_info_map[ring_id].has_traversed_turn + || turn_info_map[ring_id].has_blocked_turn) + { + continue; + } - // So don't set has_traversed_turn here - } - else if (both_opposite && ! is_self_turn(turn)) + // Check information in colocated turns + if (! cluster_checked && turn.is_clustered()) { - // For union, mark any ring with a ii turn as traversed - // For intersection, any uu - but not if it is a self-turn - turn_info_map[ring_id].has_traversed_turn = true; + check_colocation(has_blocked, turn.cluster_id, turns, clusters); + cluster_checked = true; } - else if (colocated_opp && ! colocated_target) + + // Block rings where any other turn is blocked, + // and (with exceptions): i for union and u for intersection + // Exceptions: don't block self-uu for intersection + // don't block self-ii for union + // don't block (for union) i/u if there is an self-ii too + if (has_blocked + || (op.operation == opposite_operation + && ! turn.has_colocated_both + && ! (turn.both(opposite_operation) + && is_self_turn(turn)))) { - // For union, a turn colocated with ii and NOT with uu/ux - // For intersection v.v. - turn_info_map[ring_id].has_traversed_turn = true; + turn_info_map[ring_id].has_blocked_turn = true; } } } } - template < typename GeometryOut, overlay_type OverlayType, bool ReverseOut, @@ -197,7 +197,16 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, typename geometry::ring_type::type > ring_container_type; - typedef ring_properties::type> properties; + typedef typename geometry::point_type::type point_type1; + + typedef ring_properties + < + point_type1, + typename Strategy::template area_strategy + < + point_type1 + >::type::return_type + > properties; // Silence warning C4127: conditional expression is constant #if defined(_MSC_VER) @@ -297,9 +306,13 @@ std::cout << "get turns" << std::endl; visitor.visit_turns(1, turns); #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + if (needs_self_turns::apply(geometry1)) { self_get_turn_points::self_turns(geometry1, strategy, robust_policy, turns, policy, 0); + } + if (needs_self_turns::apply(geometry2)) + { self_get_turn_points::self_turns(geometry2, strategy, robust_policy, turns, policy, 1); } @@ -309,8 +322,9 @@ std::cout << "get turns" << std::endl; #ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE std::cout << "enrich" << std::endl; #endif - typename Strategy::side_strategy_type side_strategy; + typename Strategy::side_strategy_type side_strategy = strategy.get_side_strategy(); cluster_type clusters; + std::map turn_info_per_ring; geometry::enrich_intersection_points(turns, clusters, geometry1, geometry2, @@ -334,17 +348,21 @@ std::cout << "traverse" << std::endl; strategy, robust_policy, turns, rings, + turn_info_per_ring, clusters, visitor ); + visitor.visit_turns(3, turns); - std::map turn_info_per_ring; get_ring_turn_info(turn_info_per_ring, turns, clusters); + typedef typename Strategy::template area_strategy::type area_strategy_type; + typedef ring_properties - < - typename geometry::point_type::type - > properties; + < + point_type, + typename area_strategy_type::return_type + > properties; // Select all rings which are NOT touched by any intersection point std::map selected_ring_properties; @@ -353,13 +371,15 @@ std::cout << "traverse" << std::endl; // Add rings created during traversal { + area_strategy_type const area_strategy = strategy.template get_area_strategy(); + ring_identifier id(2, 0, -1); for (typename boost::range_iterator::type it = boost::begin(rings); it != boost::end(rings); ++it) { - selected_ring_properties[id] = properties(*it); + selected_ring_properties[id] = properties(*it, area_strategy); selected_ring_properties[id].reversed = ReverseOut; id.multi_index++; } diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp index 0f6084097..f3ec9eaa6 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp @@ -21,7 +21,8 @@ enum overlay_type overlay_intersection, overlay_difference, overlay_buffer, - overlay_dissolve + overlay_dissolve_union, + overlay_dissolve_intersection }; #ifndef DOXYGEN_NO_DETAIL @@ -41,6 +42,17 @@ enum operation_type template struct operation_from_overlay +{ +}; + +template <> +struct operation_from_overlay +{ + static const operation_type value = operation_union; +}; + +template <> +struct operation_from_overlay { static const operation_type value = operation_union; }; @@ -57,6 +69,19 @@ struct operation_from_overlay static const operation_type value = operation_intersection; }; +template <> +struct operation_from_overlay +{ + static const operation_type value = operation_union; +}; + +template <> +struct operation_from_overlay +{ + static const operation_type value = operation_intersection; +}; + + }} // namespace detail::overlay #endif //DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp b/include/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp index 0a7c3bc46..6f8fdd32b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp @@ -32,7 +32,6 @@ #include #include -#include #include #include #include diff --git a/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp b/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp index 438a37787..88aedecf8 100644 --- a/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp @@ -1,12 +1,13 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, 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 // 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 @@ -24,10 +25,11 @@ #include #include -#include #include #include +#include + namespace boost { namespace geometry { @@ -264,17 +266,20 @@ struct multipoint_multipoint_point >::apply(multipoint2, multipoint1, robust_policy, oit, strategy); } - std::vector::type> - points2(boost::begin(multipoint2), boost::end(multipoint2)); + typedef typename boost::range_value::type point2_type; - std::sort(points2.begin(), points2.end(), detail::relate::less()); + std::vector points2(boost::begin(multipoint2), + boost::end(multipoint2)); + + geometry::less<> const less = geometry::less<>(); + std::sort(points2.begin(), points2.end(), 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()); + *it1, less); action_selector_pl_pl < diff --git a/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp b/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp index abade1fe1..7dbc5d5fa 100644 --- a/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp @@ -27,11 +27,11 @@ namespace boost { namespace geometry namespace detail { namespace overlay { -template +template struct ring_properties { typedef Point point_type; - typedef typename default_area_result::type area_type; + typedef AreaType area_type; bool valid; @@ -56,13 +56,13 @@ struct ring_properties , parent_area(-1) {} - template - inline ring_properties(RingOrBox const& ring_or_box) + template + inline ring_properties(RingOrBox const& ring_or_box, AreaStrategy const& strategy) : reversed(false) , discarded(false) , parent_area(-1) { - this->area = geometry::area(ring_or_box); + this->area = geometry::area(ring_or_box, strategy); valid = geometry::point_on_border(this->point, ring_or_box); } diff --git a/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp index 638bcf474..262ba748a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -41,10 +41,12 @@ namespace detail { namespace overlay struct ring_turn_info { bool has_traversed_turn; + bool has_blocked_turn; bool within_other; ring_turn_info() : has_traversed_turn(false) + , has_blocked_turn(false) , within_other(false) {} }; @@ -59,41 +61,45 @@ namespace dispatch template struct select_rings { - template + template static inline void apply(Box const& box, Geometry const& , - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { - ring_properties[id] = typename RingPropertyMap::mapped_type(box); + ring_properties[id] = typename RingPropertyMap::mapped_type(box, strategy); } - template + template static inline void apply(Box const& box, - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { - ring_properties[id] = typename RingPropertyMap::mapped_type(box); + ring_properties[id] = typename RingPropertyMap::mapped_type(box, strategy); } }; template struct select_rings { - template + template static inline void apply(Ring const& ring, Geometry const& , - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { if (boost::size(ring) > 0) { - ring_properties[id] = typename RingPropertyMap::mapped_type(ring); + ring_properties[id] = typename RingPropertyMap::mapped_type(ring, strategy); } } - template + template static inline void apply(Ring const& ring, - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { if (boost::size(ring) > 0) { - ring_properties[id] = typename RingPropertyMap::mapped_type(ring); + ring_properties[id] = typename RingPropertyMap::mapped_type(ring, strategy); } } }; @@ -102,14 +108,15 @@ namespace dispatch template struct select_rings { - template + template static inline void apply(Polygon const& polygon, Geometry const& geometry, - ring_identifier id, RingPropertyMap& ring_properties) + ring_identifier id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { typedef typename geometry::ring_type::type ring_type; typedef select_rings per_ring; - per_ring::apply(exterior_ring(polygon), geometry, id, ring_properties); + per_ring::apply(exterior_ring(polygon), geometry, id, ring_properties, strategy); typename interior_return_type::type rings = interior_rings(polygon); @@ -117,18 +124,19 @@ namespace dispatch it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; - per_ring::apply(*it, geometry, id, ring_properties); + per_ring::apply(*it, geometry, id, ring_properties, strategy); } } - template + template static inline void apply(Polygon const& polygon, - ring_identifier id, RingPropertyMap& ring_properties) + ring_identifier id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { typedef typename geometry::ring_type::type ring_type; typedef select_rings per_ring; - per_ring::apply(exterior_ring(polygon), id, ring_properties); + per_ring::apply(exterior_ring(polygon), id, ring_properties, strategy); typename interior_return_type::type rings = interior_rings(polygon); @@ -136,7 +144,7 @@ namespace dispatch it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; - per_ring::apply(*it, id, ring_properties); + per_ring::apply(*it, id, ring_properties, strategy); } } }; @@ -144,9 +152,10 @@ namespace dispatch template struct select_rings { - template + template static inline void apply(Multi const& multi, Geometry const& geometry, - ring_identifier id, RingPropertyMap& ring_properties) + ring_identifier id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { typedef typename boost::range_iterator < @@ -159,7 +168,7 @@ namespace dispatch for (iterator_type it = boost::begin(multi); it != boost::end(multi); ++it) { id.ring_index = -1; - per_polygon::apply(*it, geometry, id, ring_properties); + per_polygon::apply(*it, geometry, id, ring_properties, strategy); id.multi_index++; } } @@ -258,9 +267,9 @@ inline void update_ring_selection(Geometry1 const& geometry1, info = tcit->second; // Copy by value } - if (info.has_traversed_turn) + if (info.has_traversed_turn || info.has_blocked_turn) { - // This turn is traversed (or blocked), + // This turn is traversed or blocked, // don't include the original ring continue; } @@ -311,12 +320,16 @@ inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2, { typedef typename geometry::tag::type tag1; typedef typename geometry::tag::type tag2; + typedef typename geometry::point_type::type point1_type; + typedef typename geometry::point_type::type point2_type; RingPropertyMap all_ring_properties; dispatch::select_rings::apply(geometry1, geometry2, - ring_identifier(0, -1, -1), all_ring_properties); + ring_identifier(0, -1, -1), all_ring_properties, + strategy.template get_area_strategy()); dispatch::select_rings::apply(geometry2, geometry1, - ring_identifier(1, -1, -1), all_ring_properties); + ring_identifier(1, -1, -1), all_ring_properties, + strategy.template get_area_strategy()); update_ring_selection(geometry1, geometry2, turn_info_per_ring, all_ring_properties, selected_ring_properties, @@ -337,10 +350,12 @@ inline void select_rings(Geometry const& geometry, Strategy const& strategy) { typedef typename geometry::tag::type tag; + typedef typename geometry::point_type::type point_type; RingPropertyMap all_ring_properties; dispatch::select_rings::apply(geometry, - ring_identifier(0, -1, -1), all_ring_properties); + ring_identifier(0, -1, -1), all_ring_properties, + strategy.template get_area_strategy()); update_ring_selection(geometry, geometry, turn_info_per_ring, all_ring_properties, selected_ring_properties, diff --git a/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp index 5e9d8efa8..a00606b08 100644 --- a/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -78,19 +78,22 @@ struct self_section_visitor Turns& m_turns; InterruptPolicy& m_interrupt_policy; std::size_t m_source_index; + bool m_skip_adjacent; inline self_section_visitor(Geometry const& g, IntersectionStrategy const& is, RobustPolicy const& rp, Turns& turns, InterruptPolicy& ip, - std::size_t source_index) + std::size_t source_index, + bool skip_adjacent) : m_geometry(g) , m_intersection_strategy(is) , m_rescale_policy(rp) , m_turns(turns) , m_interrupt_policy(ip) , m_source_index(source_index) + , m_skip_adjacent(skip_adjacent) {} template @@ -109,7 +112,7 @@ struct self_section_visitor TurnPolicy >::apply(m_source_index, m_geometry, sec1, m_source_index, m_geometry, sec2, - false, + false, m_skip_adjacent, m_intersection_strategy, m_rescale_policy, m_turns, m_interrupt_policy); @@ -132,7 +135,7 @@ struct get_turns RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, - std::size_t source_index) + std::size_t source_index, bool skip_adjacent) { typedef model::box < @@ -143,9 +146,11 @@ struct get_turns >::type > box_type; - typedef geometry::sections sections_type; + // sectionalize in two dimensions to detect + // all potential spikes correctly + typedef geometry::sections sections_type; - typedef boost::mpl::vector_c dimensions; + typedef boost::mpl::vector_c dimensions; sections_type sec; geometry::sectionalize(geometry, robust_policy, sec, @@ -155,7 +160,7 @@ struct get_turns < Reverse, Geometry, Turns, TurnPolicy, IntersectionStrategy, RobustPolicy, InterruptPolicy - > visitor(geometry, intersection_strategy, robust_policy, turns, interrupt_policy, source_index); + > visitor(geometry, intersection_strategy, robust_policy, turns, interrupt_policy, source_index, skip_adjacent); // false if interrupted geometry::partition @@ -224,7 +229,8 @@ struct self_get_turn_points RobustPolicy const& , Turns& , InterruptPolicy& , - std::size_t) + std::size_t /*source_index*/, + bool /*skip_adjacent*/) { return true; } @@ -286,7 +292,8 @@ inline void self_turns(Geometry const& geometry, RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, - std::size_t source_index = 0) + std::size_t source_index = 0, + bool skip_adjacent = false) { concepts::check(); @@ -298,7 +305,8 @@ inline void self_turns(Geometry const& geometry, typename tag::type, Geometry, turn_policy - >::apply(geometry, strategy, robust_policy, turns, interrupt_policy, source_index); + >::apply(geometry, strategy, robust_policy, turns, interrupt_policy, + source_index, skip_adjacent); } }} // namespace detail::self_get_turn_points @@ -331,7 +339,8 @@ inline void self_turns(Geometry const& geometry, RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, - std::size_t source_index = 0) + std::size_t source_index = 0, + bool skip_adjacent = false) { concepts::check(); @@ -344,7 +353,8 @@ inline void self_turns(Geometry const& geometry, < reverse, AssignPolicy - >(geometry, strategy, robust_policy, turns, interrupt_policy, source_index); + >(geometry, strategy, robust_policy, turns, interrupt_policy, + source_index, skip_adjacent); } diff --git a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp index a2f7691c3..fea5698ae 100644 --- a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -18,7 +23,6 @@ #include #include #include -#include namespace boost { namespace geometry { @@ -87,13 +91,20 @@ struct less_by_index template inline bool operator()(const T& first, const T& second) const { + // Length might be considered too // First order by from/to if (first.direction != second.direction) { return first.direction < second.direction; } - // All the same, order by turn index (we might consider length too) - return first.turn_index < second.turn_index; + // Then by turn index + if (first.turn_index != second.turn_index) + { + return first.turn_index < second.turn_index; + } + // This can also be the same (for example in buffer), but seg_id is + // never the same + return first.seg_id < second.seg_id; } }; @@ -106,17 +117,13 @@ struct less_false } }; -template +template struct less_by_side { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side; - - less_by_side(const Point& p1, const Point& p2) + less_by_side(const Point& p1, const Point& p2, SideStrategy const& strategy) : m_p1(p1) , m_p2(p2) + , m_strategy(strategy) {} template @@ -125,8 +132,8 @@ struct less_by_side LessOnSame on_same; Compare compare; - int const side_first = side::apply(m_p1, m_p2, first.point); - int const side_second = side::apply(m_p1, m_p2, second.point); + int const side_first = m_strategy.apply(m_p1, m_p2, first.point); + int const side_second = m_strategy.apply(m_p1, m_p2, second.point); if (side_first == 0 && side_second == 0) { @@ -166,7 +173,7 @@ struct less_by_side // They are both left, both right, and/or both collinear (with each other and/or with p1,p2) // Check mutual side - int const side_second_wrt_first = side::apply(m_p2, first.point, second.point); + int const side_second_wrt_first = m_strategy.apply(m_p2, first.point, second.point); if (side_second_wrt_first == 0) { @@ -184,6 +191,7 @@ struct less_by_side private : Point m_p1, m_p2; + SideStrategy const& m_strategy; }; // Sorts vectors in counter clockwise order (by default) @@ -193,6 +201,7 @@ template bool Reverse2, overlay_type OverlayType, typename Point, + typename SideStrategy, typename Compare > struct side_sorter @@ -223,9 +232,10 @@ private : }; public : - side_sorter() + side_sorter(SideStrategy const& strategy) : m_origin_count(0) , m_origin_segment_distance(0) + , m_strategy(strategy) {} template @@ -309,8 +319,8 @@ public : // to give colinear points // Sort by side and assign rank - less_by_side less_unique(m_origin, turn_point); - less_by_side less_non_unique(m_origin, turn_point); + less_by_side less_unique(m_origin, turn_point, m_strategy); + less_by_side less_non_unique(m_origin, turn_point, m_strategy); std::sort(m_ranked_points.begin(), m_ranked_points.end(), less_unique); @@ -425,6 +435,7 @@ public : Point m_origin; std::size_t m_origin_count; int m_origin_segment_distance; + SideStrategy m_strategy; private : diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index ccb5d0e1f..42ccf07ab 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -94,6 +99,7 @@ template typename Turns, typename Clusters, typename RobustPolicy, + typename SideStrategy, typename Visitor > struct traversal @@ -108,22 +114,25 @@ struct traversal typedef sort_by_side::side_sorter < Reverse1, Reverse2, OverlayType, - point_type, side_compare_type + point_type, SideStrategy, side_compare_type > sbs_type; inline traversal(Geometry1 const& geometry1, Geometry2 const& geometry2, Turns& turns, Clusters const& clusters, - RobustPolicy const& robust_policy, Visitor& visitor) + RobustPolicy const& robust_policy, SideStrategy const& strategy, + Visitor& visitor) : m_geometry1(geometry1) , m_geometry2(geometry2) , m_turns(turns) , m_clusters(clusters) , m_robust_policy(robust_policy) + , m_strategy(strategy) , m_visitor(visitor) { } - inline void finalize_visit_info() + template + inline void finalize_visit_info(TurnInfoMap& turn_info_map) { for (typename boost::range_iterator::type it = boost::begin(m_turns); @@ -134,6 +143,32 @@ struct traversal for (int i = 0; i < 2; i++) { turn_operation_type& op = turn.operations[i]; + if (op.visited.visited() + || op.visited.started() + || op.visited.finished() ) + { + ring_identifier const ring_id + ( + op.seg_id.source_index, + op.seg_id.multi_index, + op.seg_id.ring_index + ); + turn_info_map[ring_id].has_traversed_turn = true; + + if (op.operation == operation_continue) + { + // Continue operations should mark the other operation + // as traversed too + turn_operation_type& other_op = turn.operations[1 - i]; + ring_identifier const other_ring_id + ( + other_op.seg_id.source_index, + other_op.seg_id.multi_index, + other_op.seg_id.ring_index + ); + turn_info_map[other_ring_id].has_traversed_turn = true; + } + } op.visited.finalize(); } } @@ -184,7 +219,7 @@ struct traversal { op.visited.set_visited(); } - if (turn.cluster_id >= 0) + if (turn.is_clustered()) { set_visited_in_cluster(turn.cluster_id, op.enriched.rank); } @@ -245,7 +280,7 @@ struct traversal // It is not a dead end if there is an operation to continue, or of // there is a cluster (assuming for now we can get out of the cluster) - return turn.cluster_id >= 0 + return turn.is_clustered() || turn.has(target_operation) || turn.has(operation_continue); } @@ -372,13 +407,50 @@ struct traversal return true; } + inline int select_turn_in_cluster_union(std::size_t selected_rank, + typename sbs_type::rp const& ranked_point, + signed_size_type start_turn_index, int start_op_index) const + { + // Returns 0 if it not OK + // Returns 1 if it OK + // Returns 2 if it OK and start turn matches + // Returns 3 if it OK and start turn and start op both match + if (ranked_point.rank != selected_rank + || ranked_point.direction != sort_by_side::dir_to) + { + return 0; + } + + turn_type const& turn = m_turns[ranked_point.turn_index]; + turn_operation_type const& op = turn.operations[ranked_point.operation_index]; + + // Check counts: in some cases interior rings might be generated with + // polygons on both sides + + // Check finalized: TODO: this should be finetuned, it is not necessary + bool const ok = op.enriched.count_left == 0 + && op.enriched.count_right > 0 + && ! op.visited.finalized(); + + if (! ok) + { + return 0; + } + + return ranked_point.turn_index == start_turn_index + && ranked_point.operation_index == start_op_index ? 3 + : ranked_point.turn_index == start_turn_index ? 2 + : 1 + ; + } + inline bool select_from_cluster_union(signed_size_type& turn_index, - int& op_index, sbs_type& sbs) const + int& op_index, sbs_type& sbs, + signed_size_type start_turn_index, int start_op_index) const { std::vector aggregation; sort_by_side::aggregate_operations(sbs, aggregation, m_turns, operation_union); - sort_by_side::rank_with_rings const& incoming = aggregation.front(); // Take the first one outgoing for the incoming region @@ -394,62 +466,32 @@ struct traversal } } + int best_code = 0; + bool result = false; for (std::size_t i = 1; i < sbs.m_ranked_points.size(); i++) { typename sbs_type::rp const& ranked_point = sbs.m_ranked_points[i]; - if (ranked_point.rank == selected_rank - && ranked_point.direction == sort_by_side::dir_to) + + if (ranked_point.rank > selected_rank) { + // Sorted on rank, so it makes no sense to continue + break; + } + + int const code + = select_turn_in_cluster_union(selected_rank, ranked_point, + start_turn_index, start_op_index); + + if (code > best_code) + { + // It is 1 or higher and matching better than previous + best_code = code; turn_index = ranked_point.turn_index; op_index = ranked_point.operation_index; - - turn_type const& turn = m_turns[turn_index]; - turn_operation_type const& op = turn.operations[op_index]; - - if (op.enriched.count_left == 0 - && op.enriched.count_right > 0 - && ! op.visited.finalized()) - { - // In some cases interior rings might be generated with polygons - // on both sides - - // TODO: this should be finetuned such that checking - // finalized is not necessary - return true; - } + result = true; } } - return false; - } - - - inline bool all_operations_of_type(sort_by_side::rank_with_rings const& rwr, - operation_type op_type, - sort_by_side::direction_type dir) const - { - typedef std::set::const_iterator sit_type; - for (sit_type it = rwr.rings.begin(); it != rwr.rings.end(); ++it) - { - sort_by_side::ring_with_direction const& rwd = *it; - if (rwd.direction != dir) - { - return false; - } - turn_type const& turn = m_turns[rwd.turn_index]; - if (! turn.both(op_type)) - { - return false; - } - - // Check if this is not yet taken - turn_operation_type const& op = turn.operations[rwd.operation_index]; - if (op.visited.finalized()) - { - return false; - } - - } - return true; + return result; } inline bool analyze_cluster_intersection(signed_size_type& turn_index, @@ -467,12 +509,14 @@ struct traversal || intersection_pattern_common_interior2(selected_rank, aggregation) || intersection_pattern_common_interior3(selected_rank, aggregation) || intersection_pattern_common_interior4(selected_rank, aggregation) + || intersection_pattern_common_interior5(selected_rank, aggregation) + || intersection_pattern_common_interior6(selected_rank, aggregation) ; if (! detected) { - int incoming_region_id = 0; - std::set outgoing_region_ids; + signed_size_type incoming_region_id = 0; + std::set outgoing_region_ids; for (std::size_t i = 0; i < aggregation.size(); i++) { @@ -522,7 +566,7 @@ struct traversal { for (int i = 0; i < 2; i++) { - int const region_id = turn.operations[i].enriched.region_id; + signed_size_type const region_id = turn.operations[i].enriched.region_id; if (outgoing_region_ids.count(region_id) == 1) { selected_rank = 0; @@ -537,6 +581,9 @@ struct traversal if (selected_rank > 0) { + typename turn_operation_type::comparable_distance_type + min_remaining_distance = 0; + std::size_t selected_index = sbs.m_ranked_points.size(); for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++) { @@ -554,8 +601,13 @@ struct traversal continue; } - // Take the last turn from this rank - selected_index = i; + // Take turn with the smallest remaining distance + if (selected_index == sbs.m_ranked_points.size() + || ranked_op.remaining_distance < min_remaining_distance) + { + selected_index = i; + min_remaining_distance = ranked_op.remaining_distance; + } } } @@ -573,13 +625,13 @@ struct traversal inline bool select_turn_from_cluster(signed_size_type& turn_index, int& op_index, - signed_size_type start_turn_index, + signed_size_type start_turn_index, int start_op_index, segment_identifier const& previous_seg_id) const { bool const is_union = target_operation == operation_union; turn_type const& turn = m_turns[turn_index]; - BOOST_ASSERT(turn.cluster_id >= 0); + BOOST_ASSERT(turn.is_clustered()); typename Clusters::const_iterator mit = m_clusters.find(turn.cluster_id); BOOST_ASSERT(mit != m_clusters.end()); @@ -587,7 +639,7 @@ struct traversal cluster_info const& cinfo = mit->second; std::set const& ids = cinfo.turn_indices; - sbs_type sbs; + sbs_type sbs(m_strategy); for (typename std::set::const_iterator sit = ids.begin(); sit != ids.end(); ++sit) @@ -620,7 +672,8 @@ struct traversal if (is_union) { - result = select_from_cluster_union(turn_index, op_index, sbs); + result = select_from_cluster_union(turn_index, op_index, sbs, + start_turn_index, start_op_index); } else { @@ -633,7 +686,7 @@ struct traversal turn_type const& current_turn, segment_identifier const& previous_seg_id) { - sbs_type sbs; + sbs_type sbs(m_strategy); // Add this turn to the sort-by-side sorter for (int i = 0; i < 2; i++) @@ -661,7 +714,9 @@ struct traversal turn_operation_type const& start_op, int start_op_index) const { - if (OverlayType != overlay_buffer) + if (OverlayType != overlay_buffer + && OverlayType != overlay_dissolve_union + && OverlayType != overlay_dissolve_intersection) { return; } @@ -689,6 +744,9 @@ struct traversal bool const correct = ! start_turn.both(operation_union) + && start_op.seg_id.source_index == other_op.seg_id.source_index + && start_op.seg_id.multi_index == other_op.seg_id.multi_index + && start_op.seg_id.ring_index == other_op.seg_id.ring_index && start_op.seg_id.segment_index == to_vertex_index; #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE) @@ -762,7 +820,7 @@ struct traversal if (target_operation == operation_intersection) { bool const back_at_start_cluster - = current_turn.cluster_id >= 0 + = current_turn.is_clustered() && m_turns[start_turn_index].cluster_id == current_turn.cluster_id; if (turn_index == start_turn_index || back_at_start_cluster) @@ -773,7 +831,7 @@ struct traversal return true; } - if (current_turn.cluster_id < 0 + if (! current_turn.is_clustered() && current_turn.both(operation_intersection)) { if (analyze_ii_intersection(turn_index, op_index, @@ -784,10 +842,10 @@ struct traversal } } - if (current_turn.cluster_id >= 0) + if (current_turn.is_clustered()) { if (! select_turn_from_cluster(turn_index, op_index, - start_turn_index, previous_seg_id)) + start_turn_index, start_op_index, previous_seg_id)) { return false; } @@ -825,6 +883,7 @@ private : Turns& m_turns; Clusters const& m_clusters; RobustPolicy const& m_robust_policy; + SideStrategy m_strategy; Visitor& m_visitor; }; diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp index 12279d762..a8abea230 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp @@ -32,8 +32,8 @@ inline bool check_pairs(std::vector const& aggreg { sort_by_side::rank_with_rings const& curr = aggregation[i]; sort_by_side::rank_with_rings const& next = aggregation[i + 1]; - int const curr_id = curr.region_id(); - int const next_id = next.region_id(); + signed_size_type const curr_id = curr.region_id(); + signed_size_type const next_id = next.region_id(); bool const possible = curr.rings.size() == 2 @@ -249,7 +249,7 @@ inline bool intersection_pattern_common_interior4(std::size_t& selected_rank, //Rank 1 {13[0] (s:0, r:0, m:0) i T rgn: 2 ISO ->15} {11[1] (s:1, r:1, m:0) i T rgn: 2 ISO ->15} //Rank 2 {13[0] (s:0, r:0, m:0) i F rgn: 2 ISO} {11[1] (s:1, r:1, m:0) i F rgn: 2 ISO} - // LEAVING (in two different directions, take last one) + // LEAVING (in two different directions, take penultimate one) //Rank 3 {10[1] (s:1, m:0) i T rgn: 1 ISO ->0} //Rank 4 {11[0] (s:0, m:0) i T rgn: 1 ISO ->12} @@ -292,12 +292,157 @@ inline bool intersection_pattern_common_interior4(std::size_t& selected_rank, // Check if pairs 1,2 (and possibly 3,4 and 5,6 etc) satisfy if (check_pairs(aggregation, incoming.region_id(), 1, n - 3)) { - selected_rank = n - 1; + selected_rank = n - 2; return true; } return false; } +inline bool intersection_pattern_common_interior5(std::size_t& selected_rank, + std::vector const& aggregation) +{ + // Pattern: isolated regions + + // See #case_recursive_boxes_65 + + // INCOMING: + // Rank 0 {19[0] (s:0, r:2, m:0) i F rgn: 4 ISO} + + // Rank 1 {19[1] (s:1, m:0) i T rgn: 1 ISO FIN ->18 (2.5)} + // Rank 2 {21[1] (s:1, m:2) i F rgn: 1 ISO} + // Rank 3 {21[1] (s:1, m:2) i T rgn: 1 ISO ->17 (2)} + // Rank 4 {19[1] (s:1, m:0) i F rgn: 1 ISO FIN} + + // LEAVING (take this one): + // Rank 5 {19[0] (s:0, r:2, m:0) i T rgn: 4 ISO ->22 (1)} + + std::size_t const n = aggregation.size(); + if (n < 3) + { + return false; + } + + sort_by_side::rank_with_rings const& incoming = aggregation.front(); + sort_by_side::rank_with_rings const& outgoing = aggregation.back(); + + bool const incoming_ok = + incoming.all_from() + && incoming.has_unique_region_id() + && incoming.is_isolated(); + + if (! incoming_ok) + { + return false; + } + + signed_size_type const incoming_region_id = incoming.region_id(); + + bool const outgoing_ok = + outgoing.all_to() + && outgoing.has_unique_region_id() + && outgoing.is_isolated() + && outgoing.region_id() == incoming_region_id; + + if (! outgoing_ok) + { + return false; + } + + selected_rank = n - 1; + bool other_region = true; + + // Assumed is that other regions go (T) and come back (F) + for (std::size_t i = 1; i < n - 1; i++) + { + sort_by_side::rank_with_rings const& rwr = aggregation[i]; + if (! rwr.has_unique_region_id() || ! rwr.is_isolated()) + { + return false; + } + signed_size_type const region_id = rwr.region_id(); + if (other_region && region_id != incoming_region_id) + { + // OK + } + else if (other_region && region_id == incoming_region_id) + { + // OK, next phase (same region as incoming region) + selected_rank = i; + other_region = false; + } + else if (! other_region && region_id != incoming_region_id) + { + // After that the region is the same is incoming, it should + // stay like that + return false; + } + } + + return true; +} + +inline bool intersection_pattern_common_interior6(std::size_t& selected_rank, + std::vector const& aggregation) +{ + // Pattern: isolated regions in between + + // See #case_recursive_boxes_75 + + // Incoming: one region + // In between: several rings having isolated region, all the same + // Outging == incoming + + std::size_t const n = aggregation.size(); + if (n < 3) + { + return false; + } + + sort_by_side::rank_with_rings const& incoming = aggregation.front(); + sort_by_side::rank_with_rings const& outgoing = aggregation.back(); + sort_by_side::rank_with_rings const& first_isolated = aggregation[2]; + + bool const incoming_ok = + incoming.all_from() + && incoming.has_unique_region_id() + && ! incoming.is_isolated(); + + if (! incoming_ok) + { + return false; + } + + signed_size_type const incoming_region_id = incoming.region_id(); + + bool const outgoing_ok = + outgoing.all_to() + && outgoing.has_unique_region_id() + && ! outgoing.is_isolated() + && outgoing.region_id() == incoming_region_id; + + if (! outgoing_ok) + { + return false; + } + + const signed_size_type isolated_region_id = first_isolated.region_id(); + + for (std::size_t i = 1; i < n - 1; i++) + { + sort_by_side::rank_with_rings const& rwr = aggregation[i]; + if (! rwr.has_unique_region_id() + || ! rwr.is_isolated() + || rwr.region_id() != isolated_region_id) + { + return false; + } + } + + selected_rank = n - 1; + + return true; +} + }} // namespace detail::overlay #endif // DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp index 6a346423e..4df3f6e7a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp @@ -41,6 +41,7 @@ template typename Geometry1, typename Geometry2, typename Turns, + typename TurnInfoMap, typename Clusters, typename IntersectionStrategy, typename RobustPolicy, @@ -49,9 +50,13 @@ template > struct traversal_ring_creator { - typedef traversal - traversal_type; + typedef traversal + < + Reverse1, Reverse2, OverlayType, + Geometry1, Geometry2, Turns, Clusters, + RobustPolicy, typename IntersectionStrategy::side_strategy_type, + Visitor + > traversal_type; typedef typename boost::range_value::type turn_type; typedef typename turn_type::turn_operation_type turn_operation_type; @@ -60,13 +65,17 @@ struct traversal_ring_creator = operation_from_overlay::value; inline traversal_ring_creator(Geometry1 const& geometry1, Geometry2 const& geometry2, - Turns& turns, Clusters const& clusters, + Turns& turns, TurnInfoMap& turn_info_map, + Clusters const& clusters, IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Visitor& visitor) - : m_trav(geometry1, geometry2, turns, clusters, robust_policy,visitor) + : m_trav(geometry1, geometry2, turns, clusters, + robust_policy, intersection_strategy.get_side_strategy(), + visitor) , m_geometry1(geometry1) , m_geometry2(geometry2) , m_turns(turns) + , m_turn_info_map(turn_info_map) , m_clusters(clusters) , m_intersection_strategy(intersection_strategy) , m_robust_policy(robust_policy) @@ -103,12 +112,14 @@ struct traversal_ring_creator { geometry::copy_segments(m_geometry1, previous_op.seg_id, to_vertex_index, + m_intersection_strategy.get_side_strategy(), m_robust_policy, current_ring); } else { geometry::copy_segments(m_geometry2, previous_op.seg_id, to_vertex_index, + m_intersection_strategy.get_side_strategy(), m_robust_policy, current_ring); } } @@ -152,6 +163,7 @@ struct traversal_ring_creator turn_type& current_turn = m_turns[turn_index]; turn_operation_type& op = current_turn.operations[op_index]; detail::overlay::append_no_dups_or_spikes(current_ring, current_turn.point, + m_intersection_strategy.get_side_strategy(), m_robust_policy); // Register the visit @@ -169,6 +181,7 @@ struct traversal_ring_creator turn_operation_type& start_op = m_turns[start_turn_index].operations[start_op_index]; detail::overlay::append_no_dups_or_spikes(ring, start_turn.point, + m_intersection_strategy.get_side_strategy(), m_robust_policy); signed_size_type current_turn_index = start_turn_index; @@ -193,7 +206,7 @@ struct traversal_ring_creator return traverse_error_none; } - if (start_turn.cluster_id >= 0) + if (start_turn.is_clustered()) { turn_type const& turn = m_turns[current_turn_index]; if (turn.cluster_id == start_turn.cluster_id) @@ -271,10 +284,12 @@ struct traversal_ring_creator if (geometry::num_points(ring) >= min_num_points) { - clean_closing_dups_and_spikes(ring, m_robust_policy); + clean_closing_dups_and_spikes(ring, + m_intersection_strategy.get_side_strategy(), + m_robust_policy); rings.push_back(ring); - m_trav.finalize_visit_info(); + m_trav.finalize_visit_info(m_turn_info_map); finalized_ring_size++; } } @@ -335,6 +350,7 @@ private: Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; Turns& m_turns; + TurnInfoMap& m_turn_info_map; // contains turn-info information per ring Clusters const& m_clusters; IntersectionStrategy const& m_intersection_strategy; RobustPolicy const& m_robust_policy; diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 0b4f393ef..24fe2b98e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -48,17 +48,24 @@ template > struct traversal_switch_detector { - enum isolation_type { isolation_unknown = -1, isolation_no = 0, isolation_yes = 1 }; + enum isolation_type + { + isolation_unknown = -1, + isolation_no = 0, + isolation_yes = 1, + isolation_multiple = 2 + }; typedef typename boost::range_value::type turn_type; typedef typename turn_type::turn_operation_type turn_operation_type; + typedef std::set set_type; // Per ring, first turns are collected (in turn_indices), and later // a region_id is assigned struct merged_ring_properties { signed_size_type region_id; - std::set turn_indices; + set_type turn_indices; merged_ring_properties() : region_id(-1) @@ -68,7 +75,8 @@ struct traversal_switch_detector struct connection_properties { std::size_t count; - std::set cluster_indices; + // Contains turn-index OR, if clustered, minus-cluster_id + set_type unique_turn_ids; connection_properties() : count(0) {} @@ -82,6 +90,7 @@ struct traversal_switch_detector { signed_size_type region_id; isolation_type isolated; + set_type unique_turn_ids; // Maps from connected region_id to their properties connection_map connected_region_counts; @@ -96,7 +105,7 @@ struct traversal_switch_detector typedef std::map merge_map; typedef std::map region_connection_map; - typedef std::set::const_iterator set_iterator; + typedef set_type::const_iterator set_iterator; inline traversal_switch_detector(Geometry1 const& geometry1, Geometry2 const& geometry2, Turns& turns, Clusters& clusters, @@ -110,111 +119,232 @@ struct traversal_switch_detector { } - isolation_type get_isolation(region_properties const& properties, - signed_size_type parent_region_id, - const std::set& visited) + bool inspect_difference(set_type& turn_id_difference, + set_type const& turn_ids, + set_type const& other_turn_ids) const { - if (properties.isolated != isolation_unknown) + // TODO: consider if std::set_difference can be used in the final version + int const turn_count = turn_ids.size(); + int const other_turn_count = other_turn_ids.size(); + + // First quick check on size (TODO: implement multiple-multiple connections) + if (turn_count - other_turn_count > 1) { - return properties.isolated; + return false; } - bool all_colocated = true; - int unique_cluster_id = -1; - for (typename connection_map::const_iterator it = properties.connected_region_counts.begin(); - all_colocated && it != properties.connected_region_counts.end(); ++it) + // Check if all turns are also present in the connection. + // The difference is returned + for (set_iterator it = turn_ids.begin(); it != turn_ids.end(); ++it) + { + signed_size_type const& id = *it; + if (other_turn_ids.count(id) == 0) + { + turn_id_difference.insert(id); + } + } + return true; + } + + bool one_connection_to_another_region(region_properties const& region) const + { + // For example: + // +----------------------+ + // | __ | + // | / \| + // | | x + // | \__/| + // | | + // +----------------------+ + + if (region.connected_region_counts.size() == 1) + { + connection_properties const& cprop = region.connected_region_counts.begin()->second; + return cprop.count <= 1; + } + return region.connected_region_counts.empty(); + } + + // TODO: might be combined with previous + bool multiple_connections_to_one_region(region_properties const& region) const + { + // For example: + // +----------------------+ + // | __ | + // | / \| + // | | x + // | \ /| + // | / \| + // | | x + // | \__/| + // | | + // +----------------------+ + + if (region.connected_region_counts.size() == 1) + { + connection_properties const& cprop = region.connected_region_counts.begin()->second; + return cprop.count > 1; + } + return false; + } + + bool one_connection_to_multiple_regions(region_properties const& region) const + { + // For example: + // +----------------------+ + // | __ | __ + // | / \|/ | + // | | x | + // | \__/|\__| + // | | + // +----------------------+ + + bool first = true; + signed_size_type first_turn_id = 0; + for (typename connection_map::const_iterator it = region.connected_region_counts.begin(); + it != region.connected_region_counts.end(); ++it) { connection_properties const& cprop = it->second; - if (cprop.cluster_indices.size() != 1) + + if (cprop.count != 1) { - // Either no cluster (non colocated point), or more clusters - all_colocated = false; + return false; } - int const cluster_id = *cprop.cluster_indices.begin(); - if (cluster_id == -1) + signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin(); + if (first) { - all_colocated = false; + first_turn_id = unique_turn_id; + first = false; } - else if (unique_cluster_id == -1) + else if (first_turn_id != unique_turn_id) { - unique_cluster_id = cluster_id; - } - else if (unique_cluster_id != cluster_id) - { - all_colocated = false; + return false; } } - if (all_colocated) - { - return isolation_yes; - } + return true; + } + bool has_only_isolated_children(region_properties const& region) const + { + bool first_with_turn = true; + bool first_with_multiple = true; + signed_size_type first_turn_id = 0; + signed_size_type first_multiple_region_id = 0; - // It is isolated if there is only one connection, or if there are more connections but all - // of them are isolated themselves, or if there are more connections - // but they are all colocated - std::size_t non_isolation_count = 0; - bool child_not_isolated = false; - for (typename connection_map::const_iterator it = properties.connected_region_counts.begin(); - it != properties.connected_region_counts.end(); ++it) + for (typename connection_map::const_iterator it = region.connected_region_counts.begin(); + it != region.connected_region_counts.end(); ++it) { signed_size_type const region_id = it->first; connection_properties const& cprop = it->second; - if (region_id == parent_region_id) - { - // Normal situation, skip its direct parent - continue; - } - if (visited.count(region_id) > 0) - { - // Find one of its ancestors again, this is a ring. Not isolated. - return isolation_no; - } - if (cprop.count > 1) - { - return isolation_no; - } - - typename region_connection_map::iterator mit = m_connected_regions.find(region_id); + typename region_connection_map::const_iterator mit = m_connected_regions.find(region_id); if (mit == m_connected_regions.end()) { // Should not occur - continue; + return false; } - std::set vis = visited; - vis.insert(parent_region_id); + region_properties const& connected_region = mit->second; - region_properties& prop = mit->second; - if (prop.isolated == isolation_unknown) + bool const multiple = connected_region.isolated == isolation_multiple; + + if (cprop.count != 1) { - isolation_type const iso = get_isolation(prop, properties.region_id, vis); - prop.isolated = iso; - if (iso == isolation_no) + if (! multiple) { - child_not_isolated = true; + return false; + } + + // It connects multiple times to an isolated region. + // This is allowed as long as it happens only once + if (first_with_multiple) + { + first_multiple_region_id = connected_region.region_id; + first_with_multiple = false; + } + else if (first_multiple_region_id != connected_region.region_id) + { + return false; + } + + // Turns in region should be either present in the connection, + // of form part of the connection with the other region + set_type diff; + if (! inspect_difference(diff, region.unique_turn_ids, + connected_region.unique_turn_ids)) + { + return false; + } + if (diff.size() > 1) + { + // For now: + return false; } } - if (prop.isolated == isolation_no) + + if (connected_region.isolated != isolation_yes && ! multiple) { - non_isolation_count++; + signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin(); + if (first_with_turn) + { + first_turn_id = unique_turn_id; + first_with_turn = false; + } + else if (first_turn_id != unique_turn_id) + { + return false; + } } } - - return child_not_isolated || non_isolation_count > 1 ? isolation_no : isolation_yes; + // If there is only one connection (with a 'parent'), and all other + // connections are itself isolated, it is isolated + return true; } void get_isolated_regions() { - for (typename region_connection_map::iterator it = m_connected_regions.begin(); + typedef typename region_connection_map::iterator it_type; + + // First time: check regions isolated (one connection only), + // semi-isolated (multiple connections between same region), + // and complex isolated (connection with multiple rings but all + // at same point) + for (it_type it = m_connected_regions.begin(); it != m_connected_regions.end(); ++it) { region_properties& properties = it->second; - if (properties.isolated == isolation_unknown) + if (one_connection_to_another_region(properties)) { - std::set visited; - properties.isolated = get_isolation(properties, properties.region_id, visited); + properties.isolated = isolation_yes; + } + else if (multiple_connections_to_one_region(properties)) + { + properties.isolated = isolation_multiple; + } + else if (one_connection_to_multiple_regions(properties)) + { + properties.isolated = isolation_yes; + } + } + + // Propagate isolation to next level + // TODO: should be optimized + std::size_t defensive_check = 0; + bool changed = true; + while (changed && defensive_check++ < m_connected_regions.size()) + { + changed = false; + for (it_type it = m_connected_regions.begin(); + it != m_connected_regions.end(); ++it) + { + region_properties& properties = it->second; + + if (properties.isolated == isolation_unknown + && has_only_isolated_children(properties)) + { + properties.isolated = isolation_yes; + changed = true; + } } } } @@ -238,7 +368,7 @@ struct traversal_switch_detector } } - void assign_regions() + void assign_region_ids() { for (typename merge_map::const_iterator it = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it) @@ -259,39 +389,53 @@ struct traversal_switch_detector op.enriched.region_id = properties.region_id; } } - signed_size_type const& id0 = turn.operations[0].enriched.region_id; - signed_size_type const& id1 = turn.operations[1].enriched.region_id; - if (id0 != id1 && id0 != -1 && id1 != -1) + } + } + } + + void assign_connected_regions() + { + for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index) + { + turn_type const& turn = m_turns[turn_index]; + + signed_size_type const unique_turn_id + = turn.is_clustered() ? -turn.cluster_id : turn_index; + + turn_operation_type op0 = turn.operations[0]; + turn_operation_type op1 = turn.operations[1]; + + signed_size_type const& id0 = op0.enriched.region_id; + signed_size_type const& id1 = op1.enriched.region_id; + + // Add region (by assigning) and add involved turns + if (id0 != -1) + { + m_connected_regions[id0].region_id = id0; + m_connected_regions[id0].unique_turn_ids.insert(unique_turn_id); + } + if (id1 != -1 && id0 != id1) + { + m_connected_regions[id1].region_id = id1; + m_connected_regions[id1].unique_turn_ids.insert(unique_turn_id); + } + + if (id0 != id1 && id0 != -1 && id1 != -1) + { + // Assign connections + connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1]; + connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0]; + + // Reference this turn or cluster to later check uniqueness on ring + if (prop0.unique_turn_ids.count(unique_turn_id) == 0) { - // Force insertion - m_connected_regions[id0].region_id = id0; - m_connected_regions[id1].region_id = id1; - - connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1]; - connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0]; - - if (turn.cluster_id < 0) - { - // Turn is not colocated, add reference to connection - prop0.count++; - prop1.count++; - } - else - { - // Turn is colocated, only add region reference if it was not yet registered - if (prop0.cluster_indices.count(turn.cluster_id) == 0) - { - prop0.count++; - } - if (prop1.cluster_indices.count(turn.cluster_id) == 0) - { - prop1.count++; - } - } - // Insert cluster-id (also -1 is inserted - reinsertion of - // same cluster id is OK) - prop0.cluster_indices.insert(turn.cluster_id); - prop1.cluster_indices.insert(turn.cluster_id); + prop0.count++; + prop0.unique_turn_ids.insert(unique_turn_id); + } + if (prop1.unique_turn_ids.count(unique_turn_id) == 0) + { + prop1.count++; + prop1.unique_turn_ids.insert(unique_turn_id); } } } @@ -306,7 +450,7 @@ struct traversal_switch_detector return false; } - if (turn.cluster_id == -1) + if (! turn.is_clustered()) { // If it is a uu/ii-turn (non clustered), it is never same region return ! (turn.both(operation_union) || turn.both(operation_intersection)); @@ -320,39 +464,22 @@ struct traversal_switch_detector == turn.operations[1].enriched.zone; } - // If a cluster contains an ii/cc it is not same region (for intersection) - typename Clusters::const_iterator it = m_clusters.find(turn.cluster_id); - if (it == m_clusters.end()) - { - // Should not occur - return true; - } - - cluster_info const& cinfo = it->second; - for (set_iterator sit = cinfo.turn_indices.begin(); - sit != cinfo.turn_indices.end(); ++sit) - { - turn_type const& cluster_turn = m_turns[*sit]; - if (cluster_turn.both(operation_union) - || cluster_turn.both(operation_intersection)) - { - return false; - } - } - - // It is the same region - return false; + // For an intersection, two regions connect if they are not ii + // (ii-regions are isolated) or, in some cases, not iu (for example + // when a multi-polygon is inside an interior ring and connecting it) + return ! (turn.both(operation_intersection) + || turn.combination(operation_intersection, operation_union)); } - inline int get_region_id(turn_operation_type const& op) const + inline signed_size_type get_region_id(turn_operation_type const& op) const { return op.enriched.region_id; } void create_region(signed_size_type& new_region_id, ring_identifier const& ring_id, - merged_ring_properties& properties, int region_id = -1) + merged_ring_properties& properties, signed_size_type region_id = -1) { if (properties.region_id > 0) { @@ -400,7 +527,7 @@ struct traversal_switch_detector } void propagate_region(signed_size_type& new_region_id, - ring_identifier const& ring_id, int region_id) + ring_identifier const& ring_id, signed_size_type region_id) { typename merge_map::iterator it = m_turns_per_ring.find(ring_id); if (it != m_turns_per_ring.end()) @@ -438,7 +565,7 @@ struct traversal_switch_detector } } - // All rings having turns are in the map. Now iterate them + // All rings having turns are in turns/ring map. Process them. { signed_size_type new_region_id = 1; for (typename merge_map::iterator it @@ -447,7 +574,8 @@ struct traversal_switch_detector create_region(new_region_id, it->first, it->second); } - assign_regions(); + assign_region_ids(); + assign_connected_regions(); get_isolated_regions(); assign_isolation(); } @@ -464,9 +592,8 @@ struct traversal_switch_detector } // A touching cluster, gather regions - std::set regions; - - std::set const& ids = cinfo.turn_indices; + set_type regions; + set_type const& ids = cinfo.turn_indices; #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) std::cout << "SWITCH EXAMINE CLUSTER " << it->first << std::endl; @@ -476,14 +603,10 @@ struct traversal_switch_detector { signed_size_type turn_index = *sit; turn_type const& turn = m_turns[turn_index]; - if (turn.colocated_ii && ! turn.colocated_uu) - { - continue; - } for (int oi = 0; oi < 2; oi++) { - int const region = get_region_id(turn.operations[oi]); - regions.insert(region); + signed_size_type const region_id = get_region_id(turn.operations[oi]); + regions.insert(region_id); } } // Switch source if this cluster connects the same region @@ -497,7 +620,7 @@ struct traversal_switch_detector if (turn.discarded || turn.blocked() - || turn.cluster_id >= 0 + || turn.is_clustered() || ! (turn.both(operation_union) || turn.both(operation_intersection))) { // Skip discarded, blocked, non-uu/ii and clustered turns @@ -513,8 +636,8 @@ struct traversal_switch_detector continue; } - int const region0 = get_region_id(turn.operations[0]); - int const region1 = get_region_id(turn.operations[1]); + signed_size_type const region0 = get_region_id(turn.operations[0]); + signed_size_type const region1 = get_region_id(turn.operations[1]); // Switch sources for same region turn.switch_source = region0 == region1; @@ -529,7 +652,7 @@ struct traversal_switch_detector turn_type const& turn = m_turns[turn_index]; if ((turn.both(operation_union) || turn.both(operation_intersection)) - && turn.cluster_id < 0) + && ! turn.is_clustered()) { std::cout << "UU/II SWITCH RESULT " << turn_index << " -> " diff --git a/include/boost/geometry/algorithms/detail/overlay/traverse.hpp b/include/boost/geometry/algorithms/detail/overlay/traverse.hpp index 058f6c945..b9cbea312 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traverse.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traverse.hpp @@ -62,14 +62,16 @@ public : typename RobustPolicy, typename Turns, typename Rings, - typename Visitor, - typename Clusters + typename TurnInfoMap, + typename Clusters, + typename Visitor > static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Turns& turns, Rings& rings, + TurnInfoMap& turn_info_map, Clusters& clusters, Visitor& visitor) { @@ -89,11 +91,11 @@ public : < Reverse1, Reverse2, OverlayType, Geometry1, Geometry2, - Turns, Clusters, + Turns, TurnInfoMap, Clusters, IntersectionStrategy, RobustPolicy, Visitor, Backtrack - > trav(geometry1, geometry2, turns, clusters, + > trav(geometry1, geometry2, turns, turn_info_map, clusters, intersection_strategy, robust_policy, visitor); std::size_t finalized_ring_size = boost::size(rings); diff --git a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp index 06bf1a293..3ed75cf09 100644 --- a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -89,22 +89,21 @@ struct turn_info Point point; method_type method; - signed_size_type cluster_id; // For multiple turns on same location, >= 0. Else -1 + bool touch_only; // True in case of method touch(interior) and lines do not cross + signed_size_type cluster_id; // For multiple turns on same location, > 0. Else -1. 0 is unused. bool discarded; - // TODO: move this to enriched - bool colocated_ii; // Colocated with a ii turn (TODO: or a ix turn) - bool colocated_uu; // Colocated with a uu turn or a ux turn + bool has_colocated_both; // Colocated with a uu turn (for union) or ii (other) bool switch_source; // For u/u turns which can either switch or not Container operations; inline turn_info() : method(method_none) + , touch_only(false) , cluster_id(-1) , discarded(false) - , colocated_ii(false) - , colocated_uu(false) + , has_colocated_both(false) , switch_source(false) {} @@ -136,6 +135,10 @@ struct turn_info { return has(operation_blocked); } + inline bool is_clustered() const + { + return cluster_id > 0; + } private : inline bool has12(operation_type type1, operation_type type2) const diff --git a/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp b/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp index 607ba8153..b8ea5e30e 100644 --- a/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp +++ b/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp @@ -5,10 +5,11 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2013-2015 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017 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 @@ -19,11 +20,13 @@ #include #include +#include #include #include #include #include + namespace boost { namespace geometry { @@ -32,6 +35,26 @@ namespace boost { namespace geometry namespace detail { +template +inline bool collinear_point_is_spike_or_equal(Point1 const& last_point, + Point2 const& segment_a, + Point3 const& segment_b) +{ + // Check if segment is equal + int const sgn_x1 = sign_of_difference<0>(last_point, segment_b); + int const sgn_y1 = sign_of_difference<1>(last_point, segment_b); + if (sgn_x1 == 0 && sgn_y1 == 0) + { + return true; + } + + // Check if segment moves forward + int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); + int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); + + return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; +} + // Checks if a point ("last_point") causes a spike w.r.t. // the specified two other points (segment_a, segment_b) // @@ -42,33 +65,29 @@ namespace detail // So specify last point first, then (a,b) // The segment's orientation does matter: if lp is to the right of b // no spike is reported -template -static inline bool point_is_spike_or_equal(Point1 const& last_point, - Point2 const& segment_a, - Point3 const& segment_b) +template +< + typename Point1, typename Point2, typename Point3, + typename SideStrategy +> +static inline bool point_is_spike_or_equal(Point1 const& last_point, // prev | back + Point2 const& segment_a, // next | back - 2 + Point3 const& segment_b, // curr | back - 1 | spike's vertex + SideStrategy const& strategy) { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy; - - int const side = side_strategy::apply(last_point, segment_a, segment_b); + int const side = strategy.apply(segment_a, segment_b, last_point); if (side == 0) { // Last point is collinear w.r.t previous segment. - // Check if it is equal - int const sgn_x1 = sign_of_difference<0>(last_point, segment_b); - int const sgn_y1 = sign_of_difference<1>(last_point, segment_b); - if (sgn_x1 == 0 && sgn_y1 == 0) - { - return true; - } - - // Check if it moves forward - int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); - int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); - - return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; +#ifdef BOOST_GEOMETRY_ENABLE_POINT_IS_SPIKE_OR_EQUAL_TEST + bool r1 = collinear_point_is_spike_or_equal(last_point, segment_a, segment_b); + bool r2 = direction_code(segment_a, segment_b, last_point) < 1; + if (r1 != r2) + std::cout << "spike detection failure with: " << r1 << " " << r2 << std::endl; + return r2; +#else + return direction_code(segment_a, segment_b, last_point) < 1; +#endif } return false; } @@ -78,14 +97,16 @@ template typename Point1, typename Point2, typename Point3, + typename SideStrategy, typename RobustPolicy > static inline bool point_is_spike_or_equal(Point1 const& last_point, Point2 const& segment_a, Point3 const& segment_b, + SideStrategy const& strategy, RobustPolicy const& robust_policy) { - if (point_is_spike_or_equal(last_point, segment_a, segment_b)) + if (point_is_spike_or_equal(last_point, segment_a, segment_b, strategy)) { return true; } @@ -111,7 +132,8 @@ static inline bool point_is_spike_or_equal(Point1 const& last_point, ( last_point_rob, segment_a_rob, - segment_b_rob + segment_b_rob, + strategy ); } diff --git a/include/boost/geometry/algorithms/detail/point_on_border.hpp b/include/boost/geometry/algorithms/detail/point_on_border.hpp index a5b03d842..831081aa6 100644 --- a/include/boost/geometry/algorithms/detail/point_on_border.hpp +++ b/include/boost/geometry/algorithms/detail/point_on_border.hpp @@ -84,43 +84,49 @@ struct midpoint_helper template struct point_on_range { + // Version with iterator + template + static inline bool apply(Point& point, Iterator begin, Iterator end) + { + Iterator it = begin; + if (it == end) + { + return false; + } + + if (! Midpoint) + { + geometry::detail::conversion::convert_point_to_point(*it, point); + return true; + } + + Iterator prev = it++; + + // Go to next non-duplicate point + while (it != end + && detail::equals::equals_point_point(*it, *prev)) + { + prev = it++; + } + if (it != end) + { + return midpoint_helper + < + Point, + 0, dimension::value + >::apply(point, *prev, *it); + } + return false; + } + + // Version with range template static inline bool apply(Point& point, Range const& range) { typedef typename geometry::cs_tag::type cs_tag; BOOST_STATIC_ASSERT((! Midpoint || boost::is_same::value)); - const std::size_t n = boost::size(range); - if (Midpoint && n > 1) - { - typedef typename boost::range_iterator - < - Range const - >::type iterator; - - iterator it = boost::begin(range); - iterator prev = it++; - while (it != boost::end(range) - && detail::equals::equals_point_point(*it, *prev)) - { - prev = it++; - } - if (it != boost::end(range)) - { - return midpoint_helper - < - Point, - 0, dimension::value - >::apply(point, *prev, *it); - } - } - - if (n > 0) - { - geometry::detail::conversion::convert_point_to_point(*boost::begin(range), point); - return true; - } - return false; + return apply(point, boost::begin(range), boost::end(range)); } }; diff --git a/include/boost/geometry/algorithms/detail/relate/less.hpp b/include/boost/geometry/algorithms/detail/relate/less.hpp deleted file mode 100644 index 462fcc35f..000000000 --- a/include/boost/geometry/algorithms/detail/relate/less.hpp +++ /dev/null @@ -1,83 +0,0 @@ -// 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 - -#include -#include -#include - -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/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index f1b4fdf81..ddbd7d615 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -260,17 +260,19 @@ struct linear_areal if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; + typedef typename IntersectionStrategy::template point_in_geometry_strategy::type within_strategy_type; + within_strategy_type const within_strategy = intersection_strategy.template get_point_in_geometry_strategy(); boundary_checker boundary_checker1(geometry1); no_turns_la_linestring_pred < Geometry2, Result, - typename IntersectionStrategy::template point_in_geometry_strategy::type, + within_strategy_type, boundary_checker, TransposeResult > pred1(geometry2, result, - intersection_strategy.template get_point_in_geometry_strategy(), + within_strategy, boundary_checker1); for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) diff --git a/include/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp b/include/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp index 47c6963b8..8d5f21555 100644 --- a/include/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp @@ -121,7 +121,7 @@ struct multi_point_geometry_eb template bool apply(Point const& boundary_point) { - if (! std::binary_search(m_points.begin(), m_points.end(), boundary_point, relate::less())) + if (! std::binary_search(m_points.begin(), m_points.end(), boundary_point, geometry::less<>())) { m_boundary_found = true; return false; @@ -143,7 +143,7 @@ struct multi_point_geometry_eb typedef typename boost::range_value::type point_type; typedef std::vector points_type; points_type points(boost::begin(multi_point), boost::end(multi_point)); - std::sort(points.begin(), points.end(), relate::less()); + std::sort(points.begin(), points.end(), geometry::less<>()); boundary_visitor visitor(points); tc.for_each_boundary_point(visitor); diff --git a/include/boost/geometry/algorithms/detail/relate/point_point.hpp b/include/boost/geometry/algorithms/detail/relate/point_point.hpp index 68d8be031..e0bed72ba 100644 --- a/include/boost/geometry/algorithms/detail/relate/point_point.hpp +++ b/include/boost/geometry/algorithms/detail/relate/point_point.hpp @@ -21,9 +21,10 @@ #include #include -#include #include +#include + namespace boost { namespace geometry { @@ -213,7 +214,9 @@ struct multipoint_multipoint // 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()); + + geometry::less<> const less = geometry::less<>(); + std::sort(points.begin(), points.end(), less); bool found_inside = false; bool found_outside = false; @@ -224,7 +227,7 @@ struct multipoint_multipoint it != boost::end(iterated_mpt) ; ++it ) { bool ii = - std::binary_search(points.begin(), points.end(), *it, less()); + std::binary_search(points.begin(), points.end(), *it, less); if ( ii ) found_inside = true; else diff --git a/include/boost/geometry/algorithms/detail/relate/topology_check.hpp b/include/boost/geometry/algorithms/detail/relate/topology_check.hpp index 67cc554cf..810466ec0 100644 --- a/include/boost/geometry/algorithms/detail/relate/topology_check.hpp +++ b/include/boost/geometry/algorithms/detail/relate/topology_check.hpp @@ -13,7 +13,8 @@ #include -#include + +#include #include #include @@ -165,6 +166,8 @@ private: m_endpoints.reserve(boost::size(m_mls) * 2); + m_has_interior = false; + typedef typename boost::range_iterator::type ls_iterator; for ( ls_iterator it = boost::begin(m_mls) ; it != boost::end(m_mls) ; ++it ) { @@ -212,7 +215,7 @@ private: if (! m_endpoints.empty() ) { - std::sort(m_endpoints.begin(), m_endpoints.end(), relate::less()); + std::sort(m_endpoints.begin(), m_endpoints.end(), geometry::less<>()); m_has_boundary = find_odd_count(m_endpoints.begin(), m_endpoints.end()); } @@ -222,7 +225,7 @@ private: template static inline std::size_t count_equal(It first, It last, Point const& point) { - std::pair rng = std::equal_range(first, last, point, relate::less()); + std::pair rng = std::equal_range(first, last, point, geometry::less<>()); return (std::size_t)std::distance(rng.first, rng.second); } diff --git a/include/boost/geometry/algorithms/detail/sections/section_functions.hpp b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp index 7bc5c0804..d283784e2 100644 --- a/include/boost/geometry/algorithms/detail/sections/section_functions.hpp +++ b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -19,6 +19,9 @@ #include #include +// For spherical/geographic longitudes covered_by point/box +#include + namespace boost { namespace geometry { @@ -27,6 +30,90 @@ namespace boost { namespace geometry namespace detail { namespace section { +template +< + std::size_t Dimension, + typename Geometry, + typename CastedCSTag = typename tag_cast + < + typename cs_tag::type, + spherical_tag + >::type +> +struct preceding_check +{ + template + static inline bool apply(int dir, Point const& point, Box const& /*point_box*/, Box const& other_box) + { + return (dir == 1 && get(point) < get(other_box)) + || (dir == -1 && get(point) > get(other_box)); + } +}; + +template +struct preceding_check<0, Geometry, spherical_tag> +{ + template + static inline bool apply(int dir, Point const& point, Box const& point_box, Box const& other_box) + { + typedef typename select_coordinate_type + < + Point, Box + >::type calc_t; + typedef typename coordinate_system::type::units units_t; + + calc_t const c0 = 0; + + calc_t const value = get<0>(point); + calc_t const other_min = get(other_box); + calc_t const other_max = get(other_box); + + bool const pt_covered = strategy::within::covered_by_range + < + Point, 0, spherical_tag + >::apply(value, + other_min, + other_max); + + if (pt_covered) + { + return false; + } + + if (dir == 1) + { + calc_t const diff_min = math::longitude_distance_signed + < + units_t, calc_t + >(other_min, value); + + calc_t const diff_min_min = math::longitude_distance_signed + < + units_t, calc_t + >(other_min, get(point_box)); + + return diff_min < c0 && diff_min_min <= c0 && diff_min_min <= diff_min; + } + else if (dir == -1) + { + calc_t const diff_max = math::longitude_distance_signed + < + units_t, calc_t + >(other_max, value); + + calc_t const diff_max_max = math::longitude_distance_signed + < + units_t, calc_t + >(other_max, get(point_box)); + + return diff_max > c0 && diff_max_max >= c0 && diff_max <= diff_max_max; + } + + return false; + } +}; + + template < std::size_t Dimension, @@ -34,14 +121,15 @@ template typename RobustBox, typename RobustPolicy > -static inline bool preceding(int dir, Point const& point, - RobustBox const& robust_box, - RobustPolicy const& robust_policy) +static inline bool preceding(int dir, + Point const& point, + RobustBox const& point_robust_box, + RobustBox const& other_robust_box, + RobustPolicy const& robust_policy) { typename geometry::robust_point_type::type robust_point; geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get(robust_point) < get(robust_box)) - || (dir == -1 && get(robust_point) > get(robust_box)); + return preceding_check::apply(dir, robust_point, point_robust_box, other_robust_box); } template @@ -51,14 +139,13 @@ template typename RobustBox, typename RobustPolicy > -static inline bool exceeding(int dir, Point const& point, - RobustBox const& robust_box, - RobustPolicy const& robust_policy) +static inline bool exceeding(int dir, + Point const& point, + RobustBox const& point_robust_box, + RobustBox const& other_robust_box, + RobustPolicy const& robust_policy) { - typename geometry::robust_point_type::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get(robust_point) > get(robust_box)) - || (dir == -1 && get(robust_point) < get(robust_box)); + return preceding(-dir, point, point_robust_box, other_robust_box, robust_policy); } diff --git a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp index ff56acec4..f1d8e7d23 100644 --- a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp +++ b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2013, 2014, 2015. -// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015, 2017. +// Modifications copyright (c) 2013-2017 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 @@ -136,11 +136,21 @@ struct sections : std::vector > namespace detail { namespace sectionalize { +// NOTE: This utility will NOT work for latitudes, dimension 1 in spherical +// and geographic coordinate system because in these coordinate systems +// e.g. a segment on northern hemisphere may go towards greater latitude +// and then towards lesser latitude. template < + typename Point, typename DimensionVector, std::size_t Index, - std::size_t Count + std::size_t Count, + typename CastedCSTag = typename tag_cast + < + typename cs_tag::type, + spherical_tag + >::type > struct get_direction_loop { @@ -161,21 +171,67 @@ struct get_direction_loop get_direction_loop < + Point, DimensionVector, Index + 1, - Count + Count, + CastedCSTag >::apply(seg, directions); } }; -template -struct get_direction_loop +template +< + typename Point, + typename DimensionVector, + std::size_t Count +> +struct get_direction_loop +{ + typedef typename boost::mpl::at_c::type dimension; + + template + static inline void apply(Segment const& seg, + int directions[Count]) + { + typedef typename coordinate_type::type coordinate_type; + typedef typename coordinate_system::type::units units_t; + + coordinate_type const diff = math::longitude_distance_signed + < + units_t, coordinate_type + >(geometry::get<0, 0>(seg), + geometry::get<1, 0>(seg)); + + coordinate_type zero = coordinate_type(); + directions[0] = diff > zero ? 1 : diff < zero ? -1 : 0; + + get_direction_loop + < + Point, + DimensionVector, + 1, + Count, + spherical_tag + >::apply(seg, directions); + } +}; + +template +< + typename Point, + typename DimensionVector, + std::size_t Count, + typename CastedCSTag +> +struct get_direction_loop { template static inline void apply(Segment const&, int [Count]) {} }; + //! Copy one static array to another template struct copy_loop @@ -410,7 +466,7 @@ struct sectionalize_part int direction_classes[dimension_count] = {0}; get_direction_loop < - DimensionVector, 0, dimension_count + Point, DimensionVector, 0, dimension_count >::apply(robust_segment, direction_classes); // if "dir" == 0 for all point-dimensions, it is duplicate. @@ -929,9 +985,12 @@ inline void sectionalize(Geometry const& geometry, typename cs_tag::type >::type envelope_strategy_type; - sectionalize(geometry, robust_policy, sections, - envelope_strategy_type(), - source_index, max_count); + boost::geometry::sectionalize + < + Reverse, DimensionVector + >(geometry, robust_policy, sections, + envelope_strategy_type(), + source_index, max_count); } }} // namespace boost::geometry diff --git a/include/boost/geometry/algorithms/detail/touches/implementation.hpp b/include/boost/geometry/algorithms/detail/touches/implementation.hpp index 94f1fba58..0cdb8ad1d 100644 --- a/include/boost/geometry/algorithms/detail/touches/implementation.hpp +++ b/include/boost/geometry/algorithms/detail/touches/implementation.hpp @@ -413,7 +413,8 @@ struct touches #endif // DOXYGEN_NO_DISPATCH -namespace resolve_variant { +namespace resolve_variant +{ template struct self_touches @@ -443,10 +444,11 @@ struct self_touches detail::touches::areal_interrupt_policy policy; strategy_type strategy; rescale_policy_type robust_policy; + // TODO: skip_adjacent should be set to false detail::self_get_turn_points::get_turns < false, policy_type - >::apply(geometry, strategy, robust_policy, turns, policy, 0); + >::apply(geometry, strategy, robust_policy, turns, policy, 0, true); return policy.result(); } diff --git a/include/boost/geometry/algorithms/detail/within/multi_point.hpp b/include/boost/geometry/algorithms/detail/within/multi_point.hpp index 7e85f3338..359853f6a 100644 --- a/include/boost/geometry/algorithms/detail/within/multi_point.hpp +++ b/include/boost/geometry/algorithms/detail/within/multi_point.hpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -33,6 +32,8 @@ #include +#include + #include #include @@ -63,7 +64,7 @@ struct multi_point_point } }; -// NOTE: currently the strategy is ignored, math::equals() is used inside relate::less +// NOTE: currently the strategy is ignored, math::equals() is used inside geometry::less<> struct multi_point_multi_point { template @@ -73,7 +74,7 @@ struct multi_point_multi_point { typedef typename boost::range_value::type point2_type; - relate::less const less = relate::less(); + geometry::less<> const less = geometry::less<>(); std::vector points2(boost::begin(multi_point2), boost::end(multi_point2)); std::sort(points2.begin(), points2.end(), less); diff --git a/include/boost/geometry/algorithms/dispatch/expand.hpp b/include/boost/geometry/algorithms/dispatch/expand.hpp index 2c23d6a1c..c7a769648 100644 --- a/include/boost/geometry/algorithms/dispatch/expand.hpp +++ b/include/boost/geometry/algorithms/dispatch/expand.hpp @@ -5,10 +5,11 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017, 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. @@ -39,8 +40,6 @@ namespace dispatch template < typename GeometryOut, typename Geometry, - typename StrategyLess = strategy::compare::default_strategy, - typename StrategyGreater = strategy::compare::default_strategy, typename TagOut = typename tag::type, typename Tag = typename tag::type, typename CSTagOut = typename cs_tag::type, diff --git a/include/boost/geometry/algorithms/is_convex.hpp b/include/boost/geometry/algorithms/is_convex.hpp index 8feb48db6..4a9251b27 100644 --- a/include/boost/geometry/algorithms/is_convex.hpp +++ b/include/boost/geometry/algorithms/is_convex.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -10,17 +15,23 @@ #define BOOST_GEOMETRY_ALGORITHMS_IS_CONVEX_HPP +#include +#include +#include + +#include #include #include #include #include #include -#include +#include #include +#include #include -#include #include + namespace boost { namespace geometry { @@ -31,15 +42,9 @@ namespace detail { namespace is_convex struct ring_is_convex { - template - static inline bool apply(Ring const& ring) + template + static inline bool apply(Ring const& ring, SideStrategy const& strategy) { - typedef typename geometry::point_type::type point_type; - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy_type; - std::size_t n = boost::size(ring); if (boost::size(ring) < core_detail::closure::minimum_ring_size < @@ -86,7 +91,7 @@ struct ring_is_convex // iterator for (std::size_t i = 0; i < n; i++) { - int const side = side_strategy_type::apply(*previous, *current, *next); + int const side = strategy.apply(*previous, *current, *next); if (side == 1) { // Next is on the left side of clockwise ring: @@ -129,7 +134,8 @@ struct is_convex : not_implemented template struct is_convex { - static inline bool apply(Box const& ) + template + static inline bool apply(Box const& , Strategy const& ) { // Any box is convex (TODO: consider spherical boxes) return true; @@ -144,13 +150,71 @@ struct is_convex : detail::is_convex::ring_is_convex } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH -// TODO: variants +namespace resolve_variant { + +template +struct is_convex +{ + template + static bool apply(Geometry const& geometry, Strategy const& strategy) + { + concepts::check(); + return dispatch::is_convex::apply(geometry, strategy); + } + + static bool apply(Geometry const& geometry, geometry::default_strategy const&) + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + return apply(geometry, side_strategy()); + } +}; + +template +struct is_convex > +{ + template + struct visitor: boost::static_visitor + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy) : m_strategy(strategy) {} + + template + bool operator()(Geometry const& geometry) const + { + return is_convex::apply(geometry, m_strategy); + } + }; + + template + static inline bool apply(boost::variant const& geometry, + Strategy const& strategy) + { + return boost::apply_visitor(visitor(strategy), geometry); + } +}; + +} // namespace resolve_variant // TODO: documentation / qbk template inline bool is_convex(Geometry const& geometry) { - return dispatch::is_convex::apply(geometry); + return resolve_variant::is_convex + < + Geometry + >::apply(geometry, geometry::default_strategy()); +} + +// TODO: documentation / qbk +template +inline bool is_convex(Geometry const& geometry, Strategy const& strategy) +{ + return resolve_variant::is_convex::apply(geometry, strategy); } diff --git a/include/boost/geometry/algorithms/point_on_surface.hpp b/include/boost/geometry/algorithms/point_on_surface.hpp index e9041f937..3f4d0f4af 100644 --- a/include/boost/geometry/algorithms/point_on_surface.hpp +++ b/include/boost/geometry/algorithms/point_on_surface.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2013 Mateusz Loskot, London, UK. // Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -33,6 +33,7 @@ #include #include +#include namespace boost { namespace geometry @@ -241,8 +242,9 @@ inline void replace_extremes_for_self_tangencies(Extremes& extremes, Intruders& extremes = triangle; } -template -inline bool calculate_point_on_surface(Geometry const& geometry, Point& point) +template +inline bool calculate_point_on_surface(Geometry const& geometry, Point& point, + SideStrategy const& strategy) { typedef typename geometry::point_type::type point_type; typedef typename geometry::coordinate_type::type coordinate_type; @@ -250,7 +252,7 @@ inline bool calculate_point_on_surface(Geometry const& geometry, Point& point) typedef std::vector > intruders_type; intruders_type intruders; - geometry::extreme_points(geometry, extremes, intruders); + geometry::extreme_points(geometry, extremes, intruders, strategy); if (extremes.size() < 3) { @@ -291,21 +293,57 @@ inline bool calculate_point_on_surface(Geometry const& geometry, Point& point) \tparam Geometry geometry type. This also defines the type of the output point \param geometry Geometry to take point from \param point Point to assign +\param strategy side strategy */ -template -inline void point_on_surface(Geometry const& geometry, Point & point) +template +inline void point_on_surface(Geometry const& geometry, Point & point, + SideStrategy const& strategy) { concepts::check(); concepts::check(); // First try in Y-direction (which should always succeed for valid polygons) - if (! detail::point_on_surface::calculate_point_on_surface<1>(geometry, point)) + if (! detail::point_on_surface::calculate_point_on_surface<1>(geometry, point, strategy)) { // For invalid polygons, we might try X-direction - detail::point_on_surface::calculate_point_on_surface<0>(geometry, point); + detail::point_on_surface::calculate_point_on_surface<0>(geometry, point, strategy); } } +/*! +\brief Assigns a Point guaranteed to lie on the surface of the Geometry +\tparam Geometry geometry type. This also defines the type of the output point +\param geometry Geometry to take point from +\param point Point to assign + */ +template +inline void point_on_surface(Geometry const& geometry, Point & point) +{ + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type strategy_type; + + point_on_surface(geometry, point, strategy_type()); +} + + +/*! +\brief Returns point guaranteed to lie on the surface of the Geometry +\tparam Geometry geometry type. This also defines the type of the output point +\param geometry Geometry to take point from +\param strategy side strategy +\return The Point guaranteed to lie on the surface of the Geometry + */ +template +inline typename geometry::point_type::type +return_point_on_surface(Geometry const& geometry, SideStrategy const& strategy) +{ + typename geometry::point_type::type result; + geometry::point_on_surface(geometry, result, strategy); + return result; +} + /*! \brief Returns point guaranteed to lie on the surface of the Geometry \tparam Geometry geometry type. This also defines the type of the output point diff --git a/include/boost/geometry/algorithms/remove_spikes.hpp b/include/boost/geometry/algorithms/remove_spikes.hpp index caa7fed9b..196b20c6a 100644 --- a/include/boost/geometry/algorithms/remove_spikes.hpp +++ b/include/boost/geometry/algorithms/remove_spikes.hpp @@ -5,6 +5,11 @@ // Copyright (c) 2009-2013 Mateusz Loskot, London, UK. // Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -34,6 +39,8 @@ #include #include +#include + #include @@ -59,20 +66,13 @@ namespace detail { namespace remove_spikes { -template struct range_remove_spikes { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy; - - typedef typename coordinate_type::type coordinate_type; - typedef typename point_type::type point_type; - - - static inline void apply(Range& range) + template + static inline void apply(Range& range, SideStrategy const& strategy) { + typedef typename point_type::type point_type; + std::size_t n = boost::size(range); std::size_t const min_num_points = core_detail::closure::minimum_ring_size < @@ -91,7 +91,10 @@ struct range_remove_spikes cleaned.push_back(*it); while(cleaned.size() >= 3 - && detail::point_is_spike_or_equal(cleaned.back(), *(cleaned.end() - 3), *(cleaned.end() - 2))) + && detail::point_is_spike_or_equal(cleaned.back(), + *(cleaned.end() - 3), + *(cleaned.end() - 2), + strategy)) { // Remove pen-ultimate point causing the spike (or which was equal) cleaned.erase(cleaned.end() - 2); @@ -110,13 +113,21 @@ struct range_remove_spikes found = false; // Check for spike in first point int const penultimate = 2; - while(cleaned.size() >= 3 && detail::point_is_spike_or_equal(cleaned.front(), *(cleaned.end() - penultimate), cleaned.back())) + while(cleaned.size() >= 3 + && detail::point_is_spike_or_equal(cleaned.front(), + *(cleaned.end() - penultimate), + cleaned.back(), + strategy)) { cleaned.pop_back(); found = true; } // Check for spike in second point - while(cleaned.size() >= 3 && detail::point_is_spike_or_equal(*(cleaned.begin() + 1), cleaned.back(), cleaned.front())) + while(cleaned.size() >= 3 + && detail::point_is_spike_or_equal(*(cleaned.begin() + 1), + cleaned.back(), + cleaned.front(), + strategy)) { cleaned.pop_front(); found = true; @@ -144,15 +155,13 @@ struct range_remove_spikes }; -template struct polygon_remove_spikes { - static inline void apply(Polygon& polygon) + template + static inline void apply(Polygon& polygon, SideStrategy const& strategy) { - typedef typename geometry::ring_type::type ring_type; - - typedef range_remove_spikes per_range; - per_range::apply(exterior_ring(polygon)); + typedef range_remove_spikes per_range; + per_range::apply(exterior_ring(polygon), strategy); typename interior_return_type::type rings = interior_rings(polygon); @@ -160,23 +169,24 @@ struct polygon_remove_spikes for (typename detail::interior_iterator::type it = boost::begin(rings); it != boost::end(rings); ++it) { - per_range::apply(*it); + per_range::apply(*it, strategy); } } }; -template +template struct multi_remove_spikes { - static inline void apply(MultiGeometry& multi) + template + static inline void apply(MultiGeometry& multi, SideStrategy const& strategy) { for (typename boost::range_iterator::type it = boost::begin(multi); it != boost::end(multi); ++it) { - SingleVersion::apply(*it); + SingleVersion::apply(*it, strategy); } } }; @@ -199,21 +209,22 @@ template > struct remove_spikes { - static inline void apply(Geometry&) + template + static inline void apply(Geometry&, SideStrategy const&) {} }; template struct remove_spikes - : detail::remove_spikes::range_remove_spikes + : detail::remove_spikes::range_remove_spikes {}; template struct remove_spikes - : detail::remove_spikes::polygon_remove_spikes + : detail::remove_spikes::polygon_remove_spikes {}; @@ -221,11 +232,7 @@ template struct remove_spikes : detail::remove_spikes::multi_remove_spikes < - MultiPolygon, detail::remove_spikes::polygon_remove_spikes - < - typename boost::range_value::type - > > {}; @@ -239,28 +246,46 @@ namespace resolve_variant { template struct remove_spikes { - static void apply(Geometry& geometry) + template + static void apply(Geometry& geometry, Strategy const& strategy) { concepts::check(); - dispatch::remove_spikes::apply(geometry); + dispatch::remove_spikes::apply(geometry, strategy); + } + + static void apply(Geometry& geometry, geometry::default_strategy const&) + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + apply(geometry, side_strategy()); } }; template struct remove_spikes > { + template struct visitor: boost::static_visitor { + Strategy const& m_strategy; + + visitor(Strategy const& strategy) : m_strategy(strategy) {} + template void operator()(Geometry& geometry) const { - remove_spikes::apply(geometry); + remove_spikes::apply(geometry, m_strategy); } }; - static inline void apply(boost::variant& geometry) + template + static inline void apply(boost::variant& geometry, + Strategy const& strategy) { - boost::apply_visitor(visitor(), geometry); + boost::apply_visitor(visitor(strategy), geometry); } }; @@ -275,7 +300,20 @@ struct remove_spikes > template inline void remove_spikes(Geometry& geometry) { - resolve_variant::remove_spikes::apply(geometry); + resolve_variant::remove_spikes::apply(geometry, geometry::default_strategy()); +} + +/*! + \ingroup remove_spikes + \tparam Geometry geometry type + \tparam Strategy side strategy type + \param geometry the geometry to make remove_spikes + \param strategy the side strategy used by the algorithm +*/ +template +inline void remove_spikes(Geometry& geometry, Strategy const& strategy) +{ + resolve_variant::remove_spikes::apply(geometry, strategy); } diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index a1f7e4e29..26621982e 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -110,50 +110,41 @@ public : } }; -struct dissolve_overlay_visitor -{ -public : - void print(char const* /*header*/) - { - } - - template - void print(char const* /*header*/, Turns const& /*turns*/, int /*turn_index*/) - { - } - - template - void print(char const* /*header*/, Turns const& /*turns*/, int /*turn_index*/, int /*op_index*/) - { - } - - template - void visit_turns(int , Turns const& ) {} - - template - void visit_clusters(Clusters const& , Turns const& ) {} - - template - void visit_traverse(Turns const& /*turns*/, Turn const& /*turn*/, Operation const& /*op*/, const char* /*header*/) - { - } - - template - void visit_traverse_reject(Turns const& , Turn const& , Operation const& , - detail::overlay::traverse_error_type ) - {} -}; - - - template struct dissolve_ring_or_polygon { - template + template + static inline void clear(Turns& turns) + { + typedef typename boost::range_value::type turn_type; + + for (typename boost::range_iterator::type + it = boost::begin(turns); + it != boost::end(turns); + ++it) + { + turn_type& turn = *it; + turn.discarded = false; + turn.cluster_id = -1; + turn.has_colocated_both = false; + turn.switch_source = false; + turn.touch_only = false; + } + + clear_visit_info(turns); + } + + + template + < + typename RescalePolicy, typename OutputIterator, + typename Strategy, typename Visitor + > static inline OutputIterator apply(Geometry const& geometry, RescalePolicy const& rescale_policy, OutputIterator out, - Strategy const& strategy) + Strategy const& strategy, + Visitor& visitor) { typedef typename point_type::type point_type; @@ -169,93 +160,111 @@ struct dissolve_ring_or_polygon geometry::self_turns < detail::overlay::assign_null_policy - >(geometry, strategy, rescale_policy, turns, policy); + >(geometry, strategy, rescale_policy, turns, policy, 0, false); - // The dissolve process is not necessary if there are no turns at all + visitor.visit_turns(1, turns); - if (boost::size(turns) > 0) - { - typedef typename ring_type::type ring_type; - typedef std::vector out_vector; - out_vector rings; - - typedef std::map - < - signed_size_type, - detail::overlay::cluster_info - > cluster_type; - - cluster_type clusters; - dissolve_overlay_visitor visitor; - - // Enrich/traverse the polygons twice: once for union... - typename Strategy::side_strategy_type const - side_strategy = strategy.get_side_strategy(); - - enrich_intersection_points(turns, - clusters, geometry, geometry, rescale_policy, - side_strategy); - - detail::overlay::traverse - < - false, false, - Geometry, Geometry, - overlay_dissolve, - backtrack_for_dissolve - >::apply(geometry, geometry, - strategy, rescale_policy, - turns, rings, clusters, visitor); - - clear_visit_info(turns); - - // ... and for intersection - enrich_intersection_points(turns, - clusters, geometry, geometry, rescale_policy, - side_strategy); - - detail::overlay::traverse - < - false, false, - Geometry, Geometry, - overlay_intersection, - backtrack_for_dissolve - >::apply(geometry, geometry, - strategy, rescale_policy, - turns, rings, clusters, visitor); - - std::map map; - detail::overlay::get_ring_turn_info(map, turns, clusters); - - typedef detail::overlay::ring_properties::type> properties; - - std::map selected; - - detail::overlay::select_rings(geometry, map, selected, strategy); - - // Add intersected rings - { - ring_identifier id(2, 0, -1); - for (typename boost::range_iterator const>::type - it = boost::begin(rings); - it != boost::end(rings); - ++it) - { - selected[id] = properties(*it); - id.multi_index++; - } - } - - detail::overlay::assign_parents(geometry, rings, selected, strategy, true); - return detail::overlay::add_rings(selected, geometry, rings, out); - - } - else + if (boost::size(turns) == 0) { + // No self-turns, then add original geometry GeometryOut g; geometry::convert(geometry, g); *out++ = g; return out; } + + typedef typename ring_type::type ring_type; + typedef std::vector out_vector; + out_vector rings; + + typedef std::map + < + signed_size_type, + detail::overlay::cluster_info + > cluster_type; + + cluster_type clusters; + + // Enrich/traverse the polygons twice: first for union... + typename Strategy::side_strategy_type const + side_strategy = strategy.get_side_strategy(); + + enrich_intersection_points(turns, + clusters, geometry, geometry, rescale_policy, + side_strategy); + + visitor.visit_turns(2, turns); + + visitor.visit_clusters(clusters, turns); + + std::map turn_info_per_ring; + + detail::overlay::traverse + < + false, false, + Geometry, Geometry, + overlay_dissolve_union, + backtrack_for_dissolve + >::apply(geometry, geometry, + strategy, rescale_policy, + turns, rings, turn_info_per_ring, clusters, visitor); + + visitor.visit_turns(3, turns); + + // ... and then for intersection + clear(turns); + enrich_intersection_points(turns, + clusters, geometry, geometry, rescale_policy, + side_strategy); + + visitor.visit_turns(4, turns); + + detail::overlay::traverse + < + false, false, + Geometry, Geometry, + overlay_dissolve_intersection, + backtrack_for_dissolve + >::apply(geometry, geometry, + strategy, rescale_policy, + turns, rings, turn_info_per_ring, clusters, visitor); + + visitor.visit_turns(5, turns); + + detail::overlay::get_ring_turn_info(turn_info_per_ring, turns, clusters); + + typedef typename geometry::point_type::type point_type; + typedef typename Strategy::template area_strategy + < + point_type + >::type area_strategy_type; + typedef typename area_strategy_type::return_type area_result_type; + typedef detail::overlay::ring_properties properties; + + std::map selected; + + detail::overlay::select_rings(geometry, turn_info_per_ring, selected, strategy); + + // Add intersected rings + { + area_strategy_type const area_strategy = strategy.template get_area_strategy(); + + ring_identifier id(2, 0, -1); + for (typename boost::range_iterator const>::type + it = boost::begin(rings); + it != boost::end(rings); + ++it) + { + selected[id] = properties(*it, area_strategy); + id.multi_index++; + } + } + + // Assign parents, checking orientation and discarding negative + // children with negative parents + detail::overlay::assign_parents(geometry, rings, selected, + strategy, true, true); + return detail::overlay::add_rings(selected, geometry, rings, out); } }; @@ -335,13 +344,15 @@ inline OutputIterator dissolve_inserter(Geometry const& geometry, rescale_policy_type robust_policy = geometry::get_rescale_policy(geometry); + detail::overlay::overlay_null_visitor visitor; + return dispatch::dissolve < typename tag::type, typename tag::type, Geometry, GeometryOut - >::apply(geometry, robust_policy, out, strategy); + >::apply(geometry, robust_policy, out, strategy, visitor); } /*! @@ -388,6 +399,8 @@ inline void dissolve(Geometry const& geometry, Collection& output_collection, St concepts::check(); + detail::overlay::overlay_null_visitor visitor; + dispatch::dissolve < typename tag::type, @@ -396,7 +409,7 @@ inline void dissolve(Geometry const& geometry, Collection& output_collection, St geometry_out >::apply(geometry, detail::no_rescale_policy(), std::back_inserter(output_collection), - strategy); + strategy, visitor); } template diff --git a/include/boost/geometry/extensions/multi/algorithms/dissolve.hpp b/include/boost/geometry/extensions/multi/algorithms/dissolve.hpp index 2abb05e0e..bedc995ff 100644 --- a/include/boost/geometry/extensions/multi/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/multi/algorithms/dissolve.hpp @@ -38,9 +38,14 @@ namespace detail { namespace dissolve template struct dissolve_multi { - template - static inline OutputIterator apply(Multi const& multi, RescalePolicy const& rescale_policy, - OutputIterator out, Strategy const& strategy) + template + < + typename RescalePolicy, typename OutputIterator, + typename Strategy, typename Visitor + > + static inline OutputIterator apply(Multi const& multi, + RescalePolicy const& rescale_policy, OutputIterator out, + Strategy const& strategy, Visitor& visitor) { typedef typename boost::range_value::type polygon_type; typedef typename boost::range_iterator::type iterator_type; @@ -55,7 +60,8 @@ struct dissolve_multi < polygon_type, GeometryOut - >::apply(*it, rescale_policy, std::back_inserter(step1), strategy); + >::apply(*it, rescale_policy, std::back_inserter(step1), + strategy, visitor); } // Step 2: remove mutual overlap diff --git a/include/boost/geometry/extensions/nsphere/algorithms/expand.hpp b/include/boost/geometry/extensions/nsphere/algorithms/expand.hpp index a87d5cf03..fdf14dbe9 100644 --- a/include/boost/geometry/extensions/nsphere/algorithms/expand.hpp +++ b/include/boost/geometry/extensions/nsphere/algorithms/expand.hpp @@ -5,6 +5,11 @@ // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, 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. @@ -17,6 +22,7 @@ #include +#include #include @@ -26,9 +32,6 @@ #include -#include -#include - namespace boost { namespace geometry { @@ -40,7 +43,6 @@ namespace detail { namespace expand template < - typename StrategyLess, typename StrategyGreater, std::size_t Dimension, std::size_t DimensionCount > struct nsphere_loop @@ -48,20 +50,10 @@ struct nsphere_loop template static inline void apply(Box& box, NSphere const& source) { - typedef typename strategy::compare::detail::select_strategy - < - StrategyLess, 1, NSphere, Dimension - >::type less_type; - - typedef typename strategy::compare::detail::select_strategy - < - StrategyGreater, -1, NSphere, Dimension - >::type greater_type; - typedef typename select_coordinate_type::type coordinate_type; - less_type less; - greater_type greater; + std::less const less; + std::greater const greater; coordinate_type const min_coord = get(source) - get_radius<0>(source); coordinate_type const max_coord = get(source) + get_radius<0>(source); @@ -78,7 +70,6 @@ struct nsphere_loop nsphere_loop < - StrategyLess, StrategyGreater, Dimension + 1, DimensionCount >::apply(box, source); } @@ -87,12 +78,10 @@ struct nsphere_loop template < - typename StrategyLess, typename StrategyGreater, std::size_t DimensionCount > struct nsphere_loop < - StrategyLess, StrategyGreater, DimensionCount, DimensionCount > { @@ -112,13 +101,11 @@ namespace dispatch // Box + Nsphere -> new box containing also nsphere template < - typename BoxOut, typename NSphere, - typename StrategyLess, typename StrategyGreater + typename BoxOut, typename NSphere > -struct expand +struct expand : detail::expand::nsphere_loop < - StrategyLess, StrategyGreater, 0, dimension::type::value > {}; diff --git a/include/boost/geometry/formulas/authalic_radius_sqr.hpp b/include/boost/geometry/formulas/authalic_radius_sqr.hpp new file mode 100644 index 000000000..54da31761 --- /dev/null +++ b/include/boost/geometry/formulas/authalic_radius_sqr.hpp @@ -0,0 +1,96 @@ +// Boost.Geometry + +// Copyright (c) 2017 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_FORMULAS_AUTHALIC_RADIUS_SQR_HPP +#define BOOST_GEOMETRY_FORMULAS_AUTHALIC_RADIUS_SQR_HPP + +#include +#include +#include + +#include + +#include + +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace formula_dispatch +{ + +template ::type> +struct authalic_radius_sqr + : not_implemented +{}; + +template +struct authalic_radius_sqr +{ + static inline ResultType apply(Geometry const& geometry) + { + return math::sqr(get_radius<0>(geometry)); + } +}; + +template +struct authalic_radius_sqr +{ + static inline ResultType apply(Geometry const& geometry) + { + ResultType const a2 = math::sqr(get_radius<0>(geometry)); + ResultType const e2 = formula::eccentricity_sqr(geometry); + + return apply(a2, e2); + } + + static inline ResultType apply(ResultType const& a2, ResultType const& e2) + { + ResultType const c0 = 0; + + if (math::equals(e2, c0)) + { + return a2; + } + + ResultType const e = math::sqrt(e2); + ResultType const c2 = 2; + + //ResultType const b2 = math::sqr(get_radius<2>(geometry)); + //return a2 / c2 + b2 * boost::math::atanh(e) / (c2 * e); + + ResultType const c1 = 1; + return (a2 / c2) * ( c1 + (c1 - e2) * boost::math::atanh(e) / e ); + } +}; + +} // namespace formula_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template +inline ResultType authalic_radius_sqr(Geometry const& geometry) +{ + return formula_dispatch::authalic_radius_sqr::apply(geometry); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_AUTHALIC_RADIUS_SQR_HPP diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp new file mode 100644 index 000000000..1700a8842 --- /dev/null +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -0,0 +1,375 @@ +// Boost.Geometry + +// Copyright (c) 2016-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, 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_FORMULAS_CROSS_TRACK_GEO_HPP +#define BOOST_GEOMETRY_FORMULAS_CROSS_TRACK_GEO_HPP + +#include +#include + +#ifndef BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS +#define BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS 100 +#endif + +/*! +\brief Algorithm to compute the distance between a segment and a point using + direct and inverse geodesic problems as subroutines. The algorithm + approximates the distance by an iterative Newton method. +\see C.F.F.Karney - Geodesics on an ellipsoid of revolution, + https://arxiv.org/abs/1102.1215 +*/ + +namespace boost { namespace geometry { namespace formula +{ + +template +< + typename CT, + typename Units, + template class Inverse, + template class Direct, + bool EnableClosestPoint = false +> +class distance_point_segment{ + +public: + + typedef Inverse inverse_distance_quantities_type; + typedef Inverse inverse_distance_type; + typedef Inverse inverse_azimuth_type; + typedef Inverse inverse_azimuth_reverse_type; + typedef Direct direct_distance_type; + + struct result_distance_point_segment + { + result_distance_point_segment() + : distance(0) + , closest_point_lon(0) + , closest_point_lat(0) + {} + + CT distance; + CT closest_point_lon; + CT closest_point_lat; + }; + + result_distance_point_segment + static inline non_iterative_case(CT lon, CT lat, CT distance) + { + result_distance_point_segment result; + result.distance = distance; + + if (EnableClosestPoint) + { + result.closest_point_lon = lon; + result.closest_point_lat = lat; + } + return result; + } + + template + result_distance_point_segment + static inline non_iterative_case(CT lon1, CT lat1, //p1 + CT lon2, CT lat2, //p2 + Spheroid const& spheroid) + { + CT distance = inverse_distance_quantities_type::apply(lon1, lat1, + lon2, lat2, + spheroid).distance; + return non_iterative_case(lon1, lat1, distance); + } + + template + T static inline normalize(T g4) + { + CT const pi = math::pi(); + if (g4 < 0 && g4 < -pi)//close to -270 + { + return g4 + 1.5 * pi; + } + else if (g4 > 0 && g4 > pi)//close to 270 + { + return - g4 + 1.5 * pi; + } + else if (g4 < 0 && g4 > -pi)//close to -90 + { + return -g4 - pi/2; + } + return g4 - pi/2; + } + + template + result_distance_point_segment + static inline apply(CT lon1, CT lat1, //p1 + CT lon2, CT lat2, //p2 + CT lon3, CT lat3, //query point p3 + Spheroid const& spheroid) + { + CT const earth_radius = geometry::formula::mean_radius(spheroid); + + result_distance_point_segment result; + + // Constants + CT const f = flattening(spheroid); + CT const pi = math::pi(); + CT const half_pi = pi / CT(2); + CT const c0 = CT(0); + + // Convert to radians + lon1 = math::as_radian(lon1); + lat1 = math::as_radian(lat1); + lon2 = math::as_radian(lon2); + lat2 = math::as_radian(lat2); + lon3 = math::as_radian(lon3); + lat3 = math::as_radian(lat3); + + if (lon1 > lon2) + { + std::swap(lon1, lon2); + std::swap(lat1, lat2); + } + + //segment on equator + //TODO: use the meridian distance when it'll be available + if (math::equals(lat1, c0) && math::equals(lat2, c0)) + { +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "Equatorial segment" << std::endl; +#endif + if (lon3 <= lon1) + { + return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); + } + if (lon3 >= lon2) + { + return non_iterative_case(lon2, lat2, lon3, lat3, spheroid); + } + return non_iterative_case(lon3, lat1, lon3, lat3, spheroid); + } + + CT d1 = inverse_distance_type::apply(lon1, lat1, + lon3, lat3, spheroid).distance; + CT d3 = inverse_distance_type::apply(lon1, lat1, + lon2, lat2, spheroid).distance; + + if (geometry::math::equals(d3, c0)) + { + return non_iterative_case(lon1, lat2, d1); + } + + CT d2 = inverse_distance_type::apply(lon2, lat2, + lon3, lat3, spheroid).distance; + + // Compute a12 (GEO) + geometry::formula::result_inverse res12 = + inverse_azimuth_reverse_type::apply(lon1, lat1, lon2, lat2, spheroid); + CT a12 = res12.azimuth; + CT a13 = inverse_azimuth_type::apply(lon1, lat1, lon3, lat3, spheroid).azimuth; + + CT a312 = a13 - a12; + + if (geometry::math::equals(a312, c0)) + { +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "point on segment" << std::endl; +#endif + return non_iterative_case(lon3, lat3, c0); + } + + CT projection1 = cos( a312 ) * d1 / d3; + +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "segment=(" << lon1 * math::r2d(); + std::cout << "," << lat1 * math::r2d(); + std::cout << "),(" << lon2 * math::r2d(); + std::cout << "," << lat2 * math::r2d(); + std::cout << ")\np=(" << lon3 * math::r2d(); + std::cout << "," << lat3 * math::r2d(); + std::cout << ")\na1=" << a12 * math::r2d() << std::endl; + std::cout << "a13=" << a13 * math::r2d() << std::endl; + std::cout << "a312=" << a312 * math::r2d() << std::endl; + std::cout << "cos(a312)=" << cos(a312) << std::endl; +#endif + if (projection1 < 0.0) + { +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "projection closer to p1" << std::endl; +#endif + // projection of p3 on geodesic spanned by segment (p1,p2) fall + // outside of segment on the side of p1 + return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); + } + + CT a21 = res12.reverse_azimuth - pi; + CT a23 = inverse_azimuth_type::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; + + CT a321 = a23 - a21; + +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "a21=" << a21 * math::r2d() << std::endl; + std::cout << "a23=" << a13 * math::r2d() << std::endl; + std::cout << "a321=" << a321 * math::r2d() << std::endl; + std::cout << "cos(a321)=" << cos(a321) << std::endl; +#endif + CT projection2 = cos( a321 ) * d2 / d3; + + if (projection2 < 0.0) + { +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "projection closer to p2" << std::endl; +#endif + // projection of p3 on geodesic spanned by segment (p1,p2) fall + // outside of segment on the side of p2 + return non_iterative_case(lon2, lat2, lon3, lat3, spheroid); + } + + // Guess s14 (SPHERICAL) + typedef geometry::model::point + < + CT, 2, + geometry::cs::spherical_equatorial + > point; + + CT bet1 = atan((1 - f) * tan(lon1)); + CT bet2 = atan((1 - f) * tan(lon2)); + CT bet3 = atan((1 - f) * tan(lon3)); + point p1 = point(bet1, lat1); + point p2 = point(bet2, lat2); + point p3 = point(bet3, lat3); + + geometry::strategy::distance::cross_track cross_track(earth_radius); + CT s34 = cross_track.apply(p3, p1, p2); + + geometry::strategy::distance::haversine str(earth_radius); + CT s13 = str.apply(p1, p3); + CT s14 = acos( cos(s13/earth_radius) / cos(s34/earth_radius) ) * earth_radius; + +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "s34=" << s34 << std::endl; + std::cout << "s13=" << s13 << std::endl; + std::cout << "s14=" << s14 << std::endl; + std::cout << "===============" << std::endl; +#endif + + // Update s14 (using Newton method) + CT prev_distance = 0; + geometry::formula::result_direct res14; + geometry::formula::result_inverse res34; + + int counter = 0; // robustness + CT g4; + CT delta_g4; + + do{ + prev_distance = res34.distance; + + // Solve the direct problem to find p4 (GEO) + res14 = direct_distance_type::apply(lon1, lat1, s14, a12, spheroid); + + // Solve an inverse problem to find g4 + // g4 is the angle between segment (p1,p2) and segment (p3,p4) that meet on p4 (GEO) + + CT a4 = inverse_azimuth_type::apply(res14.lon2, res14.lat2, + lon2, lat2, spheroid).azimuth; + res34 = inverse_distance_quantities_type::apply(res14.lon2, res14.lat2, + lon3, lat3, spheroid); + g4 = res34.azimuth - a4; + + delta_g4 = normalize(g4); + + CT M43 = res34.geodesic_scale; // cos(s14/earth_radius) is the spherical limit + CT m34 = res34.reduced_length; + CT der = (M43 / m34) * sin(g4); + s14 = s14 - delta_g4 / der; + +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "p4=" << res14.lon2 * math::r2d() << + "," << res14.lat2 * math::r2d() << std::endl; + std::cout << "delta_g4=" << delta_g4 << std::endl; + std::cout << "g4=" << g4 * math::r2d() << std::endl; + std::cout << "der=" << der << std::endl; + std::cout << "M43=" << M43 << std::endl; + std::cout << "spherical limit=" << cos(s14/earth_radius) << std::endl; + std::cout << "m34=" << m34 << std::endl; + std::cout << "new_s14=" << s14 << std::endl; + std::cout << std::setprecision(16) << "dist =" << res34.distance << std::endl; + std::cout << "---------end of step " << counter << std::endl<< std::endl; +#endif + result.distance = prev_distance; + +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + if (g4 == half_pi) + { + std::cout << "Stop msg: g4 == half_pi" << std::endl; + } + if (res34.distance >= prev_distance && prev_distance != 0) + { + std::cout << "Stop msg: res34.distance >= prev_distance" << std::endl; + } + if (delta_g4 == 0) + { + std::cout << "Stop msg: delta_g4 == 0" << std::endl; + } + if (counter == 19) + { + std::cout << "Stop msg: counter" << std::endl; + } +#endif + + } while (g4 != half_pi + && (prev_distance > res34.distance || prev_distance == 0) + && delta_g4 != 0 + && ++counter < BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS ) ; + +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "distance=" << res34.distance << std::endl; + + point p4(res14.lon2, res14.lat2); + CT s34_sph = str.apply(p4, p3); + + std::cout << "s34(sph) =" << s34_sph << std::endl; + std::cout << "s34(geo) =" + << inverse_distance_quantities_type::apply(get<0>(p4), get<1>(p4), lon3, lat3, spheroid).distance + << ", p4=(" << get<0>(p4) * math::r2d() << "," + << get<1>(p4) * math::r2d() << ")" + << std::endl; + + CT s31 = inverse_distance_quantities_type::apply(lon3, lat3, lon1, lat1, spheroid).distance; + CT s32 = inverse_distance_quantities_type::apply(lon3, lat3, lon2, lat2, spheroid).distance; + + CT a4 = inverse_azimuth_type::apply(get<0>(p4), get<1>(p4), lon2, lat2, spheroid).azimuth; + geometry::formula::result_direct res4 = direct_distance_type::apply(get<0>(p4), get<1>(p4), .04, a4, spheroid); + CT p4_plus = inverse_distance_quantities_type::apply(res4.lon2, res4.lat2, lon3, lat3, spheroid).distance; + + geometry::formula::result_direct res1 = direct_distance_type::apply(lon1, lat1, s14-.04, a12, spheroid); + CT p4_minus = inverse_distance_quantities_type::apply(res1.lon2, res1.lat2, lon3, lat3, spheroid).distance; + + std::cout << "s31=" << s31 << "\ns32=" << s32 + << "\np4_plus=" << p4_plus << ", p4=(" << res4.lon2 * math::r2d() << "," << res4.lat2 * math::r2d() << ")" + << "\np4_minus=" << p4_minus << ", p4=(" << res1.lon2 * math::r2d() << "," << res1.lat2 * math::r2d() << ")" + << std::endl; + + if (res34.distance <= p4_plus && res34.distance <= p4_minus) + { + std::cout << "Closest point computed" << std::endl; + } + else + { + std::cout << "There is a closer point nearby" << std::endl; + } +#endif + + return result; + } + +}; + +}}} // namespace boost::geometry::formula +#endif // BOOST_GEOMETRY_FORMULAS_CROSS_TRACK_GEO_HPP diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp index f6feb6663..b338273f3 100644 --- a/include/boost/geometry/formulas/geographic.hpp +++ b/include/boost/geometry/formulas/geographic.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2016, Oracle and/or its affiliates. +// Copyright (c) 2016-2017, 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, @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -186,7 +187,7 @@ inline Point3d projected_to_surface(Point3d const& direction, Spheroid const& sp //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid)); // "unit" spheroid, a = 1 coord_t const a_sqr = 1; - coord_t const b_sqr = math::sqr(get_radius<2>(spheroid) / get_radius<0>(spheroid)); + coord_t const b_sqr = math::sqr(formula::unit_spheroid_b(spheroid)); coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; coord_t const delta = c4 * param_a; @@ -226,7 +227,7 @@ inline bool projected_to_surface(Point3d const& origin, Point3d const& direction //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid)); // "unit" spheroid, a = 1 coord_t const a_sqr = 1; - coord_t const b_sqr = math::sqr(get_radius<2>(spheroid) / get_radius<0>(spheroid)); + coord_t const b_sqr = math::sqr(formula::unit_spheroid_b(spheroid)); coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; coord_t const param_b = c2 * ((ox*dx + oy*dy) / a_sqr + oz*dz / b_sqr); diff --git a/include/boost/geometry/formulas/mean_radius.hpp b/include/boost/geometry/formulas/mean_radius.hpp new file mode 100644 index 000000000..83c9b5403 --- /dev/null +++ b/include/boost/geometry/formulas/mean_radius.hpp @@ -0,0 +1,71 @@ +// Boost.Geometry + +// Copyright (c) 2017 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_FORMULAS_MEAN_RADIUS_HPP +#define BOOST_GEOMETRY_FORMULAS_MEAN_RADIUS_HPP + +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace formula_dispatch +{ + +template ::type> +struct mean_radius + : not_implemented +{}; + +template +struct mean_radius +{ + static inline ResultType apply(Geometry const& geometry) + { + return ResultType(get_radius<0>(geometry)); + } +}; + +template +struct mean_radius +{ + static inline ResultType apply(Geometry const& geometry) + { + // (2*a + b) / 3 + return (ResultType(2) * ResultType(get_radius<0>(geometry)) + + ResultType(get_radius<2>(geometry))) + / ResultType(3); + } +}; + +} // namespace formula_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template +inline ResultType mean_radius(Geometry const& geometry) +{ + return formula_dispatch::mean_radius::apply(geometry); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_MEAN_RADIUS_HPP diff --git a/include/boost/geometry/formulas/unit_spheroid.hpp b/include/boost/geometry/formulas/unit_spheroid.hpp new file mode 100644 index 000000000..7fdedb458 --- /dev/null +++ b/include/boost/geometry/formulas/unit_spheroid.hpp @@ -0,0 +1,43 @@ +// Boost.Geometry + +// Copyright (c) 2017 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_FORMULAS_UNIT_SPHEROID_HPP +#define BOOST_GEOMETRY_FORMULAS_UNIT_SPHEROID_HPP + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template +inline ResultType unit_spheroid_b(Spheroid const& spheroid) +{ + return ResultType(get_radius<2>(spheroid)) + / ResultType(get_radius<0>(spheroid)); +} + +template +inline ResultSpheroid unit_spheroid(Spheroid const& spheroid) +{ + typedef typename radius_type::type radius_t; + return ResultSpheroid(radius_t(1), + unit_spheroid_b(spheroid)); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_UNIT_SPHEROID_HPP diff --git a/include/boost/geometry/geometries/helper_geometry.hpp b/include/boost/geometry/geometries/helper_geometry.hpp index 9cf14a911..f3102fee9 100644 --- a/include/boost/geometry/geometries/helper_geometry.hpp +++ b/include/boost/geometry/geometries/helper_geometry.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, 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 // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -10,6 +11,8 @@ #ifndef BOOST_GEOMETRY_GEOMETRIES_HELPER_GEOMETRY_HPP #define BOOST_GEOMETRY_GEOMETRIES_HELPER_GEOMETRY_HPP +#include + #include #include #include @@ -52,6 +55,14 @@ struct default_units template struct cs_tag_to_coordinate_system +{ + BOOST_MPL_ASSERT_MSG((false), + NOT_IMPLEMENTED_FOR_THIS_COORDINATE_SYSTEM, + (types)); +}; + +template +struct cs_tag_to_coordinate_system { typedef cs::cartesian type; }; @@ -63,7 +74,7 @@ struct cs_tag_to_coordinate_system }; template -struct cs_tag_to_coordinate_system +struct cs_tag_to_coordinate_system { typedef cs::spherical type; }; diff --git a/include/boost/geometry/io/wkt/write.hpp b/include/boost/geometry/io/wkt/write.hpp index b98c894b3..ff2b4d2a3 100644 --- a/include/boost/geometry/io/wkt/write.hpp +++ b/include/boost/geometry/io/wkt/write.hpp @@ -1,12 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. -// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2007-2017 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2017 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2017 Mateusz Loskot, London, UK. +// Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -111,7 +111,7 @@ template struct wkt_point { template - static inline void apply(std::basic_ostream& os, Point const& p) + static inline void apply(std::basic_ostream& os, Point const& p, bool) { os << Policy::apply() << "("; stream_coordinate::type::value>::apply(os, p); @@ -123,12 +123,18 @@ struct wkt_point \brief Stream ranges as WKT \note policy is used to stream prefix/postfix, enabling derived classes to override this */ -template +template +< + typename Range, + bool ForceClosurePossible, + typename PrefixPolicy, + typename SuffixPolicy +> struct wkt_range { template static inline void apply(std::basic_ostream& os, - Range const& range, bool force_closed) + Range const& range, bool force_closure = ForceClosurePossible) { typedef typename boost::range_iterator::type iterator_type; @@ -153,7 +159,8 @@ struct wkt_range } // optionally, close range to ring by repeating the first point - if (force_closed + if (ForceClosurePossible + && force_closure && boost::size(range) > 1 && detail::disjoint::disjoint_point_point(*begin, *(end - 1))) { @@ -164,12 +171,6 @@ struct wkt_range os << SuffixPolicy::apply(); } - template - static inline void apply(std::basic_ostream& os, - Range const& range) - { - apply(os, range, false); - } private: typedef typename boost::range_value::type point_type; @@ -179,11 +180,12 @@ private: \brief Stream sequence of points as WKT-part, e.g. (1 2),(3 4) \note Used in polygon, all multi-geometries */ -template +template struct wkt_sequence : wkt_range < Range, + ForceClosurePossible, opening_parenthesis, closing_parenthesis > @@ -194,15 +196,14 @@ struct wkt_poly { template static inline void apply(std::basic_ostream& os, - Polygon const& poly) + Polygon const& poly, bool force_closure) { typedef typename ring_type::type ring; - bool const force_closed = true; os << PrefixPolicy::apply(); // TODO: check EMPTY here os << "("; - wkt_sequence::apply(os, exterior_ring(poly), force_closed); + wkt_sequence::apply(os, exterior_ring(poly), force_closure); typename interior_return_type::type rings = interior_rings(poly); @@ -210,10 +211,11 @@ struct wkt_poly it = boost::begin(rings); it != boost::end(rings); ++it) { os << ","; - wkt_sequence::apply(os, *it, force_closed); + wkt_sequence::apply(os, *it, force_closure); } os << ")"; } + }; template @@ -221,7 +223,7 @@ struct wkt_multi { template static inline void apply(std::basic_ostream& os, - Multi const& geometry) + Multi const& geometry, bool force_closure) { os << PrefixPolicy::apply(); // TODO: check EMPTY here @@ -236,7 +238,7 @@ struct wkt_multi { os << ","; } - StreamPolicy::apply(os, *it); + StreamPolicy::apply(os, *it, force_closure); } os << ")"; @@ -250,15 +252,19 @@ struct wkt_box template static inline void apply(std::basic_ostream& os, - Box const& box) + Box const& box, bool force_closure) { - // Convert to ring, then stream - typedef model::ring ring_type; - ring_type ring; - geometry::convert(box, ring); - os << "POLYGON("; - wkt_sequence::apply(os, ring); - os << ")"; + // Convert to a clockwire ring, then stream. + // Never close it based on last point (box might be empty and + // that should result in POLYGON((0 0,0 0,0 0,0 0, ...)) ) + if (force_closure) + { + do_apply >(os, box); + } + else + { + do_apply >(os, box); + } } private: @@ -268,6 +274,18 @@ struct wkt_box // Only streaming of boxes with two dimensions is support, otherwise it is a polyhedron! //assert_dimension(); } + + template + static inline void do_apply(std::basic_ostream& os, + Box const& box) + { + RingType ring; + geometry::convert(box, ring); + os << "POLYGON("; + wkt_sequence::apply(os, ring); + os << ")"; + } + }; @@ -278,7 +296,7 @@ struct wkt_segment template static inline void apply(std::basic_ostream& os, - Segment const& segment) + Segment const& segment, bool) { // Convert to two points, then stream typedef boost::array sequence; @@ -290,7 +308,7 @@ struct wkt_segment // In Boost.Geometry a segment is represented // in WKT-format like (for 2D): LINESTRING(x y,x y) os << "LINESTRING"; - wkt_sequence::apply(os, points); + wkt_sequence::apply(os, points); } private: @@ -324,6 +342,7 @@ struct wkt : detail::wkt::wkt_range < Linestring, + false, detail::wkt::prefix_linestring_par, detail::wkt::closing_parenthesis > @@ -355,6 +374,7 @@ struct wkt : detail::wkt::wkt_range < Ring, + true, detail::wkt::prefix_ring_par_par, detail::wkt::double_closing_parenthesis > @@ -393,7 +413,8 @@ struct wkt Multi, detail::wkt::wkt_sequence < - typename boost::range_value::type + typename boost::range_value::type, + false >, detail::wkt::prefix_multilinestring > @@ -418,9 +439,10 @@ template struct devarianted_wkt { template - static inline void apply(OutputStream& os, Geometry const& geometry) + static inline void apply(OutputStream& os, Geometry const& geometry, + bool force_closure) { - wkt::apply(os, geometry); + wkt::apply(os, geometry, force_closure); } }; @@ -431,25 +453,27 @@ struct devarianted_wkt > struct visitor: static_visitor { OutputStream& m_os; + bool m_force_closure; - visitor(OutputStream& os) + visitor(OutputStream& os, bool force_closure) : m_os(os) + , m_force_closure(force_closure) {} template inline void operator()(Geometry const& geometry) const { - devarianted_wkt::apply(m_os, geometry); + devarianted_wkt::apply(m_os, geometry, m_force_closure); } }; template static inline void apply( OutputStream& os, - variant const& geometry - ) + variant const& geometry, + bool force_closure) { - boost::apply_visitor(visitor(os), geometry); + boost::apply_visitor(visitor(os, force_closure), geometry); } }; @@ -473,8 +497,12 @@ class wkt_manipulator { public: - inline wkt_manipulator(Geometry const& g) + // Boost.Geometry, by default, closes polygons explictly, but not rings + // NOTE: this might change in the future! + inline wkt_manipulator(Geometry const& g, + bool force_closure = ! boost::is_same::type, ring_tag>::value) : m_geometry(g) + , m_force_closure(force_closure) {} template @@ -482,13 +510,14 @@ public: std::basic_ostream& os, wkt_manipulator const& m) { - dispatch::devarianted_wkt::apply(os, m.m_geometry); + dispatch::devarianted_wkt::apply(os, m.m_geometry, m.m_force_closure); os.flush(); return os; } private: Geometry const& m_geometry; + bool m_force_closure; }; /*! diff --git a/include/boost/geometry/policies/compare.hpp b/include/boost/geometry/policies/compare.hpp index 2e952d3e1..7f32b7ab3 100644 --- a/include/boost/geometry/policies/compare.hpp +++ b/include/boost/geometry/policies/compare.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, 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) @@ -20,170 +25,56 @@ namespace boost { namespace geometry { -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace compare -{ - - -template -< - int Direction, - typename Point, - typename Strategy, - std::size_t Dimension, - std::size_t DimensionCount -> -struct compare_loop -{ - typedef typename strategy::compare::detail::select_strategy - < - Strategy, Direction, Point, Dimension - >::type compare_type; - - typedef typename geometry::coordinate_type::type coordinate_type; - - static inline bool apply(Point const& left, Point const& right) - { - coordinate_type const& cleft = geometry::get(left); - coordinate_type const& cright = geometry::get(right); - - if (geometry::math::equals(cleft, cright)) - { - return compare_loop - < - Direction, Point, Strategy, - Dimension + 1, DimensionCount - >::apply(left, right); - } - else - { - compare_type compare; - return compare(cleft, cright); - } - } -}; - -template -< - int Direction, - typename Point, - typename Strategy, - std::size_t DimensionCount -> -struct compare_loop -{ - static inline bool apply(Point const&, Point const&) - { - // On coming here, points are equal. Return true if - // direction = 0 (equal), false if -1/1 (greater/less) - return Direction == 0; - } -}; - - -template -struct compare_in_all_dimensions -{ - inline bool operator()(Point const& left, Point const& right) const - { - return detail::compare::compare_loop - < - Direction, Point, Strategy, - 0, geometry::dimension::type::value - >::apply(left, right); - } -}; - - -template -class compare_in_one_dimension -{ - Strategy compare; - -public : - inline bool operator()(Point const& left, Point const& right) const - { - typedef typename geometry::coordinate_type::type coordinate_type; - - coordinate_type const& cleft = get(left); - coordinate_type const& cright = get(right); - return compare(cleft, cright); - } -}; - -}} // namespace detail::compare - -#endif - -#ifndef DOXYGEN_NO_DISPATCH -namespace dispatch -{ - -template -< - int Direction, - typename Point, - typename Strategy, - int Dimension -> -struct compare_geometries - : detail::compare::compare_in_one_dimension - < - Point, - typename strategy::compare::detail::select_strategy - < - Strategy, Direction, Point, Dimension - >::type, - Dimension - > -{}; - - -// Specialization with -1: compare in all dimensions -template -struct compare_geometries - : detail::compare::compare_in_all_dimensions -{}; - - - -} // namespace dispatch -#endif // DOXYGEN_NO_DISPATCH - - /*! \brief Less functor, to sort points in ascending order. \ingroup compare \details This functor compares points and orders them on x, then on y, then on z coordinate. -\tparam Geometry the geometry +\tparam Point the geometry \tparam Dimension the dimension to sort on, defaults to -1, indicating ALL dimensions. That's to say, first on x, on equal x-es then on y, etc. If a dimension is specified, only that dimension is considered -\tparam Strategy underlying coordinate comparing functor, - defaults to the default comparison strategies - related to the point coordinate system. If specified, the specified - strategy is used. This can e.g. be std::less. */ template < - typename Point, - int Dimension = -1, - typename Strategy = strategy::compare::default_strategy + typename Point = void, + int Dimension = -1 > struct less - : dispatch::compare_geometries - < - 1, // indicates ascending - Point, - Strategy, - Dimension - > { typedef Point first_argument_type; typedef Point second_argument_type; typedef bool result_type; + + inline bool operator()(Point const& left, Point const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::less, + Point, Point, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; + +template +struct less +{ + template + inline bool operator()(Point1 const& left, Point2 const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::less, + Point1, Point2, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } }; @@ -195,19 +86,44 @@ struct less */ template < - typename Point, - int Dimension = -1, - typename Strategy = strategy::compare::default_strategy + typename Point = void, + int Dimension = -1 > struct greater - : dispatch::compare_geometries - < - -1, // indicates descending - Point, - Strategy, - Dimension - > -{}; +{ + typedef Point first_argument_type; + typedef Point second_argument_type; + typedef bool result_type; + + bool operator()(Point const& left, Point const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::greater, + Point, Point, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; + +template +struct greater +{ + template + bool operator()(Point1 const& left, Point2 const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::greater, + Point1, Point2, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; /*! @@ -217,23 +133,47 @@ struct greater \tparam Dimension the dimension to compare on, defaults to -1, indicating ALL dimensions. If a dimension is specified, only that dimension is considered -\tparam Strategy underlying coordinate comparing functor */ template < typename Point, - int Dimension = -1, - typename Strategy = strategy::compare::default_strategy + int Dimension = -1 > struct equal_to - : dispatch::compare_geometries - < - 0, - Point, - Strategy, - Dimension - > -{}; +{ + typedef Point first_argument_type; + typedef Point second_argument_type; + typedef bool result_type; + + bool operator()(Point const& left, Point const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::equal_to, + Point, Point, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; + +template +struct equal_to +{ + template + bool operator()(Point1 const& left, Point2 const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::equal_to, + Point1, Point2, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; }} // namespace boost::geometry diff --git a/include/boost/geometry/strategies/agnostic/point_in_point.hpp b/include/boost/geometry/strategies/agnostic/point_in_point.hpp index 1a9274149..d4692766c 100644 --- a/include/boost/geometry/strategies/agnostic/point_in_point.hpp +++ b/include/boost/geometry/strategies/agnostic/point_in_point.hpp @@ -32,7 +32,7 @@ struct point_in_point { static inline bool apply(Point1 const& point1, Point2 const& point2) { - return detail::equals::equals_point_point(point1, point2); + return geometry::detail::equals::equals_point_point(point1, point2); } }; diff --git a/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp b/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp index 0a797ac0f..774294b57 100644 --- a/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp +++ b/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp @@ -18,14 +18,15 @@ #define BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POLY_WINDING_HPP -#include +#include -#include -#include +#include +#include +#include +#include #include -#include -#include +#include namespace boost { namespace geometry @@ -34,292 +35,63 @@ namespace boost { namespace geometry namespace strategy { namespace within { -// 1 deg or pi/180 rad -template ::type> -struct winding_small_angle + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { - typedef typename coordinate_system::type cs_t; - typedef math::detail::constants_on_spheroid + + +template +< + typename Point, + typename PointOfSegment, + typename CalculationType, + typename CSTag = typename tag_cast + < + typename cs_tag::type, + spherical_tag + >::type +> +struct winding_base_type +{ + BOOST_MPL_ASSERT_MSG(false, + NOT_IMPLEMENTED_FOR_THIS_COORDINATE_SYSTEM, + (CSTag)); +}; + +template +struct winding_base_type +{ + typedef within::cartesian_winding type; +}; + +template +struct winding_base_type +{ + typedef within::detail::spherical_winding_base < - CalculationType, - typename cs_t::units - > constants; - - static inline CalculationType apply() - { - return constants::half_period() / CalculationType(180); - } + Point, + PointOfSegment, + typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type, + CalculationType + > type; }; -// Fix for https://svn.boost.org/trac/boost/ticket/9628 -// For floating point coordinates, the coordinate of a point is compared -// with the segment's points using some EPS. If the coordinates are "equal" -// the sides are calculated. Therefore we can treat a segment as a long areal -// geometry having some width. There is a small ~triangular area somewhere -// between the segment's effective area and a segment's line used in sides -// calculation where the segment is on the one side of the line but on the -// other side of a segment (due to the width). -// Below picture assuming D = 1, if D = 0 horiz<->vert, E<->N, RIGHT<->UP. -// For the s1 of a segment going NE the real side is RIGHT but the point may -// be detected as LEFT, like this: -// RIGHT -// ___-----> -// ^ O Pt __ __ -// EPS __ __ -// v__ __ BUT DETECTED AS LEFT OF THIS LINE -// _____7 -// _____/ -// _____/ -// In the code below actually D = 0, so segments are nearly-vertical -// Called when the point is on the same level as one of the segment's points -// but the point is not aligned with a vertical segment -template -struct winding_side_equal -{ - typedef typename strategy::side::services::default_strategy - < - CSTag - >::type strategy_side_type; - - template - static inline int apply(Point const& point, - PointOfSegment const& se, - int count) - { - typedef typename coordinate_type::type scoord_t; - typedef typename coordinate_system::type::units units_t; - - if (math::equals(get<1>(point), get<1>(se))) - return 0; - - // Create a horizontal segment intersecting the original segment's endpoint - // equal to the point, with the derived direction (E/W). - PointOfSegment ss1, ss2; - set<1>(ss1, get<1>(se)); - set<0>(ss1, get<0>(se)); - set<1>(ss2, get<1>(se)); - scoord_t ss20 = get<0>(se); - if (count > 0) - { - ss20 += winding_small_angle::apply(); - } - else - { - ss20 -= winding_small_angle::apply(); - } - math::normalize_longitude(ss20); - set<0>(ss2, ss20); - - // Check the side using this vertical segment - return strategy_side_type::apply(ss1, ss2, point); - } -}; -// The optimization for cartesian -template <> -struct winding_side_equal -{ - template - static inline int apply(Point const& point, - PointOfSegment const& se, - int count) - { - // NOTE: for D=0 the signs would be reversed - return math::equals(get<1>(point), get<1>(se)) ? - 0 : - get<1>(point) < get<1>(se) ? - // assuming count is equal to 1 or -1 - -count : // ( count > 0 ? -1 : 1) : - count; // ( count > 0 ? 1 : -1) ; - } -}; - - -template ::type> -struct winding_check_touch -{ - typedef CalculationType calc_t; - typedef typename coordinate_system::type::units units_t; - typedef math::detail::constants_on_spheroid constants; - - template - static inline int apply(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - State& state, - bool& eq1, - bool& eq2) - { - calc_t const pi = constants::half_period(); - calc_t const pi2 = pi / calc_t(2); - - calc_t const px = get<0>(point); - calc_t const s1x = get<0>(seg1); - calc_t const s2x = get<0>(seg2); - calc_t const py = get<1>(point); - calc_t const s1y = get<1>(seg1); - calc_t const s2y = get<1>(seg2); - - // NOTE: lat in {-90, 90} and arbitrary lon - // it doesn't matter what lon it is if it's a pole - // so e.g. if one of the segment endpoints is a pole - // then only the other lon matters - - bool eq1_strict = math::equals(s1x, px); - bool eq2_strict = math::equals(s2x, px); - - eq1 = eq1_strict // lon strictly equal to s1 - || math::equals(s1y, pi2) || math::equals(s1y, -pi2); // s1 is pole - eq2 = eq2_strict // lon strictly equal to s2 - || math::equals(s2y, pi2) || math::equals(s2y, -pi2); // s2 is pole - - // segment overlapping pole - calc_t s1x_anti = s1x + constants::half_period(); - math::normalize_longitude(s1x_anti); - bool antipodal = math::equals(s2x, s1x_anti); - if (antipodal) - { - eq1 = eq2 = eq1 || eq2; - - // segment overlapping pole and point is pole - if (math::equals(py, pi2) || math::equals(py, -pi2)) - { - eq1 = eq2 = true; - } - } - - // Both equal p -> segment vertical - // The only thing which has to be done is check if point is ON segment - if (eq1 && eq2) - { - // segment endpoints on the same sides of the globe - if (! antipodal - // p's lat between segment endpoints' lats - ? (s1y <= py && s2y >= py) || (s2y <= py && s1y >= py) - // going through north or south pole? - : (pi - s1y - s2y <= pi - ? (eq1_strict && s1y <= py) || (eq2_strict && s2y <= py) // north - || math::equals(py, pi2) // point on north pole - : (eq1_strict && s1y >= py) || (eq2_strict && s2y >= py)) // south - || math::equals(py, -pi2) // point on south pole - ) - { - state.m_touches = true; - } - return true; - } - return false; - } -}; -// The optimization for cartesian -template -struct winding_check_touch -{ - typedef CalculationType calc_t; - - template - static inline bool apply(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - State& state, - bool& eq1, - bool& eq2) - { - calc_t const px = get<0>(point); - calc_t const s1x = get<0>(seg1); - calc_t const s2x = get<0>(seg2); - - eq1 = math::equals(s1x, px); - eq2 = math::equals(s2x, px); - - // Both equal p -> segment vertical - // The only thing which has to be done is check if point is ON segment - if (eq1 && eq2) - { - calc_t const py = get<1>(point); - calc_t const s1y = get<1>(seg1); - calc_t const s2y = get<1>(seg2); - if ((s1y <= py && s2y >= py) || (s2y <= py && s1y >= py)) - { - state.m_touches = true; - } - return true; - } - return false; - } -}; - - -// Called if point is not aligned with a vertical segment -template ::type> -struct winding_calculate_count -{ - typedef CalculationType calc_t; - typedef typename coordinate_system::type::units units_t; - - static inline bool greater(calc_t const& l, calc_t const& r) - { - calc_t diff = l - r; - math::normalize_longitude(diff); - return diff > calc_t(0); - } - - static inline int apply(calc_t const& p, - calc_t const& s1, calc_t const& s2, - bool eq1, bool eq2) - { - // Probably could be optimized by avoiding normalization for some comparisons - // e.g. s1 > p could be calculated from p > s1 - - // If both segment endpoints were poles below checks wouldn't be enough - // but this means that either both are the same or that they are N/S poles - // and therefore the segment is not valid. - // If needed (eq1 && eq2 ? 0) could be returned - - return - eq1 ? (greater(s2, p) ? 1 : -1) // Point on level s1, E/W depending on s2 - : eq2 ? (greater(s1, p) ? -1 : 1) // idem - : greater(p, s1) && greater(s2, p) ? 2 // Point between s1 -> s2 --> E - : greater(p, s2) && greater(s1, p) ? -2 // Point between s2 -> s1 --> W - : 0; - } -}; -// The optimization for cartesian -template -struct winding_calculate_count -{ - typedef CalculationType calc_t; - - static inline int apply(calc_t const& p, - calc_t const& s1, calc_t const& s2, - bool eq1, bool eq2) - { - return - eq1 ? (s2 > p ? 1 : -1) // Point on level s1, E/W depending on s2 - : eq2 ? (s1 > p ? -1 : 1) // idem - : s1 < p && s2 > p ? 2 // Point between s1 -> s2 --> E - : s2 < p && s1 > p ? -2 // Point between s2 -> s1 --> W - : 0; - } -}; +} // namespace detail +#endif // DOXYGEN_NO_DETAIL /*! -\brief Within detection using winding rule +\brief Within detection using winding rule. Side strategy used internally is + choosen based on Point's coordinate system. \ingroup strategies \tparam Point \tparam_point \tparam PointOfSegment \tparam_segment_point -\tparam SideStrategy Side strategy \tparam CalculationType \tparam_calculation -\author Barend Gehrels -\note The implementation is inspired by terralib http://www.terralib.org (LGPL) -\note but totally revised afterwards, especially for cases on segments -\note Only dependant on "side", -> agnostic, suitable for spherical/latlong \qbk{ [heading See also] @@ -330,245 +102,32 @@ template < typename Point, typename PointOfSegment = Point, - typename SideStrategy = typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type, typename CalculationType = void > class winding -{ - typedef typename select_calculation_type + : public within::detail::winding_base_type < - Point, - PointOfSegment, - CalculationType - >::type calculation_type; - - /*! subclass to keep state */ - class counter - { - int m_count; - bool m_touches; - - inline int code() const - { - return m_touches ? 0 : m_count == 0 ? -1 : 1; - } - - public : - friend class winding; - - template - friend struct winding_check_touch; - - inline counter() - : m_count(0) - , m_touches(false) - {} - - }; - - static inline int check_segment(Point const& point, - PointOfSegment const& seg1, PointOfSegment const& seg2, - counter& state, bool& eq1, bool& eq2) - { - if (winding_check_touch - ::apply(point, seg1, seg2, state, eq1, eq2)) - { - return 0; - } - - calculation_type const p = get<0>(point); - calculation_type const s1 = get<0>(seg1); - calculation_type const s2 = get<0>(seg2); - return winding_calculate_count - ::apply(p, s1, s2, eq1, eq2); - } - + Point, PointOfSegment, CalculationType + >::type +{ + typedef typename within::detail::winding_base_type + < + Point, PointOfSegment, CalculationType + >::type base_t; public: - typedef typename SideStrategy::envelope_strategy_type envelope_strategy_type; + winding() {} - inline envelope_strategy_type get_envelope_strategy() const - { - return m_side_strategy.get_envelope_strategy(); - } - - typedef typename SideStrategy::disjoint_strategy_type disjoint_strategy_type; - - inline disjoint_strategy_type get_disjoint_strategy() const - { - return m_side_strategy.get_disjoint_strategy(); - } - - winding() + template + explicit winding(Model const& model) + : base_t(model) {} - - explicit winding(SideStrategy const& side_strategy) - : m_side_strategy(side_strategy) - {} - - // Typedefs and static methods to fulfill the concept - typedef Point point_type; - typedef PointOfSegment segment_point_type; - typedef counter state_type; - - inline bool apply(Point const& point, - PointOfSegment const& s1, PointOfSegment const& s2, - counter& state) const - { - typedef typename cs_tag::type cs_t; - - bool eq1 = false; - bool eq2 = false; - boost::ignore_unused(eq2); - - int count = check_segment(point, s1, s2, state, eq1, eq2); - if (count != 0) - { - int side = 0; - if (count == 1 || count == -1) - { - side = winding_side_equal::apply(point, eq1 ? s1 : s2, count); - } - else // count == 2 || count == -2 - { - // 1 left, -1 right - side = m_side_strategy.apply(s1, s2, point); - } - - if (side == 0) - { - // Point is lying on segment - state.m_touches = true; - state.m_count = 0; - return false; - } - - // Side is NEG for right, POS for left. - // The count is -2 for down, 2 for up (or -1/1) - // Side positive thus means UP and LEFTSIDE or DOWN and RIGHTSIDE - // See accompagnying figure (TODO) - if (side * count > 0) - { - state.m_count += count; - } - } - return ! state.m_touches; - } - - static inline int result(counter const& state) - { - return state.code(); - } - -private: - SideStrategy m_side_strategy; }; -#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS - -namespace services -{ - -template -struct default_strategy -{ - typedef winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -} // namespace services - -#endif - - }} // namespace strategy::within -#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS -namespace strategy { namespace covered_by { namespace services -{ - -template -struct default_strategy -{ - typedef within::winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef within::winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef within::winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef within::winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -}}} // namespace strategy::covered_by::services -#endif - - }} // namespace boost::geometry diff --git a/include/boost/geometry/strategies/cartesian/intersection.hpp b/include/boost/geometry/strategies/cartesian/intersection.hpp index 50e903885..233bb50b6 100644 --- a/include/boost/geometry/strategies/cartesian/intersection.hpp +++ b/include/boost/geometry/strategies/cartesian/intersection.hpp @@ -33,10 +33,10 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -81,11 +81,10 @@ struct cartesian_segments template struct point_in_geometry_strategy { - typedef strategy::within::winding + typedef strategy::within::cartesian_winding < typename point_type::type, typename point_type::type, - side_strategy_type, CalculationType > type; }; diff --git a/include/boost/geometry/strategies/cartesian/point_in_poly_winding.hpp b/include/boost/geometry/strategies/cartesian/point_in_poly_winding.hpp new file mode 100644 index 000000000..c41bc9b83 --- /dev/null +++ b/include/boost/geometry/strategies/cartesian/point_in_poly_winding.hpp @@ -0,0 +1,296 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013, 2014, 2016, 2017. +// Modifications copyright (c) 2013-2017 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. + +// 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_STRATEGY_CARTESIAN_POINT_IN_POLY_WINDING_HPP +#define BOOST_GEOMETRY_STRATEGY_CARTESIAN_POINT_IN_POLY_WINDING_HPP + + +#include + +#include +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace within +{ + + +/*! +\brief Within detection using winding rule in cartesian coordinate system. +\ingroup strategies +\tparam Point \tparam_point +\tparam PointOfSegment \tparam_segment_point +\tparam CalculationType \tparam_calculation +\author Barend Gehrels +\note The implementation is inspired by terralib http://www.terralib.org (LGPL) +\note but totally revised afterwards, especially for cases on segments + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] +} + */ +template +< + typename Point, + typename PointOfSegment = Point, + typename CalculationType = void +> +class cartesian_winding +{ + typedef side::side_by_triangle side_strategy_type; + + typedef typename select_calculation_type + < + Point, + PointOfSegment, + CalculationType + >::type calculation_type; + + /*! subclass to keep state */ + class counter + { + int m_count; + bool m_touches; + + inline int code() const + { + return m_touches ? 0 : m_count == 0 ? -1 : 1; + } + + public : + friend class cartesian_winding; + + inline counter() + : m_count(0) + , m_touches(false) + {} + + }; + +public: + typedef typename side_strategy_type::envelope_strategy_type envelope_strategy_type; + + static inline envelope_strategy_type get_envelope_strategy() + { + return side_strategy_type::get_envelope_strategy(); + } + + typedef typename side_strategy_type::disjoint_strategy_type disjoint_strategy_type; + + static inline disjoint_strategy_type get_disjoint_strategy() + { + return side_strategy_type::get_disjoint_strategy(); + } + + // Typedefs and static methods to fulfill the concept + typedef Point point_type; + typedef PointOfSegment segment_point_type; + typedef counter state_type; + + static inline bool apply(Point const& point, + PointOfSegment const& s1, PointOfSegment const& s2, + counter& state) + { + bool eq1 = false; + bool eq2 = false; + + int count = check_segment(point, s1, s2, state, eq1, eq2); + if (count != 0) + { + int side = 0; + if (count == 1 || count == -1) + { + side = side_equal(point, eq1 ? s1 : s2, count); + } + else // count == 2 || count == -2 + { + // 1 left, -1 right + side = side_strategy_type::apply(s1, s2, point); + } + + if (side == 0) + { + // Point is lying on segment + state.m_touches = true; + state.m_count = 0; + return false; + } + + // Side is NEG for right, POS for left. + // The count is -2 for left, 2 for right (or -1/1) + // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE + // See accompagnying figure (TODO) + if (side * count > 0) + { + state.m_count += count; + } + } + return ! state.m_touches; + } + + static inline int result(counter const& state) + { + return state.code(); + } + +private: + static inline int check_segment(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, bool& eq2) + { + if (check_touch(point, seg1, seg2, state, eq1, eq2)) + { + return 0; + } + + return calculate_count(point, seg1, seg2, eq1, eq2); + } + + static inline bool check_touch(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, bool& eq2) + { + calculation_type const px = get<0>(point); + calculation_type const s1x = get<0>(seg1); + calculation_type const s2x = get<0>(seg2); + + eq1 = math::equals(s1x, px); + eq2 = math::equals(s2x, px); + + // Both equal p -> segment vertical + // The only thing which has to be done is check if point is ON segment + if (eq1 && eq2) + { + calculation_type const py = get<1>(point); + calculation_type const s1y = get<1>(seg1); + calculation_type const s2y = get<1>(seg2); + if ((s1y <= py && s2y >= py) || (s2y <= py && s1y >= py)) + { + state.m_touches = true; + } + return true; + } + return false; + } + + static inline int calculate_count(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + bool eq1, bool eq2) + { + calculation_type const p = get<0>(point); + calculation_type const s1 = get<0>(seg1); + calculation_type const s2 = get<0>(seg2); + + return eq1 ? (s2 > p ? 1 : -1) // Point on level s1, E/W depending on s2 + : eq2 ? (s1 > p ? -1 : 1) // idem + : s1 < p && s2 > p ? 2 // Point between s1 -> s2 --> E + : s2 < p && s1 > p ? -2 // Point between s2 -> s1 --> W + : 0; + } + + static inline int side_equal(Point const& point, + PointOfSegment const& se, + int count) + { + // NOTE: for D=0 the signs would be reversed + return math::equals(get<1>(point), get<1>(se)) ? + 0 : + get<1>(point) < get<1>(se) ? + // assuming count is equal to 1 or -1 + -count : // ( count > 0 ? -1 : 1) : + count; // ( count > 0 ? 1 : -1) ; + } +}; + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +namespace services +{ + +template +struct default_strategy +{ + typedef cartesian_winding + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +template +struct default_strategy +{ + typedef cartesian_winding + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +} // namespace services + +#endif + + +}} // namespace strategy::within + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace strategy { namespace covered_by { namespace services +{ + +template +struct default_strategy +{ + typedef within::cartesian_winding + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +template +struct default_strategy +{ + typedef within::cartesian_winding + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +}}} // namespace strategy::covered_by::services +#endif + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGY_CARTESIAN_POINT_IN_POLY_WINDING_HPP diff --git a/include/boost/geometry/strategies/cartesian/side_by_triangle.hpp b/include/boost/geometry/strategies/cartesian/side_by_triangle.hpp index 4d1d97520..91bbee7bb 100644 --- a/include/boost/geometry/strategies/cartesian/side_by_triangle.hpp +++ b/include/boost/geometry/strategies/cartesian/side_by_triangle.hpp @@ -4,10 +4,11 @@ // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017, 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. @@ -29,9 +30,9 @@ #include #include +#include #include -#include #include @@ -182,10 +183,11 @@ public : // arguments, we cyclically permute them so that the first // argument is always the lexicographically smallest point. - geometry::detail::relate::less less; - if (less(p, p1)) + typedef compare::cartesian less; + + if (less::apply(p, p1)) { - if (less(p, p2)) + if (less::apply(p, p2)) { // p is the lexicographically smallest return side_value(p, p1, p2, epsp); @@ -197,7 +199,7 @@ public : } } - if (less(p1, p2)) + if (less::apply(p1, p2)) { // p1 is the lexicographically smallest return side_value(p1, p2, p, epsp); diff --git a/include/boost/geometry/strategies/compare.hpp b/include/boost/geometry/strategies/compare.hpp index 295831922..b196b75ec 100644 --- a/include/boost/geometry/strategies/compare.hpp +++ b/include/boost/geometry/strategies/compare.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 2017. +// Modifications copyright (c) 2017, 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,155 +20,204 @@ #ifndef BOOST_GEOMETRY_STRATEGIES_COMPARE_HPP #define BOOST_GEOMETRY_STRATEGIES_COMPARE_HPP + #include #include -#include +#include +#include +#include #include #include +#include -#include +#include namespace boost { namespace geometry { -/*! - \brief Traits class binding a comparing strategy to a coordinate system - \ingroup util - \tparam Tag tag of coordinate system of point-type - \tparam Direction direction to compare on: 1 for less (-> ascending order) - and -1 for greater (-> descending order) - \tparam Point point-type - \tparam CoordinateSystem coordinate sytem of point - \tparam Dimension: the dimension to compare on -*/ -template -< - typename Tag, - int Direction, - typename Point, - typename CoordinateSystem, - std::size_t Dimension -> -struct strategy_compare -{ - typedef strategy::not_implemented type; -}; - - -#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS - -// For compare we add defaults specializations, -// because they defaultly redirect to std::less / greater / equal_to -template -< - typename Tag, - typename Point, - typename CoordinateSystem, - std::size_t Dimension -> -struct strategy_compare -{ - typedef std::less::type> type; -}; - - -template -< - typename Tag, - typename Point, - typename CoordinateSystem, - std::size_t Dimension -> -struct strategy_compare -{ - typedef std::greater::type> type; -}; - - -template -< - typename Tag, - typename Point, - typename CoordinateSystem, - std::size_t Dimension -> -struct strategy_compare -{ - typedef std::equal_to::type> type; -}; - - -#endif - - namespace strategy { namespace compare { -/*! - \brief Default strategy, indicates the default strategy for comparisons - \details The default strategy for comparisons defer in most cases - to std::less (for ascending) and std::greater (for descending). - However, if a spherical coordinate system is used, and comparison - is done on longitude, it will take another strategy handling circular -*/ -struct default_strategy {}; +struct less +{ + template + static inline bool apply(T1 const& l, T2 const& r) + { + return l < r; + } +}; + +struct greater +{ + template + static inline bool apply(T1 const& l, T2 const& r) + { + return l > r; + } +}; + +struct equal_to +{ + template + static inline bool apply(T1 const& , T2 const& ) + { + return false; + } +}; #ifndef DOXYGEN_NO_DETAIL namespace detail { -template -struct is_default : boost::false_type -{}; - -template <> -struct is_default : boost::true_type -{}; - - -/*! - \brief Meta-function to select strategy - \details If "default_strategy" is specified, it will take the - traits-registered class for the specified coordinate system. - If another strategy is explicitly specified, it takes that one. -*/ template < - typename Strategy, - int Direction, - typename Point, - std::size_t Dimension + typename ComparePolicy, + std::size_t Dimension, + std::size_t DimensionCount > -struct select_strategy +struct compare_loop { - typedef typename - boost::mpl::if_ - < - is_default, - typename strategy_compare - < - typename cs_tag::type, - Direction, - Point, - typename coordinate_system::type, - Dimension - >::type, - Strategy - >::type type; + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + typename geometry::coordinate_type::type const& + cleft = geometry::get(left); + typename geometry::coordinate_type::type const& + cright = geometry::get(right); + + if (math::equals(cleft, cright)) + { + return compare_loop + < + ComparePolicy, + Dimension + 1, DimensionCount + >::apply(left, right); + } + else + { + return ComparePolicy::apply(cleft, cright); + } + } +}; + +template +< + typename ComparePolicy, + std::size_t DimensionCount +> +struct compare_loop +{ + template + static inline bool apply(Point1 const& , Point2 const& ) + { + // On coming here, points are equal. + // Return false for less/greater. + return false; + } +}; + +template +< + std::size_t DimensionCount +> +struct compare_loop +{ + template + static inline bool apply(Point1 const& , Point2 const& ) + { + // On coming here, points are equal. + // Return true for equal_to. + return true; + } }; } // namespace detail #endif // DOXYGEN_NO_DETAIL -}} // namespace strategy::compare +template +< + typename ComparePolicy, + int Dimension = -1 +> +struct cartesian +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::compare_loop + < + ComparePolicy, Dimension, Dimension + 1 + >::apply(left, right); + } +}; + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +template +< + typename ComparePolicy +> +struct cartesian +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::compare_loop + < + ComparePolicy, + 0, + boost::mpl::min + < + geometry::dimension, + geometry::dimension + >::type::value + >::apply(left, right); + } +}; +#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +namespace services +{ + + +template +< + typename ComparePolicy, + typename Point1, + typename Point2 = Point1, + int Dimension = -1, + typename CSTag1 = typename cs_tag::type, + typename CSTag2 = typename cs_tag::type +> +struct default_strategy +{ + BOOST_MPL_ASSERT_MSG + ( + false, + NOT_IMPLEMENTED_FOR_THESE_TYPES, + (types) + ); +}; + + +template +struct default_strategy +{ + typedef compare::cartesian type; +}; + + +} // namespace services + + +}} // namespace strategy compare }} // namespace boost::geometry diff --git a/include/boost/geometry/strategies/geographic/area.hpp b/include/boost/geometry/strategies/geographic/area.hpp index e1d3b09b5..7b03abf4f 100644 --- a/include/boost/geometry/strategies/geographic/area.hpp +++ b/include/boost/geometry/strategies/geographic/area.hpp @@ -15,12 +15,11 @@ #include #include -#include +#include +#include #include -#include - namespace boost { namespace geometry { @@ -83,18 +82,18 @@ protected : CT const m_e2; // squared eccentricity CT const m_ep2; // squared second eccentricity CT const m_ep; // second eccentricity - CT const m_c2; // authalic radius + CT const m_c2; // squared authalic radius inline spheroid_constants(Spheroid const& spheroid) : m_spheroid(spheroid) , m_a2(math::sqr(get_radius<0>(spheroid))) - , m_e2(formula::flattening(spheroid) - * (CT(2.0) - CT(formula::flattening(spheroid)))) + , m_e2(formula::eccentricity_sqr(spheroid)) , m_ep2(m_e2 / (CT(1.0) - m_e2)) , m_ep(math::sqrt(m_ep2)) - , m_c2((m_a2 / CT(2.0)) + - ((math::sqr(get_radius<2>(spheroid)) * boost::math::atanh(math::sqrt(m_e2))) - / (CT(2.0) * math::sqrt(m_e2)))) + , m_c2(formula_dispatch::authalic_radius_sqr + < + CT, Spheroid, srs_spheroid_tag + >::apply(m_a2, m_e2)) {} }; diff --git a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp new file mode 100644 index 000000000..4a220d1c9 --- /dev/null +++ b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp @@ -0,0 +1,276 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2016-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, 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_STRATEGIES_GEOGRAPHIC_DISTANCE_CROSS_TRACK_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_CROSS_TRACK_HPP + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK +# include +#endif + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace distance +{ + +/*! +\brief Strategy functor for distance point to segment calculation on ellipsoid +\ingroup strategies +\details Class which calculates the distance of a point to a segment, for points +on the ellipsoid +\see https://arxiv.org/abs/1102.1215 +\tparam FormulaPolicy underlying point-point distance strategy +\tparam Spheroid is the spheroidal model used +\tparam CalculationType \tparam_calculation +*/ +template +< + typename FormulaPolicy = strategy::andoyer, + typename Spheroid = srs::spheroid, + typename CalculationType = void +> +class geographic_cross_track +{ +public : + template + struct return_type + : promote_floating_point + < + typename select_calculation_type + < + Point, + PointOfSegment, + CalculationType + >::type + > + {}; + + struct distance_strategy + { + typedef geographic type; + }; + + inline typename distance_strategy::type get_distance_strategy() const + { + typedef typename distance_strategy::type distance_type; + return distance_type(m_spheroid); + } + + explicit geographic_cross_track(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) + {} + + template + inline typename return_type::type + apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const + { + typedef typename coordinate_system::type::units units_type; + + typedef typename return_type::type CT; + + return (geometry::formula::distance_point_segment + < + CT, + units_type, + FormulaPolicy::template inverse, + FormulaPolicy::template direct + >::apply(get<0>(sp1), get<1>(sp1), + get<0>(sp2), get<1>(sp2), + get<0>(p), get<1>(p), + m_spheroid)).distance; + } + +private : + Spheroid m_spheroid; + +}; + + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace services +{ + +//tags +template +struct tag > +{ + typedef strategy_tag_distance_point_segment type; +}; + +template +< + typename FormulaPolicy, + typename Spheroid +> +struct tag > +{ + typedef strategy_tag_distance_point_segment type; +}; + +template +< + typename FormulaPolicy, + typename Spheroid, + typename CalculationType +> +struct tag > +{ + typedef strategy_tag_distance_point_segment type; +}; + + +//return types +template +struct return_type, P, PS> + : geographic_cross_track::template return_type +{}; + +template +< + typename FormulaPolicy, + typename Spheroid, + typename P, + typename PS +> +struct return_type, P, PS> + : geographic_cross_track::template return_type +{}; + +template +< + typename FormulaPolicy, + typename Spheroid, + typename CalculationType, + typename P, + typename PS +> +struct return_type, P, PS> + : geographic_cross_track::template return_type +{}; + +//comparable types +template +< + typename FormulaPolicy, + typename Spheroid, + typename CalculationType +> +struct comparable_type > +{ + typedef geographic_cross_track + < + FormulaPolicy, Spheroid, CalculationType + > type; +}; + +template +< + typename FormulaPolicy, + typename Spheroid, + typename CalculationType +> +struct get_comparable > +{ + typedef typename comparable_type + < + geographic_cross_track + >::type comparable_type; +public : + static inline comparable_type + apply(geographic_cross_track const& strategy) + { + return comparable_type(); + } +}; + + +template +< + typename FormulaPolicy, + typename P, + typename PS +> +struct result_from_distance, P, PS> +{ +private : + typedef typename geographic_cross_track + < + FormulaPolicy + >::template return_type::type return_type; +public : + template + static inline return_type + apply(geographic_cross_track const& , T const& distance) + { + return distance; + } +}; + + +template +struct default_strategy + < + point_tag, segment_tag, Point, PointOfSegment, + geographic_tag, geographic_tag + > +{ + typedef geographic_cross_track<> type; +}; + + +template +struct default_strategy + < + segment_tag, point_tag, PointOfSegment, Point, + geographic_tag, geographic_tag + > +{ + typedef typename default_strategy + < + point_tag, segment_tag, Point, PointOfSegment, + geographic_tag, geographic_tag + >::type type; +}; + +} // namespace services +#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +}} // namespace strategy::distance + +}} // namespace boost::geometry +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_CROSS_TRACK_HPP diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index 84acd149c..e91659d40 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -78,11 +80,12 @@ struct geographic_segments template struct point_in_geometry_strategy { - typedef strategy::within::winding + typedef strategy::within::geographic_winding < typename point_type::type, typename point_type::type, - side_strategy_type, + FormulaPolicy, + Spheroid, CalculationType > type; }; @@ -95,7 +98,7 @@ struct geographic_segments < Geometry1, Geometry2 >::type strategy_type; - return strategy_type(get_side_strategy()); + return strategy_type(m_spheroid); } template @@ -289,8 +292,12 @@ private: typedef typename select_calculation_type ::type calc_t; + typedef srs::spheroid spheroid_type; + + static const calc_t c0 = 0; + // normalized spheroid - srs::spheroid spheroid = normalized_spheroid(m_spheroid); + spheroid_type spheroid = formula::unit_spheroid(m_spheroid); // TODO: check only 2 first coordinates here? using geometry::detail::equals::equals_point_point; @@ -325,31 +332,80 @@ private: // TODO: no need to call inverse formula if we know that the points are equal // distance can be set to 0 in this case and azimuth may be not calculated - bool const is_equal_a1_b1 = equals_point_point(a1, b1); - bool const is_equal_a2_b1 = equals_point_point(a2, b1); + bool is_equal_a1_b1 = equals_point_point(a1, b1); + bool is_equal_a2_b1 = equals_point_point(a2, b1); + bool degen_neq_coords = false; - inverse_result res_b1_b2 = inverse_dist_azi::apply(b1_lon, b1_lat, b2_lon, b2_lat, spheroid); - inverse_result res_b1_a1 = inverse_dist_azi::apply(b1_lon, b1_lat, a1_lon, a1_lat, spheroid); - inverse_result res_b1_a2 = inverse_dist_azi::apply(b1_lon, b1_lat, a2_lon, a2_lat, spheroid); - sides.set<0>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_b1_a1.azimuth, res_b1_b2.azimuth), - is_equal_a2_b1 ? 0 : formula::azimuth_side_value(res_b1_a2.azimuth, res_b1_b2.azimuth)); - if (sides.same<0>()) + inverse_result res_b1_b2, res_b1_a1, res_b1_a2; + if (! b_is_point) { - // Both points are at the same side of other segment, we can leave - return Policy::disjoint(); + res_b1_b2 = inverse_dist_azi::apply(b1_lon, b1_lat, b2_lon, b2_lat, spheroid); + if (math::equals(res_b1_b2.distance, c0)) + { + b_is_point = true; + degen_neq_coords = true; + } + else + { + res_b1_a1 = inverse_dist_azi::apply(b1_lon, b1_lat, a1_lon, a1_lat, spheroid); + if (math::equals(res_b1_a1.distance, c0)) + { + is_equal_a1_b1 = true; + } + res_b1_a2 = inverse_dist_azi::apply(b1_lon, b1_lat, a2_lon, a2_lat, spheroid); + if (math::equals(res_b1_a2.distance, c0)) + { + is_equal_a2_b1 = true; + } + sides.set<0>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_b1_a1.azimuth, res_b1_b2.azimuth), + is_equal_a2_b1 ? 0 : formula::azimuth_side_value(res_b1_a2.azimuth, res_b1_b2.azimuth)); + if (sides.same<0>()) + { + // Both points are at the same side of other segment, we can leave + return Policy::disjoint(); + } + } } - bool const is_equal_a1_b2 = equals_point_point(a1, b2); + bool is_equal_a1_b2 = equals_point_point(a1, b2); - inverse_result res_a1_a2 = inverse_dist_azi::apply(a1_lon, a1_lat, a2_lon, a2_lat, spheroid); - inverse_result res_a1_b1 = inverse_dist_azi::apply(a1_lon, a1_lat, b1_lon, b1_lat, spheroid); - inverse_result res_a1_b2 = inverse_dist_azi::apply(a1_lon, a1_lat, b2_lon, b2_lat, spheroid); - sides.set<1>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_a1_b1.azimuth, res_a1_a2.azimuth), - is_equal_a1_b2 ? 0 : formula::azimuth_side_value(res_a1_b2.azimuth, res_a1_a2.azimuth)); - if (sides.same<1>()) + inverse_result res_a1_a2, res_a1_b1, res_a1_b2; + if (! a_is_point) { - // Both points are at the same side of other segment, we can leave - return Policy::disjoint(); + res_a1_a2 = inverse_dist_azi::apply(a1_lon, a1_lat, a2_lon, a2_lat, spheroid); + if (math::equals(res_a1_a2.distance, c0)) + { + a_is_point = true; + degen_neq_coords = true; + } + else + { + res_a1_b1 = inverse_dist_azi::apply(a1_lon, a1_lat, b1_lon, b1_lat, spheroid); + if (math::equals(res_a1_b1.distance, c0)) + { + is_equal_a1_b1 = true; + } + res_a1_b2 = inverse_dist_azi::apply(a1_lon, a1_lat, b2_lon, b2_lat, spheroid); + if (math::equals(res_a1_b2.distance, c0)) + { + is_equal_a1_b2 = true; + } + sides.set<1>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_a1_b1.azimuth, res_a1_a2.azimuth), + is_equal_a1_b2 ? 0 : formula::azimuth_side_value(res_a1_b2.azimuth, res_a1_a2.azimuth)); + if (sides.same<1>()) + { + // Both points are at the same side of other segment, we can leave + return Policy::disjoint(); + } + } + } + + if(a_is_point && b_is_point) + { + return is_equal_a1_b2 + ? Policy::degenerate(a, true) + : Policy::disjoint() + ; } // NOTE: at this point the segments may still be disjoint @@ -379,11 +435,11 @@ private: { if (a_is_point) { - return collinear_one_degenerated(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1, is_b_reversed); + return collinear_one_degenerated(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1, res_b1_a2, is_b_reversed, degen_neq_coords); } else if (b_is_point) { - return collinear_one_degenerated(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1, is_a_reversed); + return collinear_one_degenerated(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1, res_a1_b2, is_a_reversed, degen_neq_coords); } else { @@ -392,16 +448,16 @@ private: // use shorter segment if (res_a1_a2.distance <= res_b1_b2.distance) { - calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b1, dist_a1_a2, dist_a1_b1); - calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b2, dist_a1_a2, dist_a1_b2); + calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b1, res_a1_b2, dist_a1_a2, dist_a1_b1); + calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b2, res_a1_b1, dist_a1_a2, dist_a1_b2); dist_b1_b2 = dist_a1_b2 - dist_a1_b1; dist_b1_a1 = -dist_a1_b1; dist_b1_a2 = dist_a1_a2 - dist_a1_b1; } else { - calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a1, dist_b1_b2, dist_b1_a1); - calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a2, dist_b1_b2, dist_b1_a2); + calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a1, res_b1_a2, dist_b1_b2, dist_b1_a1); + calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a2, res_b1_a1, dist_b1_b2, dist_b1_a2); dist_a1_a2 = dist_b1_a2 - dist_b1_a1; dist_a1_b1 = -dist_b1_a1; dist_a1_b2 = dist_b1_b2 - dist_b1_a1; @@ -549,11 +605,13 @@ private: Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2, ResultInverse const& res_a1_a2, - ResultInverse const& res_a1_bi, - bool is_other_reversed) + ResultInverse const& res_a1_b1, + ResultInverse const& res_a1_b2, + bool is_other_reversed, + bool degen_neq_coords) { CalcT dist_1_2, dist_1_o; - if (! calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_bi, dist_1_2, dist_1_o)) + if (! calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b1, res_a1_b2, dist_1_2, dist_1_o, degen_neq_coords)) { return Policy::disjoint(); } @@ -574,13 +632,16 @@ private: static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, // in Point2 const& b1, Point2 const& b2, // in ResultInverse const& res_a1_a2, // in - ResultInverse const& res_a1_bi, // in - CalcT& dist_a1_a2, CalcT& dist_a1_bi) // out + ResultInverse const& res_a1_b1, // in + ResultInverse const& res_a1_b2, // in + CalcT& dist_a1_a2, // out + CalcT& dist_a1_bi, // out + bool degen_neq_coords = false) // in { dist_a1_a2 = res_a1_a2.distance; - dist_a1_bi = res_a1_bi.distance; - if (! same_direction(res_a1_bi.azimuth, res_a1_a2.azimuth)) + dist_a1_bi = res_a1_b1.distance; + if (! same_direction(res_a1_b1.azimuth, res_a1_a2.azimuth)) { dist_a1_bi = -dist_a1_bi; } @@ -598,6 +659,22 @@ private: return true; } + // check the other endpoint of a very short segment near the pole + if (degen_neq_coords) + { + static CalcT const c0 = 0; + if (math::equals(res_a1_b2.distance, c0)) + { + dist_a1_bi = 0; + return true; + } + else if (math::equals(dist_a1_a2 - res_a1_b2.distance, c0)) + { + dist_a1_bi = dist_a1_a2; + return true; + } + } + // or i1 is on b return segment_ratio(dist_a1_bi, dist_a1_a2).on_segment(); } @@ -825,8 +902,9 @@ private: static inline bool is_endpoint_equal(CalcT const& dist, P1 const& ai, P2 const& b1, P2 const& b2) { + static CalcT const c0 = 0; using geometry::detail::equals::equals_point_point; - return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2)); + return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2) || math::equals(dist, c0)); } template @@ -885,14 +963,6 @@ private: ip_flag; } - template - static inline srs::spheroid normalized_spheroid(SpheroidT const& spheroid) - { - return srs::spheroid(CalcT(1), - CalcT(get_radius<2>(spheroid)) // b/a - / CalcT(get_radius<0>(spheroid))); - } - private: Spheroid m_spheroid; }; diff --git a/include/boost/geometry/strategies/geographic/parameters.hpp b/include/boost/geometry/strategies/geographic/parameters.hpp index c70b0cabe..4896e42e8 100644 --- a/include/boost/geometry/strategies/geographic/parameters.hpp +++ b/include/boost/geometry/strategies/geographic/parameters.hpp @@ -12,9 +12,10 @@ #include +#include #include +#include #include -#include #include #include @@ -25,6 +26,23 @@ namespace boost { namespace geometry { namespace strategy struct andoyer { + //TODO: this should be replaced by an andoyer direct formula + template + < + typename CT, + bool EnableCoordinates = true, + bool EnableReverseAzimuth = false, + bool EnableReducedLength = false, + bool EnableGeodesicScale = false + > + struct direct + : formula::thomas_direct + < + CT, EnableCoordinates, EnableReverseAzimuth, + EnableReducedLength, EnableGeodesicScale + > + {}; + template < typename CT, @@ -46,6 +64,22 @@ struct andoyer struct thomas { + template + < + typename CT, + bool EnableCoordinates = true, + bool EnableReverseAzimuth = false, + bool EnableReducedLength = false, + bool EnableGeodesicScale = false + > + struct direct + : formula::thomas_direct + < + CT, EnableCoordinates, EnableReverseAzimuth, + EnableReducedLength, EnableGeodesicScale + > + {}; + template < typename CT, @@ -67,6 +101,22 @@ struct thomas struct vincenty { + template + < + typename CT, + bool EnableCoordinates = true, + bool EnableReverseAzimuth = false, + bool EnableReducedLength = false, + bool EnableGeodesicScale = false + > + struct direct + : formula::vincenty_direct + < + CT, EnableCoordinates, EnableReverseAzimuth, + EnableReducedLength, EnableGeodesicScale + > + {}; + template < typename CT, diff --git a/include/boost/geometry/strategies/geographic/point_in_poly_winding.hpp b/include/boost/geometry/strategies/geographic/point_in_poly_winding.hpp new file mode 100644 index 000000000..95a196147 --- /dev/null +++ b/include/boost/geometry/strategies/geographic/point_in_poly_winding.hpp @@ -0,0 +1,80 @@ +// Boost.Geometry + +// Copyright (c) 2017 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_STRATEGY_GEOGRAPHIC_POINT_IN_POLY_WINDING_HPP +#define BOOST_GEOMETRY_STRATEGY_GEOGRAPHIC_POINT_IN_POLY_WINDING_HPP + + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace within +{ + + +/*! +\brief Within detection using winding rule in geographic coordinate system. +\ingroup strategies +\tparam Point \tparam_point +\tparam PointOfSegment \tparam_segment_point +\tparam FormulaPolicy Geodesic formula policy +\tparam Spheroid Spheroid model +\tparam CalculationType \tparam_calculation + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] +} + */ +template +< + typename Point, + typename PointOfSegment = Point, + typename FormulaPolicy = strategy::andoyer, + typename Spheroid = srs::spheroid, + typename CalculationType = void +> +class geographic_winding + : public within::detail::spherical_winding_base + < + Point, + PointOfSegment, + side::geographic, + CalculationType + > +{ + typedef within::detail::spherical_winding_base + < + Point, + PointOfSegment, + side::geographic, + CalculationType + > base_t; + +public: + geographic_winding() + {} + + explicit geographic_winding(Spheroid const& model) + : base_t(model) + {} +}; + + +}} // namespace strategy::within + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGY_GEOGRAPHIC_POINT_IN_POLY_WINDING_HPP diff --git a/include/boost/geometry/strategies/spherical/compare.hpp b/include/boost/geometry/strategies/spherical/compare.hpp new file mode 100644 index 000000000..26163f740 --- /dev/null +++ b/include/boost/geometry/strategies/spherical/compare.hpp @@ -0,0 +1,321 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, 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_STRATEGIES_SPHERICAL_COMPARE_HPP +#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_HPP + + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +namespace strategy { namespace compare +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +static inline typename geometry::coordinate_type

::type +get(P const& p, boost::true_type /*same units*/) +{ + return geometry::get(p); +} + +template +static inline typename geometry::coordinate_type

::type +get(P const& p, boost::false_type /*different units*/) +{ + return geometry::get_as_radian(p); +} + +template +< + typename ComparePolicy, + typename Point1, + typename Point2, + std::size_t DimensionCount +> +struct spherical_latitude +{ + typedef typename geometry::coordinate_type::type coordinate1_type; + typedef typename geometry::coordinate_system::type::units units1_type; + typedef typename geometry::coordinate_type::type coordinate2_type; + typedef typename geometry::coordinate_system::type::units units2_type; + typedef typename boost::is_same::type same_units_type; + + template + static inline bool apply(Point1 const& left, Point2 const& right, + T1 const& l1, T2 const& r1) + { + // latitudes equal + if (math::equals(l1, r1)) + { + return compare::detail::compare_loop + < + ComparePolicy, 2, DimensionCount + >::apply(left, right); + } + else + { + return ComparePolicy::apply(l1, r1); + } + } + + static inline bool apply(Point1 const& left, Point2 const& right) + { + coordinate1_type const& l1 = compare::detail::get<1>(left, same_units_type()); + coordinate2_type const& r1 = compare::detail::get<1>(right, same_units_type()); + + return apply(left, right, l1, r1); + } +}; + +template +< + typename ComparePolicy, + typename Point1, + typename Point2 +> +struct spherical_latitude +{ + template + static inline bool apply(Point1 const& left, Point2 const& right, + T1 const& , T2 const& ) + { + return apply(left, right); + } + + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::compare_loop + < + ComparePolicy, 1, 1 + >::apply(left, right); + } +}; + +template +< + typename ComparePolicy, + typename Point1, + typename Point2, + std::size_t DimensionCount +> +struct spherical_longitude +{ + typedef typename geometry::coordinate_type::type coordinate1_type; + typedef typename geometry::coordinate_system::type::units units1_type; + typedef typename geometry::coordinate_type::type coordinate2_type; + typedef typename geometry::coordinate_system::type::units units2_type; + typedef typename boost::is_same::type same_units_type; + typedef typename boost::mpl::if_::type units_type; + + static const bool is_equatorial = ! boost::is_same + < + typename geometry::cs_tag::type, + geometry::spherical_polar_tag + >::value; + + static inline bool are_both_at_antimeridian(coordinate1_type const& l0, + coordinate2_type const& r0, + bool & is_left_at, + bool & is_right_at) + { + is_left_at = math::is_longitude_antimeridian(l0); + is_right_at = math::is_longitude_antimeridian(r0); + return is_left_at && is_right_at; + } + + static inline bool apply(Point1 const& left, Point2 const& right) + { + // if units are different the coordinates are in radians + coordinate1_type const& l0 = compare::detail::get<0>(left, same_units_type()); + coordinate2_type const& r0 = compare::detail::get<0>(right, same_units_type()); + coordinate1_type const& l1 = compare::detail::get<1>(left, same_units_type()); + coordinate2_type const& r1 = compare::detail::get<1>(right, same_units_type()); + + bool is_left_at_antimeridian = false; + bool is_right_at_antimeridian = false; + + // longitudes equal + if (math::equals(l0, r0) + // both at antimeridian + || are_both_at_antimeridian(l0, r0, is_left_at_antimeridian, is_right_at_antimeridian) + // both at pole + || (math::equals(l1, r1) + && math::is_latitude_pole(l1))) + { + return spherical_latitude + < + ComparePolicy, Point1, Point2, DimensionCount + >::apply(left, right, l1, r1); + } + // if left is at antimeridian and right is not at antimeridian + // then left is greater than right + else if (is_left_at_antimeridian) + { + // less/equal_to -> false, greater -> true + return ComparePolicy::apply(1, 0); + } + // if right is at antimeridian and left is not at antimeridian + // then left is lesser than right + else if (is_right_at_antimeridian) + { + // less -> true, equal_to/greater -> false + return ComparePolicy::apply(0, 1); + } + else + { + return ComparePolicy::apply(l0, r0); + } + } +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +/*! +\brief Compare strategy for spherical coordinates +\ingroup strategies +\tparam Point point-type +\tparam Dimension dimension +*/ +template +< + typename ComparePolicy, + int Dimension = -1 +> +struct spherical + : cartesian +{}; + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +// all dimensions starting from longitude +template +struct spherical +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::spherical_longitude + < + ComparePolicy, + Point1, + Point2, + boost::mpl::min + < + geometry::dimension, + geometry::dimension + >::type::value + >::apply(left, right); + } +}; + +// only longitudes (and latitudes to check poles) +template +struct spherical +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::spherical_longitude + < + ComparePolicy, Point1, Point2, 1 + >::apply(left, right); + } +}; + +// only latitudes +template +struct spherical +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::spherical_latitude + < + ComparePolicy, Point1, Point2, 2 + >::apply(left, right); + } +}; + +#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + + +namespace services +{ + + +template +struct default_strategy + < + ComparePolicy, Point1, Point2, Dimension, + spherical_polar_tag, spherical_polar_tag + > +{ + typedef compare::spherical type; +}; + +template +struct default_strategy + < + ComparePolicy, Point1, Point2, Dimension, + spherical_equatorial_tag, spherical_equatorial_tag + > +{ + typedef compare::spherical type; +}; + +template +struct default_strategy + < + ComparePolicy, Point1, Point2, Dimension, + geographic_tag, geographic_tag + > +{ + typedef compare::spherical type; +}; + + +} // namespace services + + +}} // namespace strategy::compare + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_HPP diff --git a/include/boost/geometry/strategies/spherical/compare_circular.hpp b/include/boost/geometry/strategies/spherical/compare_circular.hpp deleted file mode 100644 index 2f890dfd8..000000000 --- a/include/boost/geometry/strategies/spherical/compare_circular.hpp +++ /dev/null @@ -1,152 +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_STRATEGIES_SPHERICAL_COMPARE_SPHERICAL_HPP -#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_SPHERICAL_HPP - -#include - -#include -#include -#include -#include - - -namespace boost { namespace geometry -{ - - -namespace strategy { namespace compare -{ - - -#ifndef DOXYGEN_NO_DETAIL -namespace detail -{ - -template -struct shift -{ -}; - -template <> -struct shift -{ - static inline double full() { return 360.0; } - static inline double half() { return 180.0; } -}; - -template <> -struct shift -{ - static inline double full() { return 2.0 * boost::math::constants::pi(); } - static inline double half() { return boost::math::constants::pi(); } -}; - -} // namespace detail -#endif - -/*! -\brief Compare (in one direction) strategy for spherical coordinates -\ingroup strategies -\tparam Point point-type -\tparam Dimension dimension -*/ -template -struct circular_comparator -{ - static inline CoordinateType put_in_range(CoordinateType const& c, - double min_border, double max_border) - { - CoordinateType value = c; - while (value < min_border) - { - value += detail::shift::full(); - } - while (value > max_border) - { - value -= detail::shift::full(); - } - return value; - } - - inline bool operator()(CoordinateType const& c1, CoordinateType const& c2) const - { - Compare compare; - - // Check situation that one of them is e.g. std::numeric_limits. - static const double full = detail::shift::full(); - double mx = 10.0 * full; - if (c1 < -mx || c1 > mx || c2 < -mx || c2 > mx) - { - // do normal comparison, using circular is not useful - return compare(c1, c2); - } - - static const double half = full / 2.0; - CoordinateType v1 = put_in_range(c1, -half, half); - CoordinateType v2 = put_in_range(c2, -half, half); - - // Two coordinates on a circle are - // at max <= half a circle away from each other. - // So if it is more, shift origin. - CoordinateType diff = geometry::math::abs(v1 - v2); - if (diff > half) - { - v1 = put_in_range(v1, 0, full); - v2 = put_in_range(v2, 0, full); - } - - return compare(v1, v2); - } -}; - -}} // namespace strategy::compare - -#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS - -// Specialize for the longitude (dim 0) -template -< - typename Point, - template class CoordinateSystem, - typename Units -> -struct strategy_compare, 0> -{ - typedef typename coordinate_type::type coordinate_type; - typedef strategy::compare::circular_comparator - < - coordinate_type, - Units, - std::less - > type; -}; - -template -< - typename Point, - template class CoordinateSystem, - typename Units -> -struct strategy_compare, 0> -{ - typedef typename coordinate_type::type coordinate_type; - typedef strategy::compare::circular_comparator - < - coordinate_type, - Units, - std::greater - > type; -}; - -#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_SPHERICAL_HPP diff --git a/include/boost/geometry/strategies/spherical/distance_cross_track.hpp b/include/boost/geometry/strategies/spherical/distance_cross_track.hpp index 7daafa4a1..82a2fb3d6 100644 --- a/include/boost/geometry/strategies/spherical/distance_cross_track.hpp +++ b/include/boost/geometry/strategies/spherical/distance_cross_track.hpp @@ -2,9 +2,10 @@ // 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. +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017, Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -26,8 +27,6 @@ #include #include -#include - #include #include #include @@ -373,19 +372,6 @@ public : typedef typename return_type::type return_type; -#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK - std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " " - << crs_AD * geometry::math::r2d() << std::endl; - std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " " - << crs_AB * geometry::math::r2d() << std::endl; - std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " " - << crs_BD * geometry::math::r2d << std::endl; - std::cout << "Projection AD-AB " << projection1 << " : " - << d_crs1 * geometry::math::r2d() << std::endl; - std::cout << "Projection BD-BA " << projection2 << " : " - << d_crs2 * geometry::math::r2d() << std::endl; -#endif - // http://williams.best.vwh.net/avform.htm#XTE return_type d1 = m_strategy.apply(sp1, p); return_type d3 = m_strategy.apply(sp1, sp2); @@ -398,10 +384,25 @@ public : return_type d2 = m_strategy.apply(sp2, p); - return_type crs_AD = geometry::detail::course(sp1, p); - return_type crs_AB = geometry::detail::course(sp1, sp2); - return_type crs_BA = crs_AB - geometry::math::pi(); - return_type crs_BD = geometry::detail::course(sp2, p); + return_type lon1 = geometry::get_as_radian<0>(sp1); + return_type lat1 = geometry::get_as_radian<1>(sp1); + return_type lon2 = geometry::get_as_radian<0>(sp2); + return_type lat2 = geometry::get_as_radian<1>(sp2); + return_type lon = geometry::get_as_radian<0>(p); + return_type lat = geometry::get_as_radian<1>(p); + + return_type crs_AD = geometry::formula::spherical_azimuth + (lon1, lat1, lon, lat).azimuth; + + geometry::formula::result_spherical result = + geometry::formula::spherical_azimuth + (lon1, lat1, lon2, lat2); + return_type crs_AB = result.azimuth; + return_type crs_BA = result.reverse_azimuth - geometry::math::pi(); + + return_type crs_BD = geometry::formula::spherical_azimuth + (lon2, lat2, lon, lat).azimuth; + return_type d_crs1 = crs_AD - crs_AB; return_type d_crs2 = crs_BD - crs_BA; @@ -409,6 +410,24 @@ public : return_type projection1 = cos( d_crs1 ) * d1 / d3; return_type projection2 = cos( d_crs2 ) * d2 / d3; +#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK + std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " " + << crs_AD * geometry::math::r2d() << std::endl; + std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " " + << crs_AB * geometry::math::r2d() << std::endl; + std::cout << "Course " << dsv(sp2) << " to " << dsv(sp1) << " " + << crs_BA * geometry::math::r2d() << std::endl; + std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " " + << crs_BD * geometry::math::r2d() << std::endl; + std::cout << "Projection AD-AB " << projection1 << " : " + << d_crs1 * geometry::math::r2d() << std::endl; + std::cout << "Projection BD-BA " << projection2 << " : " + << d_crs2 * geometry::math::r2d() << std::endl; + std::cout << " d1: " << (d1 ) + << " d2: " << (d2 ) + << std::endl; +#endif + if (projection1 > 0.0 && projection2 > 0.0) { #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index 035961c0a..b5ae878b8 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -33,7 +33,6 @@ #include -#include #include #include #include @@ -42,6 +41,7 @@ #include #include #include +#include #include #include @@ -94,11 +94,10 @@ struct ecef_segments template struct point_in_geometry_strategy { - typedef strategy::within::winding + typedef strategy::within::spherical_winding < typename point_type::type, typename point_type::type, - side_strategy_type, CalculationType > type; }; @@ -302,36 +301,64 @@ struct ecef_segments vec3d_t const b1v = calc_policy.template to_cart3d(b1); vec3d_t const b2v = calc_policy.template to_cart3d(b2); + bool degen_neq_coords = false; side_info sides; typename CalcPolicy::template plane plane2 = calc_policy.get_plane(b1v, b2v); - // not normalized normals, the same as in side strategy - sides.set<0>(plane2.side_value(a1v), plane2.side_value(a2v)); - if (sides.same<0>()) + calc_t dist_b1_b2 = 0; + if (! b_is_point) { - // Both points are at same side of other segment, we can leave - return Policy::disjoint(); + calculate_dist(b1v, b2v, plane2, dist_b1_b2); + if (math::equals(dist_b1_b2, c0)) + { + degen_neq_coords = true; + b_is_point = true; + dist_b1_b2 = 0; + } + else + { + // not normalized normals, the same as in side strategy + sides.set<0>(plane2.side_value(a1v), plane2.side_value(a2v)); + if (sides.same<0>()) + { + // Both points are at same side of other segment, we can leave + return Policy::disjoint(); + } + } } typename CalcPolicy::template plane plane1 = calc_policy.get_plane(a1v, a2v); - // not normalized normals, the same as in side strategy - sides.set<1>(plane1.side_value(b1v), plane1.side_value(b2v)); - if (sides.same<1>()) + calc_t dist_a1_a2 = 0; + if (! a_is_point) { - // Both points are at same side of other segment, we can leave - return Policy::disjoint(); + calculate_dist(a1v, a2v, plane1, dist_a1_a2); + if (math::equals(dist_a1_a2, c0)) + { + degen_neq_coords = true; + a_is_point = true; + dist_a1_a2 = 0; + } + else + { + // not normalized normals, the same as in side strategy + sides.set<1>(plane1.side_value(b1v), plane1.side_value(b2v)); + if (sides.same<1>()) + { + // Both points are at same side of other segment, we can leave + return Policy::disjoint(); + } + } } // NOTE: at this point the segments may still be disjoint - calc_t len1, len2; - + calc_t len1 = 0; // point or opposite sides of a sphere/spheroid, assume point - if (! detail::vec_normalize(plane1.normal, len1)) + if (! a_is_point && ! detail::vec_normalize(plane1.normal, len1)) { a_is_point = true; if (sides.get<0, 0>() == 0 || sides.get<0, 1>() == 0) @@ -340,7 +367,8 @@ struct ecef_segments } } - if (! detail::vec_normalize(plane2.normal, len2)) + calc_t len2 = 0; + if (! b_is_point && ! detail::vec_normalize(plane2.normal, len2)) { b_is_point = true; if (sides.get<1, 0>() == 0 || sides.get<1, 1>() == 0) @@ -404,30 +432,32 @@ struct ecef_segments { if (a_is_point) { - return collinear_one_degenerated(a, true, b1, b2, a1, a2, b1v, b2v, plane2, a1v); + return collinear_one_degenerated(a, true, b1, b2, a1, a2, b1v, b2v, + plane2, a1v, a2v, dist_b1_b2, degen_neq_coords); } else if (b_is_point) { // b2 used to be consistent with (degenerated) checks above (is it needed?) - return collinear_one_degenerated(b, false, a1, a2, b1, b2, a1v, a2v, plane1, b1v); + return collinear_one_degenerated(b, false, a1, a2, b1, b2, a1v, a2v, + plane1, b1v, b2v, dist_a1_a2, degen_neq_coords); } else { - calc_t dist_a1_a2, dist_a1_b1, dist_a1_b2; - calc_t dist_b1_b2, dist_b1_a1, dist_b1_a2; + calc_t dist_a1_b1, dist_a1_b2; + calc_t dist_b1_a1, dist_b1_a2; // use shorter segment if (len1 <= len2) { - calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b1v, dist_a1_a2, dist_a1_b1); - calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b2v, dist_a1_a2, dist_a1_b2); + calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b1v, b2v, dist_a1_a2, dist_a1_b1); + calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b2v, b1v, dist_a1_a2, dist_a1_b2); dist_b1_b2 = dist_a1_b2 - dist_a1_b1; dist_b1_a1 = -dist_a1_b1; dist_b1_a2 = dist_a1_a2 - dist_a1_b1; } else { - calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a1v, dist_b1_b2, dist_b1_a1); - calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a2v, dist_b1_b2, dist_b1_a2); + calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a1v, a2v, dist_b1_b2, dist_b1_a1); + calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a2v, a1v, dist_b1_b2, dist_b1_a2); dist_a1_a2 = dist_b1_a2 - dist_b1_a1; dist_a1_b1 = -dist_b1_a1; dist_a1_b2 = dist_b1_b2 - dist_b1_a1; @@ -487,10 +517,11 @@ struct ecef_segments vec3d_t i1; intersection_point_flag ip_flag; - calc_t dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1; + calc_t dist_a1_i1, dist_b1_i1; if (calculate_ip_data(a1, a2, b1, b2, a1v, a2v, b1v, b2v, - plane1, plane2, calc_policy, sides, - i1, dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1, ip_flag)) + plane1, plane2, calc_policy, + sides, dist_a1_a2, dist_b1_b2, + i1, dist_a1_i1, dist_b1_i1, ip_flag)) { // intersects segment_intersection_info @@ -520,12 +551,14 @@ private: collinear_one_degenerated(Segment const& segment, bool degenerated_a, Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2, - Vec3d const& v1, Vec3d const& v2, + Vec3d const& a1v, Vec3d const& a2v, Plane const& plane, - Vec3d const& vother) + Vec3d const& b1v, Vec3d const& b2v, + CalcT const& dist_1_2, + bool degen_neq_coords) { - CalcT dist_1_2, dist_1_o; - return ! calculate_collinear_data(a1, a2, b1, b2, v1, v2, plane, vother, dist_1_2, dist_1_o) + CalcT dist_1_o; + return ! calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane, b1v, b2v, dist_1_2, dist_1_o, degen_neq_coords) ? Policy::disjoint() : Policy::one_degenerate(segment, segment_ratio(dist_1_o, dist_1_2), degenerated_a); } @@ -536,11 +569,14 @@ private: Vec3d const& a1v, // in Vec3d const& a2v, // in Plane const& plane1, // in - Vec3d const& b1v_or_b2v, // in - CalcT& dist_a1_a2, CalcT& dist_a1_i1) // out + Vec3d const& b1v, // in + Vec3d const& b2v, // in + CalcT const& dist_a1_a2, // in + CalcT& dist_a1_i1, // out + bool degen_neq_coords = false) // in { // calculate dist_a1_a2 and dist_a1_i1 - calculate_dists(a1v, a2v, plane1, b1v_or_b2v, dist_a1_a2, dist_a1_i1); + calculate_dist(a1v, a2v, plane1, b1v, dist_a1_i1); // if i1 is close to a1 and b1 or b2 is equal to a1 if (is_endpoint_equal(dist_a1_i1, a1, b1, b2)) @@ -555,6 +591,26 @@ private: return true; } + // check the other endpoint of a very short segment near the pole + if (degen_neq_coords) + { + static CalcT const c0 = 0; + + CalcT dist_a1_i2 = 0; + calculate_dist(a1v, a2v, plane1, b2v, dist_a1_i2); + + if (math::equals(dist_a1_i2, c0)) + { + dist_a1_i1 = 0; + return true; + } + else if (math::equals(dist_a1_a2 - dist_a1_i2, c0)) + { + dist_a1_i1 = dist_a1_a2; + return true; + } + } + // or i1 is on b return segment_ratio(dist_a1_i1, dist_a1_a2).on_segment(); } @@ -568,15 +624,17 @@ private: Plane const& plane2, // in CalcPolicy const& calc_policy, // in side_info const& sides, // in - Vec3d & ip, // out - CalcT& dist_a1_a2, CalcT& dist_a1_ip, // out - CalcT& dist_b1_b2, CalcT& dist_b1_ip, // out - intersection_point_flag& ip_flag) // out + CalcT const& dist_a1_a2, // in + CalcT const& dist_b1_b2, // in + Vec3d & ip, // out + CalcT& dist_a1_ip, // out + CalcT& dist_b1_ip, // out + intersection_point_flag& ip_flag) // out { Vec3d ip1, ip2; calc_policy.intersection_points(plane1, plane2, ip1, ip2); - calculate_dists(a1v, a2v, plane1, ip1, dist_a1_a2, dist_a1_ip); + calculate_dist(a1v, a2v, plane1, ip1, dist_a1_ip); ip = ip1; // choose the opposite side of the globe if the distance is shorter @@ -602,7 +660,7 @@ private: return false; } - calculate_dists(b1v, b2v, plane2, ip, dist_b1_b2, dist_b1_ip); + calculate_dist(b1v, b2v, plane2, ip, dist_b1_ip); bool is_on_b = false, is_near_b1 = false, is_near_b2 = false; if (! is_potentially_crossing(dist_b1_b2, dist_b1_ip, is_on_b, is_near_b1, is_near_b2)) @@ -701,20 +759,26 @@ private: } template - static inline void calculate_dists(Vec3d const& a1v, // in - Vec3d const& a2v, // in - Plane const& plane1, // in - Vec3d const& i1, // in - CalcT& dist_a1_a2, // out - CalcT& dist_a1_i1) // out + static inline void calculate_dist(Vec3d const& a1v, // in + Vec3d const& a2v, // in + Plane const& plane1, // in + CalcT& dist_a1_a2) // out { - //CalcT const c0 = 0; - CalcT const c1 = 1; - CalcT const c2 = 2; - CalcT const c4 = 4; - - CalcT cos_a1_a2 = plane1.cos_angle_between(a1v, a2v); + static CalcT const c1 = 1; + CalcT const cos_a1_a2 = plane1.cos_angle_between(a1v, a2v); dist_a1_a2 = -cos_a1_a2 + c1; // [1, -1] -> [0, 2] representing [0, pi] + } + + template + static inline void calculate_dist(Vec3d const& a1v, // in + Vec3d const& /*a2v*/, // in + Plane const& plane1, // in + Vec3d const& i1, // in + CalcT& dist_a1_i1) // out + { + static CalcT const c1 = 1; + static CalcT const c2 = 2; + static CalcT const c4 = 4; bool is_forward = true; CalcT cos_a1_i1 = plane1.cos_angle_between(a1v, i1, is_forward); @@ -728,7 +792,19 @@ private: dist_a1_i1 += c4; // += 2pi } } - + /* + template + static inline void calculate_dists(Vec3d const& a1v, // in + Vec3d const& a2v, // in + Plane const& plane1, // in + Vec3d const& i1, // in + CalcT& dist_a1_a2, // out + CalcT& dist_a1_i1) // out + { + calculate_dist(a1v, a2v, plane1, dist_a1_a2); + calculate_dist(a1v, a2v, plane1, i1, dist_a1_i1); + } + */ // the dist of the ip on the other side of the sphere template static inline CalcT dist_of_i2(CalcT const& dist_a1_i1) @@ -769,8 +845,9 @@ private: static inline bool is_endpoint_equal(CalcT const& dist, P1 const& ai, P2 const& b1, P2 const& b2) { + static CalcT const c0 = 0; using geometry::detail::equals::equals_point_point; - return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2)); + return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2) || math::equals(dist, c0)); } template diff --git a/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp b/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp new file mode 100644 index 000000000..0f1a901d1 --- /dev/null +++ b/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp @@ -0,0 +1,581 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013, 2014, 2016, 2017. +// Modifications copyright (c) 2013-2017 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. + +// 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_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP +#define BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP + + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace within +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +< + typename Point, + typename PointOfSegment = Point, + typename SideStrategy = typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type, + typename CalculationType = void +> +class spherical_winding_base +{ + typedef typename select_calculation_type + < + Point, + PointOfSegment, + CalculationType + >::type calculation_type; + + typedef typename coordinate_system::type::units units_t; + typedef math::detail::constants_on_spheroid constants; + + /*! subclass to keep state */ + class counter + { + int m_count; + //int m_count_n; + int m_count_s; + int m_raw_count; + int m_raw_count_anti; + bool m_touches; + + inline int code() const + { + if (m_touches) + { + return 0; + } + + if (m_raw_count != 0 && m_raw_count_anti != 0) + { + if (m_raw_count > 0) // right, wrap around south pole + { + return (m_count + m_count_s) == 0 ? -1 : 1; + } + else // left, wrap around north pole + { + //return (m_count + m_count_n) == 0 ? -1 : 1; + // m_count_n is 0 + return m_count == 0 ? -1 : 1; + } + } + + return m_count == 0 ? -1 : 1; + } + + public : + friend class spherical_winding_base; + + inline counter() + : m_count(0) + //, m_count_n(0) + , m_count_s(0) + , m_raw_count(0) + , m_raw_count_anti(0) + , m_touches(false) + {} + + }; + + struct count_info + { + explicit count_info(int c = 0, bool ia = false) + : count(c) + , is_anti(ia) + {} + + int count; + bool is_anti; + }; + +public: + typedef typename SideStrategy::envelope_strategy_type envelope_strategy_type; + + inline envelope_strategy_type get_envelope_strategy() const + { + return m_side_strategy.get_envelope_strategy(); + } + + typedef typename SideStrategy::disjoint_strategy_type disjoint_strategy_type; + + inline disjoint_strategy_type get_disjoint_strategy() const + { + return m_side_strategy.get_disjoint_strategy(); + } + + spherical_winding_base() + {} + + template + explicit spherical_winding_base(Model const& model) + : m_side_strategy(model) + {} + + // Typedefs and static methods to fulfill the concept + typedef Point point_type; + typedef PointOfSegment segment_point_type; + typedef counter state_type; + + inline bool apply(Point const& point, + PointOfSegment const& s1, PointOfSegment const& s2, + counter& state) const + { + bool eq1 = false; + bool eq2 = false; + bool s_antipodal = false; + + count_info ci = check_segment(point, s1, s2, state, eq1, eq2, s_antipodal); + if (ci.count != 0) + { + if (! ci.is_anti) + { + int side = 0; + if (ci.count == 1 || ci.count == -1) + { + side = side_equal(point, eq1 ? s1 : s2, ci, s1, s2); + } + else // count == 2 || count == -2 + { + if (! s_antipodal) + { + // 1 left, -1 right + side = m_side_strategy.apply(s1, s2, point); + } + else + { + calculation_type const pi = constants::half_period(); + calculation_type const s1_lat = get<1>(s1); + calculation_type const s2_lat = get<1>(s2); + + side = math::sign(ci.count) + * (pi - s1_lat - s2_lat <= pi // segment goes through north pole + ? -1 // going right all points will be on right side + : 1); // going right all points will be on left side + } + } + + if (side == 0) + { + // Point is lying on segment + state.m_touches = true; + state.m_count = 0; + return false; + } + + // Side is NEG for right, POS for left. + // The count is -2 for left, 2 for right (or -1/1) + // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE + // See accompagnying figure (TODO) + if (side * ci.count > 0) + { + state.m_count += ci.count; + } + + state.m_raw_count += ci.count; + } + else + { + // Count negated because the segment is on the other side of the globe + // so it is reversed to match this side of the globe + + // Assuming geometry wraps around north pole, for segments on the other side of the globe + // the point will always be RIGHT+RIGHTSIDE or LEFT+LEFTSIDE, so side*-count always < 0 + //state.m_count_n -= 0; + + // Assuming geometry wraps around south pole, for segments on the other side of the globe + // the point will always be RIGHT+LEFTSIDE or LEFT+RIGHTSIDE, so side*-count always > 0 + state.m_count_s -= ci.count; + + state.m_raw_count_anti -= ci.count; + } + } + return ! state.m_touches; + } + + static inline int result(counter const& state) + { + return state.code(); + } + +private: + + static inline count_info check_segment(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, bool& eq2, bool& s_antipodal) + { + if (check_touch(point, seg1, seg2, state, eq1, eq2, s_antipodal)) + { + return count_info(0, false); + } + + return calculate_count(point, seg1, seg2, eq1, eq2, s_antipodal); + } + + static inline int check_touch(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, + bool& eq2, + bool& s_antipodal) + { + calculation_type const c0 = 0; + calculation_type const c2 = 2; + calculation_type const pi = constants::half_period(); + calculation_type const half_pi = pi / c2; + + calculation_type const p_lon = get<0>(point); + calculation_type const s1_lon = get<0>(seg1); + calculation_type const s2_lon = get<0>(seg2); + calculation_type const p_lat = get<1>(point); + calculation_type const s1_lat = get<1>(seg1); + calculation_type const s2_lat = get<1>(seg2); + + // NOTE: lat in {-90, 90} and arbitrary lon + // it doesn't matter what lon it is if it's a pole + // so e.g. if one of the segment endpoints is a pole + // then only the other lon matters + + bool eq1_strict = longitudes_equal(s1_lon, p_lon); + bool eq2_strict = longitudes_equal(s2_lon, p_lon); + bool eq1_anti = false; + bool eq2_anti = false; + + calculation_type const anti_p_lon = p_lon + (p_lon <= c0 ? pi : -pi); + + eq1 = eq1_strict // lon strictly equal to s1 + || (eq1_anti = longitudes_equal(s1_lon, anti_p_lon)) // anti-lon strictly equal to s1 + || math::equals(math::abs(s1_lat), half_pi); // s1 is pole + eq2 = eq2_strict // lon strictly equal to s2 + || (eq2_anti = longitudes_equal(s2_lon, anti_p_lon)) // anti-lon strictly equal to s2 + || math::equals(math::abs(s2_lat), half_pi); // s2 is pole + + // segment overlapping pole + calculation_type const s_lon_diff = math::longitude_distance_signed(s1_lon, s2_lon); + s_antipodal = math::equals(s_lon_diff, pi); + if (s_antipodal) + { + eq1 = eq2 = eq1 || eq2; + + // segment overlapping pole and point is pole + if (math::equals(math::abs(p_lat), half_pi)) + { + eq1 = eq2 = true; + } + } + + // Both equal p -> segment vertical + // The only thing which has to be done is check if point is ON segment + if (eq1 && eq2) + { + // segment endpoints on the same sides of the globe + if (! s_antipodal) + { + // p's lat between segment endpoints' lats + if ( (s1_lat <= p_lat && s2_lat >= p_lat) || (s2_lat <= p_lat && s1_lat >= p_lat) ) + { + if (!eq1_anti || !eq2_anti) + { + state.m_touches = true; + } + } + } + else + { + // going through north or south pole? + if (pi - s1_lat - s2_lat <= pi) + { + if ( (eq1_strict && s1_lat <= p_lat) || (eq2_strict && s2_lat <= p_lat) // north + || math::equals(p_lat, half_pi) ) // point on north pole + { + state.m_touches = true; + } + else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, -half_pi) ) // point on south pole + { + return false; + } + } + else // south pole + { + if ( (eq1_strict && s1_lat >= p_lat) || (eq2_strict && s2_lat >= p_lat) // south + || math::equals(p_lat, -half_pi) ) // point on south pole + { + state.m_touches = true; + } + else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, half_pi) ) // point on north pole + { + return false; + } + } + } + + return true; + } + + return false; + } + + // Called if point is not aligned with a vertical segment + static inline count_info calculate_count(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + bool eq1, bool eq2, bool s_antipodal) + { + // If both segment endpoints were poles below checks wouldn't be enough + // but this means that either both are the same or that they are N/S poles + // and therefore the segment is not valid. + // If needed (eq1 && eq2 ? 0) could be returned + + calculation_type const c0 = 0; + calculation_type const pi = constants::half_period(); + + calculation_type const p = get<0>(point); + calculation_type const s1 = get<0>(seg1); + calculation_type const s2 = get<0>(seg2); + + calculation_type const s1_p = math::longitude_distance_signed(s1, p); + + if (s_antipodal) + { + return count_info(s1_p < c0 ? -2 : 2, false); // choose W/E + } + + calculation_type const s1_s2 = math::longitude_distance_signed(s1, s2); + + if (eq1 || eq2) // Point on level s1 or s2 + { + return count_info(s1_s2 < c0 ? -1 : 1, // choose W/E + longitudes_equal(p + pi, (eq1 ? s1 : s2))); + } + + // Point between s1 and s2 + if ( math::sign(s1_p) == math::sign(s1_s2) + && math::abs(s1_p) < math::abs(s1_s2) ) + { + return count_info(s1_s2 < c0 ? -2 : 2, false); // choose W/E + } + + calculation_type const s1_p_anti = math::longitude_distance_signed(s1, p + pi); + + // Anti-Point between s1 and s2 + if ( math::sign(s1_p_anti) == math::sign(s1_s2) + && math::abs(s1_p_anti) < math::abs(s1_s2) ) + { + return count_info(s1_s2 < c0 ? -2 : 2, true); // choose W/E + } + + return count_info(0, false); + } + + + // Fix for https://svn.boost.org/trac/boost/ticket/9628 + // For floating point coordinates, the coordinate of a point is compared + // with the segment's points using some EPS. If the coordinates are "equal" + // the sides are calculated. Therefore we can treat a segment as a long areal + // geometry having some width. There is a small ~triangular area somewhere + // between the segment's effective area and a segment's line used in sides + // calculation where the segment is on the one side of the line but on the + // other side of a segment (due to the width). + // Below picture assuming D = 1, if D = 0 horiz<->vert, E<->N, RIGHT<->UP. + // For the s1 of a segment going NE the real side is RIGHT but the point may + // be detected as LEFT, like this: + // RIGHT + // ___-----> + // ^ O Pt __ __ + // EPS __ __ + // v__ __ BUT DETECTED AS LEFT OF THIS LINE + // _____7 + // _____/ + // _____/ + // In the code below actually D = 0, so segments are nearly-vertical + // Called when the point is on the same level as one of the segment's points + // but the point is not aligned with a vertical segment + inline int side_equal(Point const& point, + PointOfSegment const& se, + count_info const& ci, + PointOfSegment const& s1, PointOfSegment const& s2) const + { + typedef typename coordinate_type::type scoord_t; + typedef typename coordinate_system::type::units units_t; + + if (math::equals(get<1>(point), get<1>(se))) + { + return 0; + } + + // Create a horizontal segment intersecting the original segment's endpoint + // equal to the point, with the derived direction (E/W). + PointOfSegment ss1, ss2; + set<1>(ss1, get<1>(se)); + set<0>(ss1, get<0>(se)); + set<1>(ss2, get<1>(se)); + scoord_t ss20 = get<0>(se); + if (ci.count > 0) + { + ss20 += small_angle(); + } + else + { + ss20 -= small_angle(); + } + math::normalize_longitude(ss20); + set<0>(ss2, ss20); + + // Check the side using this vertical segment + return m_side_strategy.apply(ss1, ss2, point); + } + + // 1 deg or pi/180 rad + static inline calculation_type small_angle() + { + return constants::half_period() / calculation_type(180); + }; + + static inline bool longitudes_equal(calculation_type const& lon1, calculation_type const& lon2) + { + return math::equals( + math::longitude_distance_signed(lon1, lon2), + calculation_type(0)); + } + + SideStrategy m_side_strategy; +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +/*! +\brief Within detection using winding rule in spherical coordinate system. +\ingroup strategies +\tparam Point \tparam_point +\tparam PointOfSegment \tparam_segment_point +\tparam CalculationType \tparam_calculation + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] +} + */ +template +< + typename Point, + typename PointOfSegment = Point, + typename CalculationType = void +> +class spherical_winding + : public within::detail::spherical_winding_base + < + Point, + PointOfSegment, + side::spherical_side_formula, + CalculationType + > +{}; + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +namespace services +{ + +template +struct default_strategy +{ + typedef within::detail::spherical_winding_base + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +template +struct default_strategy +{ + typedef within::detail::spherical_winding_base + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +} // namespace services + +#endif + + +}} // namespace strategy::within + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace strategy { namespace covered_by { namespace services +{ + +template +struct default_strategy +{ + typedef within::detail::spherical_winding_base + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +template +struct default_strategy +{ + typedef within::detail::spherical_winding_base + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +}}} // namespace strategy::covered_by::services +#endif + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP diff --git a/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp b/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp index 3f7be0555..2b195e96f 100644 --- a/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp +++ b/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp @@ -2,9 +2,10 @@ // 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. +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017, Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -18,7 +19,7 @@ #include #include -#include +#include #include #include @@ -59,8 +60,20 @@ public : >::type calc_t; calc_t d1 = 0.001; // m_strategy.apply(sp1, p); - calc_t crs_AD = geometry::detail::course(p1, p); - calc_t crs_AB = geometry::detail::course(p1, p2); + + calc_t lon1 = geometry::get_as_radian<0>(p1); + calc_t lat1 = geometry::get_as_radian<1>(p1); + calc_t lon2 = geometry::get_as_radian<0>(p2); + calc_t lat2 = geometry::get_as_radian<1>(p2); + calc_t lon = geometry::get_as_radian<0>(p); + calc_t lat = geometry::get_as_radian<1>(p); + + calc_t crs_AD = geometry::formula::spherical_azimuth + (lon1, lat1, lon, lat).azimuth; + + calc_t crs_AB = geometry::formula::spherical_azimuth + (lon1, lat1, lon2, lat2).azimuth; + calc_t XTD = asin(sin(d1) * sin(crs_AD - crs_AB)); return math::equals(XTD, 0) ? 0 : XTD < 0 ? 1 : -1; diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index 27a025c7c..e7f3604ab 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include @@ -73,9 +74,10 @@ #include #include #include -#include +#include #include #include +#include #include #include @@ -83,11 +85,13 @@ #include #include #include +#include #include #include #include #include //#include +#include #include #include #include diff --git a/include/boost/geometry/util/normalize_spheroidal_box_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_box_coordinates.hpp index eb947bb09..288dab20d 100644 --- a/include/boost/geometry/util/normalize_spheroidal_box_coordinates.hpp +++ b/include/boost/geometry/util/normalize_spheroidal_box_coordinates.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, 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 // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -26,7 +27,7 @@ namespace detail { -template +template class normalize_spheroidal_box_coordinates { private: @@ -50,6 +51,9 @@ public: normalize::apply(longitude1, latitude1, false); normalize::apply(longitude2, latitude2, false); + latitude_convert_if_polar::apply(latitude1); + latitude_convert_if_polar::apply(latitude2); + if (math::equals(latitude1, constants::min_latitude()) && math::equals(latitude2, constants::min_latitude())) { @@ -76,6 +80,9 @@ public: longitude2 += constants::period(); } + latitude_convert_if_polar::apply(latitude1); + latitude_convert_if_polar::apply(latitude2); + #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE BOOST_GEOMETRY_ASSERT(! math::larger(latitude1, latitude2)); BOOST_GEOMETRY_ASSERT(! math::smaller(latitude1, constants::min_latitude())); @@ -126,6 +133,18 @@ inline void normalize_spheroidal_box_coordinates(CoordinateType& longitude1, >::apply(longitude1, latitude1, longitude2, latitude2); } +template +inline void normalize_spheroidal_box_coordinates(CoordinateType& longitude1, + CoordinateType& latitude1, + CoordinateType& longitude2, + CoordinateType& latitude2) +{ + detail::normalize_spheroidal_box_coordinates + < + Units, CoordinateType, IsEquatorial + >::apply(longitude1, latitude1, longitude2, latitude2); +} + /*! \brief Short utility to normalize the coordinates of a box on a spheroid \tparam Units The units of the coordindate system in the spheroid @@ -151,6 +170,19 @@ inline void normalize_spheroidal_box_coordinates(CoordinateType& longitude1, >::apply(longitude1, latitude1, longitude2, latitude2, band); } +template +inline void normalize_spheroidal_box_coordinates(CoordinateType& longitude1, + CoordinateType& latitude1, + CoordinateType& longitude2, + CoordinateType& latitude2, + bool band) +{ + detail::normalize_spheroidal_box_coordinates + < + Units, CoordinateType, IsEquatorial + >::apply(longitude1, latitude1, longitude2, latitude2, band); +} + } // namespace math diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp index 19d4d33d2..eaa686ed1 100644 --- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp +++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015-2016, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, 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 @@ -26,8 +26,8 @@ namespace math namespace detail { - -template +// CoordinateType, radian, true +template struct constants_on_spheroid { static inline CoordinateType period() @@ -40,6 +40,13 @@ struct constants_on_spheroid return math::pi(); } + static inline CoordinateType quarter_period() + { + static CoordinateType const + pi_half = math::pi() / CoordinateType(2); + return pi_half; + } + static inline CoordinateType min_longitude() { static CoordinateType const minus_pi = -math::pi(); @@ -65,7 +72,22 @@ struct constants_on_spheroid }; template -struct constants_on_spheroid +struct constants_on_spheroid + : constants_on_spheroid +{ + static inline CoordinateType min_latitude() + { + return CoordinateType(0); + } + + static inline CoordinateType max_latitude() + { + return math::pi(); + } +}; + +template +struct constants_on_spheroid { static inline CoordinateType period() { @@ -77,6 +99,11 @@ struct constants_on_spheroid return CoordinateType(180.0); } + static inline CoordinateType quarter_period() + { + return CoordinateType(90.0); + } + static inline CoordinateType min_longitude() { return CoordinateType(-180.0); @@ -98,8 +125,94 @@ struct constants_on_spheroid } }; +template +struct constants_on_spheroid + : constants_on_spheroid +{ + static inline CoordinateType min_latitude() + { + return CoordinateType(0); + } + + static inline CoordinateType max_latitude() + { + return CoordinateType(180.0); + } +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + template +inline CoordinateType latitude_convert_ep(CoordinateType const& lat) +{ + typedef math::detail::constants_on_spheroid + < + CoordinateType, + Units + > constants; + + return constants::quarter_period() - lat; +} + + +template +static bool is_latitude_pole(T const& lat) +{ + typedef math::detail::constants_on_spheroid + < + T, + Units + > constants; + + return math::equals(math::abs(IsEquatorial + ? lat + : math::latitude_convert_ep(lat)), + constants::quarter_period()); + +} + + +template +static bool is_longitude_antimeridian(T const& lon) +{ + typedef math::detail::constants_on_spheroid + < + T, + Units + > constants; + + return math::equals(math::abs(lon), constants::half_period()); + +} + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + +template +struct latitude_convert_if_polar +{ + template + static inline void apply(T & lat) {} +}; + +template +struct latitude_convert_if_polar +{ + template + static inline void apply(T & lat) + { + lat = latitude_convert_ep(lat); + } +}; + + +template class normalize_spheroidal_coordinates { typedef constants_on_spheroid constants; @@ -145,6 +258,8 @@ public: CoordinateType& latitude, bool normalize_poles = true) { + latitude_convert_if_polar::apply(latitude); + #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE // normalize latitude if (math::larger(latitude, constants::half_period())) @@ -183,6 +298,8 @@ public: } } + latitude_convert_if_polar::apply(latitude); + #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE BOOST_GEOMETRY_ASSERT(! math::larger(constants::min_latitude(), latitude)); BOOST_GEOMETRY_ASSERT(! math::larger(latitude, constants::max_latitude())); @@ -216,6 +333,15 @@ inline void normalize_spheroidal_coordinates(CoordinateType& longitude, >::apply(longitude, latitude); } +template +inline void normalize_spheroidal_coordinates(CoordinateType& longitude, + CoordinateType& latitude) +{ + detail::normalize_spheroidal_coordinates + < + Units, CoordinateType, IsEquatorial + >::apply(longitude, latitude); +} /*! \brief Short utility to normalize the longitude on a spheroid. @@ -316,6 +442,7 @@ inline CoordinateType longitude_interval_distance_signed(CoordinateType const& l : c0; } + } // namespace math diff --git a/test/algorithms/Jamfile.v2 b/test/algorithms/Jamfile.v2 index cb01090ef..2806c1a10 100644 --- a/test/algorithms/Jamfile.v2 +++ b/test/algorithms/Jamfile.v2 @@ -4,7 +4,7 @@ # Copyright (c) 2008-2015 Bruno Lalande, Paris, France. # Copyright (c) 2009-2015 Mateusz Loskot, London, UK. # -# This file was modified by Oracle on 2014, 2015, 2016,2017. +# This file was modified by Oracle on 2014, 2015, 2016, 2017. # Modifications copyright (c) 2014-2017, Oracle and/or its affiliates. # # Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle @@ -28,13 +28,16 @@ test-suite boost-geometry-algorithms [ run convex_hull_multi.cpp : : : : algorithms_convex_hull_multi ] [ run correct.cpp : : : : algorithms_correct ] [ run correct_multi.cpp : : : : algorithms_correct_multi ] + [ run correct_closure.cpp : : : : algorithms_correct_closure ] [ run for_each.cpp : : : : algorithms_for_each ] [ run for_each_multi.cpp : : : : algorithms_for_each_multi ] [ run is_convex.cpp : : : : algorithms_is_convex ] [ run is_empty.cpp : : : : algorithms_is_empty ] [ run is_simple.cpp : : : : algorithms_is_simple ] + [ run is_simple_geo.cpp : : : : algorithms_is_simple_geo ] [ run is_valid.cpp : : : : algorithms_is_valid ] [ run is_valid_failure.cpp : : : : algorithms_is_valid_failure ] + [ run is_valid_geo.cpp : : : : algorithms_is_valid_geo ] [ run make.cpp : : : : algorithms_make ] [ run maximum_gap.cpp : : : : algorithms_maximum_gap ] [ run num_geometries.cpp : : : : algorithms_num_geometries ] diff --git a/test/algorithms/area/area_sph_geo.cpp b/test/algorithms/area/area_sph_geo.cpp index 70185c1d8..9cde3b45a 100644 --- a/test/algorithms/area/area_sph_geo.cpp +++ b/test/algorithms/area/area_sph_geo.cpp @@ -389,6 +389,21 @@ void test_spherical_geo() // for select geography::STGeomFromText('POLYGON((4.892 52.373,4.23 52.08, // 4.479 51.930,5.119 52.093,4.892 52.373))',4326).STArea()/1000000.0 } + + { + bg::model::polygon geometry_sph; + std::string wkt = "POLYGON((0 0, 5 0, 5 5, 0 5, 0 0))"; + bg::read_wkt(wkt, geometry_sph); + + area = bg::area(geometry_sph, bg::strategy::area::spherical(6371228.0)); + BOOST_CHECK_CLOSE(area, 308932296103.83051, 0.0001); + + bg::model::polygon geometry_geo; + bg::read_wkt(wkt, geometry_geo); + + area = bg::area(geometry_geo, bg::strategy::area::geographic(bg::srs::spheroid(6371228.0, 6371228.0))); + BOOST_CHECK_CLOSE(area, 308932296103.82574, 0.001); + } } int test_main(int, char* []) diff --git a/test/algorithms/buffer/buffer_multi_point.cpp b/test/algorithms/buffer/buffer_multi_point.cpp index fb3b61fb6..3d0e94a07 100644 --- a/test/algorithms/buffer/buffer_multi_point.cpp +++ b/test/algorithms/buffer/buffer_multi_point.cpp @@ -24,6 +24,8 @@ static std::string const grid_a = "MULTIPOINT(5 0,6 0,7 0, 5 1,7 1, 0 13,8 13) static std::string const mysql_report_2015_02_25_1 = "MULTIPOINT(-9 19,9 -6,-4 4,16 -14,-3 16,14 9)"; static std::string const mysql_report_2015_02_25_2 = "MULTIPOINT(-2 11,-15 3,6 4,-14 0,20 -7,-17 -1)"; +static std::string const mysql_report_3 = "MULTIPOINT(0 0,0 0,0 0,0 0,0 0)"; + template void test_all() { @@ -78,6 +80,18 @@ void test_all() mysql_report_2015_02_25_1, join, end_flat, distance_strategy(6051788), side_strategy, bg::strategy::buffer::point_circle(800), 115057490003226.125, 1.0); + + { + multi_point_type g; + bg::read_wkt(mysql_report_3, g); + test_buffer("mysql_report_3", g, + bg::strategy::buffer::join_round(36), + bg::strategy::buffer::end_round(36), + distance_strategy(1), + side_strategy, + bg::strategy::buffer::point_circle(36), + true, 1, 0, 3.12566719800474635, 1.0, NULL); + } } template diff --git a/test/algorithms/buffer/test_buffer_svg.hpp b/test/algorithms/buffer/test_buffer_svg.hpp index 428a18714..67570e462 100644 --- a/test/algorithms/buffer/test_buffer_svg.hpp +++ b/test/algorithms/buffer/test_buffer_svg.hpp @@ -25,6 +25,7 @@ #include #include +#include inline char piece_type_char(bg::strategy::buffer::piece_type const& type) diff --git a/test/algorithms/correct.cpp b/test/algorithms/correct.cpp index 59152601a..154117634 100644 --- a/test/algorithms/correct.cpp +++ b/test/algorithms/correct.cpp @@ -136,8 +136,9 @@ void test_ring_polygon() template void test_box() { - // Boxes - std::string proper_box = "POLYGON((0 0,0 2,2 2,2 0,0 0))"; + // Boxes. Reference is an open box (because in this test WKT is not + // explicitly closed) + std::string proper_box = "POLYGON((0 0,0 2,2 2,2 0))"; test_geometry >(proper_box, proper_box); test_geometry >("BOX(0 0,2 2)", proper_box); test_geometry >("BOX(2 2,0 0)", proper_box); diff --git a/test/algorithms/correct_closure.cpp b/test/algorithms/correct_closure.cpp new file mode 100644 index 000000000..6dec6970d --- /dev/null +++ b/test/algorithms/correct_closure.cpp @@ -0,0 +1,114 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2017 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include + + +template +void check_geometry(Geometry const& geometry, std::string const& expected) +{ + std::ostringstream out; + out << bg::wkt_manipulator(geometry, false); + BOOST_CHECK_EQUAL(out.str(), expected); +} + +template +void test_geometry(std::string const& wkt, std::string const& expected) +{ + Geometry geometry; + bg::read_wkt(wkt, geometry); + + // Test tye type + bg::correct_closure(geometry); + check_geometry(geometry, expected); + + // Test varianted type + boost::variant v(geometry); + bg::correct_closure(v); + check_geometry(v, expected); +} + +template +void test_all() +{ + typedef bg::model::ring cw_closed_ring_type; + typedef bg::model::ring cw_open_ring_type; + typedef bg::model::ring ccw_closed_ring_type; + typedef bg::model::ring ccw_open_ring_type; + + // Define clockwise and counter clockwise polygon + std::string cw_ring = "POLYGON((0 0,0 1,1 1,1 0,0 0))"; + std::string cw_open_ring = "POLYGON((0 0,0 1,1 1,1 0))"; + + std::string ccw_ring = "POLYGON((0 0,1 0,1 1,0 1,0 0))"; + std::string ccw_open_ring = "POLYGON((0 0,1 0,1 1,0 1))"; + + // Cases which should be closed or opened + test_geometry(cw_open_ring, cw_ring); + test_geometry(cw_ring, cw_open_ring); + test_geometry(ccw_open_ring, ccw_ring); + test_geometry(ccw_ring, ccw_open_ring); + + // Cases which are incorrect but should still be closed or opened + test_geometry(ccw_open_ring, ccw_ring); + test_geometry(cw_ring, cw_open_ring); + + // Cases where no action is necessary (even if order is incorrect) + test_geometry(cw_ring, cw_ring); + test_geometry(ccw_ring, ccw_ring); + test_geometry(cw_open_ring, cw_open_ring); + test_geometry(ccw_open_ring, ccw_open_ring); + test_geometry(cw_ring, cw_ring); + test_geometry(ccw_ring, ccw_ring); + test_geometry(cw_open_ring, cw_open_ring); + test_geometry(ccw_open_ring, ccw_open_ring); + + // Polygon cases + std::string cw_polygon = + "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,2 1,2 2,1 2,1 1))"; + + std::string cw_open_polygon = + "POLYGON((0 0,0 4,4 4,4 0),(1 1,2 1,2 2,1 2))"; + + typedef bg::model::polygon cw_closed_polygon_type; + typedef bg::model::polygon cw_open_polygon_type; + + test_geometry(cw_open_polygon, cw_polygon); + test_geometry(cw_polygon, cw_open_polygon); + + test_geometry(cw_polygon, cw_polygon); + test_geometry(cw_open_polygon, cw_open_polygon); +} + + +int test_main(int, char* []) +{ + test_all >(); + test_all >(); + test_all >(); + + test_all > >(); + test_all > >(); + + return 0; +} diff --git a/test/algorithms/distance/Jamfile.v2 b/test/algorithms/distance/Jamfile.v2 index 656555a57..e19750a66 100644 --- a/test/algorithms/distance/Jamfile.v2 +++ b/test/algorithms/distance/Jamfile.v2 @@ -26,4 +26,5 @@ test-suite boost-geometry-algorithms-distance [ run distance_se_pl_l.cpp : : : : algorithms_distance_se_pl_l ] [ run distance_se_pl_pl.cpp : : : : algorithms_distance_se_pl_pl ] [ run distance_se_point_box.cpp : : : : algorithms_distance_se_point_box ] + [ run distance_geo_pl_l.cpp : : : : algorithms_distance_geo_pl_l ] ; diff --git a/test/algorithms/distance/distance_brute_force.hpp b/test/algorithms/distance/distance_brute_force.hpp index b62c09105..7992ffcae 100644 --- a/test/algorithms/distance/distance_brute_force.hpp +++ b/test/algorithms/distance/distance_brute_force.hpp @@ -170,7 +170,7 @@ struct distance_brute_force }; -template +template < typename Point1, typename Point2, @@ -184,7 +184,7 @@ struct distance_brute_force {}; -template +template < typename Point, typename Segment, @@ -198,7 +198,7 @@ struct distance_brute_force {}; -template +template < typename Point, typename Box, @@ -226,7 +226,7 @@ struct distance_brute_force {}; -template +template < typename Point, typename Linear, @@ -258,7 +258,7 @@ struct distance_brute_force }; -template +template < typename Point, typename MultiPoint, @@ -286,7 +286,7 @@ struct distance_brute_force } }; -template +template < typename MultiPoint1, typename MultiPoint2, @@ -320,7 +320,7 @@ struct distance_brute_force }; -template +template < typename MultiPoint, typename Linear, @@ -354,7 +354,7 @@ struct distance_brute_force }; -template +template < typename Linear, typename MultiPoint, @@ -384,7 +384,7 @@ struct distance_brute_force }; -template +template < typename MultiPoint, typename Segment, @@ -413,7 +413,7 @@ struct distance_brute_force }; -template +template < typename Linear, typename Segment, @@ -445,7 +445,7 @@ struct distance_brute_force }; -template +template < typename Linear1, typename Linear2, diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp new file mode 100644 index 000000000..6da51ca55 --- /dev/null +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -0,0 +1,645 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2016-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#include + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_distance_geographic_pl_l +#endif + +#include +#include + +#include "test_distance_geo_common.hpp" + +typedef bg::cs::geographic cs_type; +typedef bg::model::point point_type; +typedef bg::model::segment segment_type; +typedef bg::model::multi_point multi_point_type; +typedef bg::model::segment segment_type; +typedef bg::model::linestring linestring_type; +typedef bg::model::multi_linestring multi_linestring_type; + +namespace services = bg::strategy::distance::services; +typedef bg::default_distance_result::type return_type; + +typedef bg::srs::spheroid stype; + +// Strategies for point-point distance + +typedef bg::strategy::distance::andoyer andoyer_pp; +typedef bg::strategy::distance::thomas thomas_pp; +typedef bg::strategy::distance::vincenty vincenty_pp; + +// Strategies for point-segment distance + +typedef bg::strategy::distance::geographic_cross_track + andoyer_strategy; + +typedef bg::strategy::distance::geographic_cross_track + thomas_strategy; + +typedef bg::strategy::distance::geographic_cross_track + vincenty_strategy; + +//=========================================================================== + +template +inline bg::default_distance_result::type +pp_distance(std::string const& wkt1, + std::string const& wkt2, + Strategy const& strategy) +{ + point_type p1, p2; + bg::read_wkt(wkt1, p1); + bg::read_wkt(wkt2, p2); + return bg::distance(p1, p2, strategy); +} + +template +inline bg::default_distance_result::type +ps_distance(std::string const& wkt1, + std::string const& wkt2, + Strategy const& strategy) +{ + point_type p; + segment_type s; + bg::read_wkt(wkt1, p); + bg::read_wkt(wkt2, s); + return bg::distance(p, s, strategy); +} + +//=========================================================================== + +template +void test_distance_point_segment(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "point/segment distance tests" << std::endl; +#endif + typedef test_distance_of_geometries tester; + + tester::apply("p-s-01", + "POINT(0 0)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(0 0)", "POINT(2 0)", strategy_pp), + strategy_ps); + tester::apply("p-s-02", + "POINT(2.5 3)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(2.5 3)", "POINT(2.49777 0)", strategy_pp), + strategy_ps); + tester::apply("p-s-03", + "POINT(2 0)", + "SEGMENT(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-s-04", + "POINT(3 0)", + "SEGMENT(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-s-05", + "POINT(2.5 0)", + "SEGMENT(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-s-06", + "POINT(3.5 3)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(3 0)", "POINT(3.5 3)", strategy_pp), + strategy_ps); + tester::apply("p-s-07", + "POINT(15 80)", + "SEGMENT(10 15,30 15)", + 7204174.8264546748, + strategy_ps); + tester::apply("p-s-08", + "POINT(15 10)", + "SEGMENT(10 15,30 15)", + 571412.71247283253, + strategy_ps); + tester::apply("p-s-09", + "POINT(5 10)", + "SEGMENT(10 15,30 15)", + pp_distance("POINT(5 10)", "POINT(10 15)", strategy_pp), + strategy_ps); + tester::apply("p-s-10", + "POINT(35 10)", + "SEGMENT(10 15,30 15)", + pp_distance("POINT(35 10)", "POINT(30 15)", strategy_pp), + strategy_ps); + tester::apply("p-s-11", + "POINT(5 10)", + "SEGMENT(30 15,10 15)", + pp_distance("POINT(5 10)", "POINT(10 15)", strategy_pp), + strategy_ps); + tester::apply("p-s-12", + "POINT(35 10)", + "SEGMENT(30 15,10 15)", + pp_distance("POINT(35 10)", "POINT(30 15)", strategy_pp), + strategy_ps); + + tester::apply("p-s-right-up", + "POINT(3.5 3)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(3 2)", "POINT(3.5 3)", strategy_pp), + strategy_ps); + + tester::apply("p-s-left-up", + "POINT(1.5 3)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2 2)", "POINT(1.5 3)", strategy_pp), + strategy_ps); + + tester::apply("p-s-up-1", + "POINT(2 3)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2.0003 2)", "POINT(2 3)", strategy_pp), + strategy_ps); + + tester::apply("p-s-up-2", + "POINT(3 3)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2.9997 2)", "POINT(3 3)", strategy_pp), + strategy_ps); + + tester::apply("p-s-right-down", + "POINT(3.5 1)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(3 2)", "POINT(3.5 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-left-down", + "POINT(1.5 1)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2 2)", "POINT(1.5 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-down-1", + "POINT(2 1)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2.0003 2)", "POINT(2 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-down-2", + "POINT(3 1)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2.9997 2)", "POINT(3 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-south", + "POINT(3 -1)", + "SEGMENT(2 -2,3 -2)", + pp_distance("POINT(2.9997 -2)", "POINT(3 -1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-antimeridian-1", + "POINT(3 1)", + "SEGMENT(220 2,3 2)", + pp_distance("POINT(3 2)", "POINT(3 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-antimeridian-2", + "POINT(220 1)", + "SEGMENT(220 2,3 2)", + pp_distance("POINT(220 2)", "POINT(220 1)", strategy_pp), + strategy_ps); + + // equator special case + tester::apply("p-s-eq1", + "POINT(2.5 0)", + "SEGMENT(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-s-eq2", + "POINT(2.5 2)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(2.5 0)", "POINT(2.5 2)", strategy_pp), + strategy_ps); + tester::apply("p-s-eq3", + "POINT(2 2)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(2 0)", "POINT(2 2)", strategy_pp), + strategy_ps); + tester::apply("p-s-eq4", + "POINT(3 2)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(3 0)", "POINT(3 2)", strategy_pp), + strategy_ps); + + // meridian special case + tester::apply("p-s-mer1", + "POINT(2.5 2)", + "SEGMENT(2 2,2 4)", + pp_distance("POINT(2.5 2)", "POINT(2 2)", strategy_pp), + strategy_ps); + tester::apply("p-s-mer2", + "POINT(2.5 3)", + "SEGMENT(2 2,2 4)", + pp_distance("POINT(2.5 3)", "POINT(2 3.000114792872075)", andoyer_pp()), + andoyer_strategy()); + tester::apply("p-s-mer3", + "POINT(2.5 5)", + "SEGMENT(2 2,2 4)", + pp_distance("POINT(2.5 5)", "POINT(2 4)", strategy_pp), + strategy_ps); + tester::apply("p-s-mer4", + "POINT(1 80)", + "SEGMENT(0 0,0 90)", + pp_distance("POINT(1 80)", "POINT(0 80.00149225834545)", andoyer_pp()), + andoyer_strategy()); + + //degenerate segment + tester::apply("p-s-deg", + "POINT(1 80)", + "SEGMENT(0 0,0 0)", + pp_distance("POINT(0 0)", "POINT(1 80)", strategy_pp), + strategy_ps); + + //point on segment + tester::apply("p-s-deg", + "POINT(0 80)", + "SEGMENT(0 0,0 90)", + 0, + strategy_ps); + + // very small distances to segment + tester::apply("p-s-07", + "POINT(90 1e-3)", + "SEGMENT(0.5 0,175.5 0)", + pp_distance("POINT(90 0)", "POINT(90 1e-3)", strategy_pp), + strategy_ps); + tester::apply("p-s-08", + "POINT(90 1e-4)", + "SEGMENT(0.5 0,175.5 0)", + pp_distance("POINT(90 0)", "POINT(90 1e-4)", strategy_pp), + strategy_ps); + tester::apply("p-s-09", + "POINT(90 1e-5)", + "SEGMENT(0.5 0,175.5 0)", + pp_distance("POINT(90 0)", "POINT(90 1e-5)", strategy_pp), + strategy_ps); + tester::apply("p-s-10", + "POINT(90 1e-6)", + "SEGMENT(0.5 0,175.5 0)", + pp_distance("POINT(90 0)", "POINT(90 1e-6)", strategy_pp), + strategy_ps); + tester::apply("p-s-11", + "POINT(90 1e-7)", + "SEGMENT(0.5 0,175.5 0)", + pp_distance("POINT(90 0)", "POINT(90 1e-7)", strategy_pp), + strategy_ps); + tester::apply("p-s-12", + "POINT(90 1e-8)", + "SEGMENT(0.5 0,175.5 0)", + pp_distance("POINT(90 0)", "POINT(90 1e-8)", strategy_pp), + strategy_ps); + + // very large distance to segment + tester::apply("p-s-13", + "POINT(90 90)", + "SEGMENT(0.5 0,175.5 0)", + pp_distance("POINT(90 0)", "POINT(90 90)", strategy_pp), + strategy_ps); + tester::apply("p-s-14", + "POINT(90 90)", + "SEGMENT(0.5 -89,175.5 -89)", + pp_distance("POINT(90 -89)", "POINT(90 90)", strategy_pp), + strategy_ps); + // degenerate segment + tester::apply("p-s-14", + "POINT(90 90)", + "SEGMENT(0.5 -90,175.5 -90)", + pp_distance("POINT(90 -90)", "POINT(90 90)", strategy_pp), + strategy_ps); + + +} + +//=========================================================================== + +template +void test_distance_point_linestring(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "point/linestring distance tests" << std::endl; +#endif + typedef test_distance_of_geometries tester; + + tester::apply("p-l-01", + "POINT(0 0)", + "LINESTRING(2 0,2 0)", + pp_distance("POINT(0 0)", "POINT(2 0)", strategy_pp), + strategy_ps); + tester::apply("p-l-02", + "POINT(0 0)", + "LINESTRING(2 0,3 0)", + pp_distance("POINT(0 0)", "POINT(2 0)", strategy_pp), + strategy_ps); + tester::apply("p-l-03", + "POINT(2.5 3)", + "LINESTRING(2 0,3 0)", + pp_distance("POINT(2.5 3)", "POINT(2.5 0)", strategy_pp), + strategy_ps); + tester::apply("p-l-04", + "POINT(2 0)", + "LINESTRING(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-l-05", + "POINT(3 0)", + "LINESTRING(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-l-06", + "POINT(2.5 0)", + "LINESTRING(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-l-07", + "POINT(7.5 10)", + "LINESTRING(1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9 0)", + ps_distance("POINT(7.5 10)", "SEGMENT(7 0,8 0)", strategy_ps), + strategy_ps); + tester::apply("p-l-08", + "POINT(7.5 10)", + "LINESTRING(1 1,2 1,3 1,4 1,5 1,6 1,7 1,20 2,21 2)", + ps_distance("POINT(7.5 10)", "SEGMENT(7 1,20 2)", strategy_ps), + strategy_ps); +} + +void test_distance_point_linestring_strategies() +{ + typedef test_distance_of_geometries tester; + + tester::apply("p-l-03", + "POINT(2.5 3)", + "LINESTRING(2 1,3 1)", + 221147.24332788656, + vincenty_strategy()); + + tester::apply("p-l-03", + "POINT(2.5 3)", + "LINESTRING(2 1,3 1)", + 221147.36682199029, + thomas_strategy()); + + tester::apply("p-l-03", + "POINT(2.5 3)", + "LINESTRING(2 1,3 1)", + 221144.76527049288, + andoyer_strategy()); +} + +//=========================================================================== + +template +void test_distance_point_multilinestring(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "point/multilinestring distance tests" << std::endl; +#endif + typedef test_distance_of_geometries + < + point_type, multi_linestring_type + > tester; + + tester::apply("p-ml-01", + "POINT(0 0)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + pp_distance("POINT(0 0)", "POINT(2 0)", strategy_pp), + strategy_ps); + tester::apply("p-ml-02", + "POINT(2.5 3)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + pp_distance("POINT(2.5 3)", "POINT(2.5 0)", strategy_pp), + strategy_ps); + tester::apply("p-ml-03", + "POINT(2 0)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + 0, + strategy_ps); + tester::apply("p-ml-04", + "POINT(3 0)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + 0, + strategy_ps); + tester::apply("p-ml-05", + "POINT(2.5 0)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + 0, + strategy_ps); + tester::apply("p-ml-06", + "POINT(7.5 10)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0,4 0,5 0,6 0,20 1,21 1))", + ps_distance("POINT(7.5 10)", "SEGMENT(6 0,20 1)", strategy_ps), + strategy_ps); + tester::apply("p-ml-07", + "POINT(-8 10)", + "MULTILINESTRING((-20 10,-19 11,-18 10,-6 0,-5 0,-3 0),(2 0,6 0,20 1,21 1))", + ps_distance("POINT(-8 10)", "SEGMENT(-6 0,-18 10)", strategy_ps), + strategy_ps); +} + +//=========================================================================== + +template +void test_distance_linestring_multipoint(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "linestring/multipoint distance tests" << std::endl; +#endif + typedef test_distance_of_geometries + < + linestring_type, multi_point_type + > tester; + + tester::apply("l-mp-01", + "LINESTRING(2 0,0 2,100 80)", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + ps_distance("POINT(1 1)", "SEGMENT(2 0,0 2)", strategy_ps), + strategy_ps); + + tester::apply("l-mp-02", + "LINESTRING(4 0,0 4,100 80)", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + ps_distance("POINT(1 1)", "SEGMENT(0 4,4 0)", strategy_ps), + strategy_ps); + tester::apply("l-mp-03", + "LINESTRING(1 1,2 2,100 80)", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + 0, + strategy_ps); + tester::apply("l-mp-04", + "LINESTRING(3 3,4 4,100 80)", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + pp_distance("POINT(1 1)", "POINT(3 3)", strategy_pp), + strategy_ps); + tester::apply("l-mp-05", + "LINESTRING(0 0,10 0,10 10,0 10,0 0)", + "MULTIPOINT(1 -1,80 80,5 0,150 90)", + 0, + strategy_ps); +} + +//=========================================================================== +template +void test_distance_multipoint_multilinestring(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "multipoint/multilinestring distance tests" << std::endl; +#endif + typedef test_distance_of_geometries + < + multi_point_type, multi_linestring_type + > tester; + + tester::apply("mp-ml-01", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "MULTILINESTRING((2 0,0 2),(2 2,3 3))", + ps_distance("POINT(1 1)", "SEGMENT(2 0,0 2)", strategy_ps), + strategy_ps); + tester::apply("mp-ml-02", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "MULTILINESTRING((3 0,0 3),(4 4,5 5))", + ps_distance("POINT(1 1)", "SEGMENT(3 0,0 3)", strategy_ps), + strategy_ps); + tester::apply("mp-ml-03", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "MULTILINESTRING((4 4,5 5),(1 1,2 2))", + 0, + strategy_ps); + tester::apply("mp-ml-04", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "MULTILINESTRING((4 4,3 3),(4 4,5 5))", + pp_distance("POINT(1 1)", "POINT(3 3)", strategy_pp), + strategy_ps); +} + +//=========================================================================== + +template +void test_distance_multipoint_segment(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "multipoint/segment distance tests" << std::endl; +#endif + typedef test_distance_of_geometries tester; + + tester::apply("mp-s-01", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(2 0,0 2)", + ps_distance("POINT(1 1)", "SEGMENT(2 0,0 2)", strategy_ps), + strategy_ps); + tester::apply("mp-s-02", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(0 -3,1 -10)", + pp_distance("POINT(0 0)", "POINT(0 -3)", strategy_pp), + strategy_ps); + tester::apply("mp-s-03", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(1 1,2 2)", + 0, + strategy_ps); + tester::apply("mp-s-04", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(3 3,4 4)", + pp_distance("POINT(1 1)", "POINT(3 3)", strategy_pp), + strategy_ps); + tester::apply("mp-s-05", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(0.5 -3,1 -10)", + pp_distance("POINT(1 0)", "POINT(0.5 -3)", strategy_pp), + strategy_ps); +} + +//=========================================================================== + +template +void test_empty_input_pointlike_linear(Strategy const& strategy) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "testing on empty inputs... " << std::flush; +#endif + bg::model::linestring line_empty; + bg::model::multi_point multipoint_empty; + bg::model::multi_linestring > multiline_empty; + + Point point = from_wkt("POINT(0 0)"); + bg::model::linestring line = + from_wkt >("LINESTRING(0 0,1 1)"); + + // 1st geometry is empty + //test_empty_input(multipoint_empty, line, strategy); + + // 2nd geometry is empty + test_empty_input(point, line_empty, strategy); + test_empty_input(point, multiline_empty, strategy); + + // both geometries are empty + //test_empty_input(multipoint_empty, line_empty, strategy); + //test_empty_input(multipoint_empty, multiline_empty, strategy); + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "done!" << std::endl; +#endif +} + +//=========================================================================== +//=========================================================================== +//=========================================================================== + +BOOST_AUTO_TEST_CASE( test_all_point_segment ) +{ + //TODO: Operations with multipoints need geographic pt-box strategy + //before activating + + test_distance_point_segment(vincenty_pp(), vincenty_strategy()); + test_distance_point_segment(thomas_pp(), thomas_strategy()); + test_distance_point_segment(andoyer_pp(), andoyer_strategy()); + + test_distance_point_linestring(vincenty_pp(), vincenty_strategy()); + test_distance_point_linestring(thomas_pp(), thomas_strategy()); + test_distance_point_linestring(andoyer_pp(), andoyer_strategy()); + test_distance_point_linestring_strategies(); + + test_distance_point_multilinestring(vincenty_pp(), vincenty_strategy()); + test_distance_point_multilinestring(thomas_pp(), thomas_strategy()); + test_distance_point_multilinestring(andoyer_pp(), andoyer_strategy()); + + // test_distance_linestring_multipoint(vincenty_pp(), vincenty_strategy()); + // test_distance_linestring_multipoint(thomas_pp(), thomas_strategy()); + // test_distance_linestring_multipoint(andoyer_pp(), andoyer_strategy()); + + // test_distance_multipoint_multilinestring(vincenty_pp(), vincenty_strategy()); + // test_distance_multipoint_multilinestring(thomas_pp(), thomas_strategy()); + // test_distance_multipoint_multilinestring(andoyer_pp(), andoyer_strategy()); + + // test_distance_multipoint_segment(vincenty_pp(), vincenty_strategy()); + // test_distance_multipoint_segment(thomas_pp(), thomas_strategy()); + // test_distance_multipoint_segment(andoyer_pp(), andoyer_strategy()); + + test_empty_input_pointlike_linear(vincenty_strategy()); +} diff --git a/test/algorithms/distance/distance_se_pl_l.cpp b/test/algorithms/distance/distance_se_pl_l.cpp index f298bae25..5b3dfd5a9 100644 --- a/test/algorithms/distance/distance_se_pl_l.cpp +++ b/test/algorithms/distance/distance_se_pl_l.cpp @@ -10,6 +10,7 @@ // http://www.boost.org/users/license.html #include +#define BOOST_GEOMETRY_TEST_DEBUG #ifndef BOOST_TEST_MODULE #define BOOST_TEST_MODULE test_distance_spherical_equatorial_pl_l @@ -160,6 +161,14 @@ void test_distance_point_segment(Strategy const& strategy) "POINT(3.5 3)", strategy), strategy); + tester::apply("p-s-07", + "POINT(0 0)", + "SEGMENT(0 10,10 10)", + ps_distance("POINT(0 0)", "SEGMENT(10 10,0 10)", strategy), + pp_comparable_distance("POINT(0 0)", + "POINT(0 10)", + strategy), + strategy); // very small distances to segment tester::apply("p-s-07", "POINT(90 1e-3)", diff --git a/test/algorithms/distance/test_distance_geo_common.hpp b/test/algorithms/distance/test_distance_geo_common.hpp new file mode 100644 index 000000000..f1a06e0a2 --- /dev/null +++ b/test/algorithms/distance/test_distance_geo_common.hpp @@ -0,0 +1,277 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2016-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_TEST_DISTANCE_GEO_COMMON_HPP +#define BOOST_GEOMETRY_TEST_DISTANCE_GEO_COMMON_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include "distance_brute_force.hpp" + +namespace bg = ::boost::geometry; + +//======================================================================== + + +template +struct check_equal +{ + template + struct equal_to + { + static inline void apply(Value const& x, Value const& y) + { + BOOST_CHECK(x == y); + } + }; + + template + struct equal_to + { + static inline void apply(double x, double y) + { + BOOST_CHECK_CLOSE(x, y, 0.001); + } + }; + + template + static inline void apply(std::string const& /*case_id*/, + std::string const& /*subcase_id*/, + Geometry1 const& /*geometry1*/, + Geometry2 const& /*geometry2*/, + T const& detected, + T const& expected) + { + equal_to::apply(expected, detected); + } +}; + +//======================================================================== + +template +< + typename Geometry1, typename Geometry2, + int id1 = bg::geometry_id::value, + int id2 = bg::geometry_id::value +> +struct test_distance_of_geometries + : public test_distance_of_geometries +{}; + + +template +struct test_distance_of_geometries +{ + template + static inline + void apply(std::string const& case_id, + std::string const& wkt1, + std::string const& wkt2, + DistanceType const& expected_distance, + Strategy const& strategy, + bool test_reversed = true) + { + Geometry1 geometry1 = from_wkt(wkt1); + Geometry2 geometry2 = from_wkt(wkt2); + + apply(case_id, geometry1, geometry2, + expected_distance, + strategy, test_reversed); + } + + + template + < + typename DistanceType, + typename Strategy + > + static inline + void apply(std::string const& case_id, + Geometry1 const& geometry1, + Geometry2 const& geometry2, + DistanceType const& expected_distance, + Strategy const& strategy, + bool test_reversed = true) + { +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "case ID: " << case_id << "; " + << "G1: " << bg::wkt(geometry1) + << " - " + << "G2: " << bg::wkt(geometry2) + << std::endl; +#endif + namespace services = bg::strategy::distance::services; + + using bg::unit_test::distance_brute_force; + + typedef typename bg::default_distance_result + < + Geometry1, Geometry2 + >::type default_distance_result; + + typedef typename services::return_type + < + Strategy, Geometry1, Geometry2 + >::type distance_result_from_strategy; + + static const bool same_regular = boost::is_same + < + default_distance_result, + distance_result_from_strategy + >::type::value; + + BOOST_CHECK(same_regular); + + // check distance with passed strategy + distance_result_from_strategy dist = + bg::distance(geometry1, geometry2, strategy); + + check_equal + < + distance_result_from_strategy + >::apply(case_id, "a", geometry1, geometry2, + dist, expected_distance); + + // check against the comparable distance computed in a + // brute-force manner + default_distance_result dist_brute_force + = distance_brute_force(geometry1, geometry2, strategy); + + check_equal + < + default_distance_result + >::apply(case_id, "b", geometry1, geometry2, + dist_brute_force, expected_distance); + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << string_from_type::type>::name() + << string_from_type::type>::name() + << " -> " + << string_from_type::name() + << std::endl; + std::cout << "expected distance = " + << expected_distance << " ; " + << std::endl; + std::cout << "distance = " + << dist << " ; " + << std::endl; + + if ( !test_reversed ) + { + std::cout << std::endl; + } +#endif + + if ( test_reversed ) + { + // check distance with given strategy + dist = bg::distance(geometry2, geometry1, strategy); + + check_equal + < + default_distance_result + >::apply(case_id, "ra", geometry2, geometry1, + dist, expected_distance); + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "distance[reversed args] = " + << dist << " ; " + << "comp. distance[reversed args] = " + //<< cdist + << std::endl; + std::cout << std::endl; +#endif + } + + } +}; + + +//======================================================================== + + +template +void test_empty_input(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + try + { + bg::distance(geometry1, geometry2); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, + "A empty_input_exception should have been thrown"); + + try + { + bg::distance(geometry2, geometry1); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, + "A empty_input_exception should have been thrown"); + + try + { + bg::distance(geometry1, geometry2, strategy); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, + "A empty_input_exception should have been thrown"); + + try + { + bg::distance(geometry2, geometry1, strategy); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, + "A empty_input_exception should have been thrown"); +} + +#endif // BOOST_GEOMETRY_TEST_DISTANCE_GEO_COMMON_HPP diff --git a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp index 278a44dd9..d84f8936d 100644 --- a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp +++ b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp @@ -805,6 +805,32 @@ BOOST_AUTO_TEST_CASE( envelope_segment_sphere ) 1-heps, 1, 1, 2); } +BOOST_AUTO_TEST_CASE( envelope_segment_spherical_polar ) +{ + typedef bg::cs::spherical coordinate_system_type; + typedef bg::model::point P; + typedef bg::model::segment

G; + typedef bg::model::box

B; + typedef test_envelope_on_sphere_or_spheroid tester; + + tester::apply("s01", + from_wkt("SEGMENT(10 10,40 40)"), + 10, 10, 40, 40); + + tester::apply("s02", + from_wkt("SEGMENT(10 80,40 80)"), + 10, 90 - 10.345270046149988, 40, 80); + + tester::apply("s03", + from_wkt("SEGMENT(160 80,-170 80)"), + 160, 90 - 10.34527004614999, 190, 80); + + // segment ending at the north pole + tester::apply("s05", + from_wkt("SEGMENT(40 45,80 0)"), + 40, 0, 40, 45); +} + BOOST_AUTO_TEST_CASE( envelope_segment_spheroid ) { @@ -1667,8 +1693,7 @@ BOOST_AUTO_TEST_CASE( envelope_multipoint_with_height ) template void test_envelope_box() { - typedef bg::cs::spherical_equatorial coordinate_system_type; - typedef bg::model::point P; + typedef bg::model::point P; typedef bg::model::box

G; typedef bg::model::box

B; typedef test_envelope_on_sphere_or_spheroid tester; @@ -1857,10 +1882,36 @@ void test_envelope_box() tester::apply("b100", G(P(1-heps, 1-heps), P(1, 1)), 1-heps, 1-heps, 1, 1); } +template +void test_envelope_box_polar() +{ + typedef bg::model::point P; + typedef bg::model::box

G; + typedef bg::model::box

B; + typedef test_envelope_on_sphere_or_spheroid tester; + + tester::apply("b01", + from_wkt("BOX(10 10,20 20)"), + 10, 10, 20, 20); + + tester::apply("b02a", + from_wkt("BOX(170 10,-170 20)"), + 170, 10, 190, 20); + + tester::apply("b10b", + from_wkt("BOX(0 0,10 180)"), + 0, 0, 10, 180); + + tester::apply("b16a", + from_wkt("BOX(170 40,-170 180)"), + 170, 40, 190, 180); +} + BOOST_AUTO_TEST_CASE( envelope_box ) { test_envelope_box >(); test_envelope_box >(); + test_envelope_box_polar >(); } diff --git a/test/algorithms/envelope_expand/expand.cpp b/test/algorithms/envelope_expand/expand.cpp index b3cf67ba3..9ed205027 100644 --- a/test/algorithms/envelope_expand/expand.cpp +++ b/test/algorithms/envelope_expand/expand.cpp @@ -5,6 +5,11 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, 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. @@ -95,7 +100,11 @@ void test_2d() template void test_spherical_degree() { - bg::model::box b = bg::make_inverse >(); + // it doesn't work with normalization of input enabled + //bg::model::box b = bg::make_inverse >(); + Point p; + bg::read_wkt("POINT(179.73 71.56)", p); + bg::model::box b(p, p); test_expand(b, "POINT(179.73 71.56)", "(179.73,71.56),(179.73,71.56)"); @@ -104,17 +113,19 @@ void test_spherical_degree() // It detects that this point is lying RIGHT of the others, // and then it "expands" it. - // It might be argued that "181.22" is displayed instead. However, they are - // the same. test_expand(b, "POINT(-178.78 70.78)", - "(177.47,70.78),(-178.78,71.56)"); + "(177.47,70.78),(181.22,71.56)"); } template void test_spherical_radian() { - bg::model::box b = bg::make_inverse >(); + // it doesn't work with normalization of input enabled + //bg::model::box b = bg::make_inverse >(); + Point p; + bg::read_wkt("POINT(3.128 1.249)", p); + bg::model::box b(p, p); test_expand(b, "POINT(3.128 1.249)", "(3.128,1.249),(3.128,1.249)"); @@ -123,10 +134,8 @@ void test_spherical_radian() // It detects that this point is lying RIGHT of the others, // and then it "expands" it. - // It might be argued that "181.22" is displayed instead. However, they are - // the same. test_expand(b, "POINT(-3.121 1.235)", - "(3.097,1.235),(-3.121,1.249)"); + "(3.097,1.235),(3.16219,1.249)"); } int test_main(int, char* []) @@ -141,6 +150,8 @@ int test_main(int, char* []) test_spherical_degree > >(); test_spherical_radian > >(); + test_spherical_degree > >(); + test_spherical_radian > >(); #if defined(HAVE_TTMATH) diff --git a/test/algorithms/envelope_expand/expand_on_spheroid.cpp b/test/algorithms/envelope_expand/expand_on_spheroid.cpp index 1c2436f41..1c535aa87 100644 --- a/test/algorithms/envelope_expand/expand_on_spheroid.cpp +++ b/test/algorithms/envelope_expand/expand_on_spheroid.cpp @@ -1,10 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2015-2016, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -704,6 +705,26 @@ BOOST_AUTO_TEST_CASE( expand_segment_sphere ) 10, -90, 100, 45); } +BOOST_AUTO_TEST_CASE( expand_segment_spherical_polar ) +{ + typedef bg::cs::spherical coordinate_system_type; + typedef bg::model::point point_type; + typedef bg::model::box B; + typedef bg::model::segment G; + typedef test_expand_on_spheroid tester; + + tester::apply("s02", + from_wkt("BOX(20 20,50 50)"), + from_wkt("SEGMENT(10 10,40 20)"), + 10, 10, 50, 50); + + // segment ending at the north pole + tester::apply("s04", + from_wkt("BOX(5 15,50 50)"), + from_wkt("SEGMENT(40 45,80 0)"), + 5, 0, 50, 50); +} + BOOST_AUTO_TEST_CASE( expand_segment_spheroid ) { typedef bg::cs::geographic coordinate_system_type; diff --git a/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp b/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp index ba04c85ef..e604552e5 100644 --- a/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp +++ b/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2015, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, 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 // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -110,6 +111,40 @@ struct other_system_info > } }; +template <> +struct other_system_info > +{ + typedef bg::degree units; + typedef bg::cs::spherical type; + + static inline double convert(double value) + { + return value * bg::math::r2d(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::r2d(); + } +}; + +template <> +struct other_system_info > +{ + typedef bg::radian units; + typedef bg::cs::spherical type; + + static inline double convert(double value) + { + return value * bg::math::d2r(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::d2r(); + } +}; + template <> struct other_system_info > { diff --git a/test/algorithms/is_simple.cpp b/test/algorithms/is_simple.cpp index a61ba6216..41cef5ed5 100644 --- a/test/algorithms/is_simple.cpp +++ b/test/algorithms/is_simple.cpp @@ -13,36 +13,7 @@ #define BOOST_TEST_MODULE test_is_simple #endif -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include - -#ifdef BOOST_GEOMETRY_TEST_DEBUG -#include "pretty_print_geometry.hpp" -#endif +#include "test_is_simple.hpp" namespace bg = ::boost::geometry; @@ -61,71 +32,6 @@ typedef bg::model::multi_polygon multi_polygon_type; typedef bg::model::box box_type; -//---------------------------------------------------------------------------- - - -template -void test_simple(Geometry const& geometry, bool expected_result, - bool check_validity = true) -{ -#ifdef BOOST_GEOMETRY_TEST_DEBUG - std::cout << "=======" << std::endl; -#endif - - bool simple = bg::is_simple(geometry); - - BOOST_ASSERT( ! check_validity || bg::is_valid(geometry) ); - BOOST_CHECK_MESSAGE( simple == expected_result, - "Expected: " << expected_result - << " detected: " << simple - << " wkt: " << bg::wkt(geometry) ); - - typedef typename bg::strategy::intersection::services::default_strategy - < - CSTag - >::type strategy_type; - - bool simple_s = bg::is_simple(geometry, strategy_type()); - - BOOST_CHECK_EQUAL(simple, simple_s); - -#ifdef BOOST_GEOMETRY_TEST_DEBUG - std::cout << "Geometry: "; - pretty_print_geometry::apply(std::cout, geometry); - std::cout << std::endl; - std::cout << std::boolalpha; - std::cout << "is simple: " << simple << std::endl; - std::cout << "expected result: " << expected_result << std::endl; - std::cout << "=======" << std::endl; - std::cout << std::endl << std::endl; - std::cout << std::noboolalpha; -#endif -} - - -template -void test_simple(Geometry const& geometry, - bool expected_result, - bool check_validity = true) -{ - typedef typename bg::cs_tag::type cs_tag; - test_simple(geometry, expected_result, check_validity); -} - -template -void test_simple(boost::variant const& variant_geometry, - bool expected_result, - bool check_validity = true) -{ - typedef typename bg::cs_tag::type cs_tag; - test_simple(variant_geometry, expected_result, check_validity); -} - - - -//---------------------------------------------------------------------------- - - BOOST_AUTO_TEST_CASE( test_is_simple_point ) { #ifdef BOOST_GEOMETRY_TEST_DEBUG diff --git a/test/algorithms/is_simple_geo.cpp b/test/algorithms/is_simple_geo.cpp new file mode 100644 index 000000000..ebd740d05 --- /dev/null +++ b/test/algorithms/is_simple_geo.cpp @@ -0,0 +1,88 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2014-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_is_simple_geo +#endif + +#include "test_is_simple.hpp" + + +typedef bg::model::point > point_type; +typedef bg::model::segment segment_type; +typedef bg::model::linestring linestring_type; +typedef bg::model::multi_linestring multi_linestring_type; +// ccw open and closed polygons +typedef bg::model::polygon open_ccw_polygon_type; +typedef bg::model::polygon closed_ccw_polygon_type; +// multi-geometries +typedef bg::model::multi_point multi_point_type; +typedef bg::model::multi_polygon multi_polygon_type; +// box +typedef bg::model::box box_type; + + +BOOST_AUTO_TEST_CASE( test_is_simple_geo_multipoint ) +{ + typedef multi_point_type G; + + bg::strategy::intersection::geographic_segments<> s; + + test_simple_s(from_wkt("MULTIPOINT(0 90, 0 90)"), s, false); + test_simple_s(from_wkt("MULTIPOINT(0 90, 1 90)"), s, false); + test_simple_s(from_wkt("MULTIPOINT(0 -90, 0 -90)"), s, false); + test_simple_s(from_wkt("MULTIPOINT(0 -90, 1 -90)"), s, false); + test_simple_s(from_wkt("MULTIPOINT(0 80, 1 80)"), s, true); +} + +BOOST_AUTO_TEST_CASE( test_is_simple_geo_linestring ) +{ + typedef linestring_type G; + + bg::strategy::intersection::geographic_segments<> s; + + test_simple_s(from_wkt("LINESTRING(0 0, -90 0, 90 0)"), s, true); + test_simple_s(from_wkt("LINESTRING(0 90, -90 0, 90 0)"), s, false); + test_simple_s(from_wkt("LINESTRING(0 90, -90 50, 90 0)"), s, false); + test_simple_s(from_wkt("LINESTRING(0 90, -90 -50, 90 0)"), s, true); + + // invalid linestrings + test_simple_s(from_wkt("LINESTRING(0 90, 0 90)"), s, false, false); + test_simple_s(from_wkt("LINESTRING(0 -90, 0 -90)"), s, false, false); + test_simple_s(from_wkt("LINESTRING(0 90, 1 90)"), s, false, false); + test_simple_s(from_wkt("LINESTRING(0 -90, 1 -90)"), s, false, false); + + // FAILING + //test_simple_s(from_wkt("LINESTRING(0 90, 0 80, 1 80, 0 90)"), s, false); + //test_simple_s(from_wkt("LINESTRING(0 -90, 0 -80, 1 -80, 0 -90)"), s, false); + //test_simple_s(from_wkt("LINESTRING(0 90, 0 80, 1 80, 1 90)"), s, false); + //test_simple_s(from_wkt("LINESTRING(0 -90, 0 -80, 1 -80, 1 -90)"), s, false); + + test_simple_s(from_wkt("LINESTRING(35 0, 110 36, 159 0, 82 30)"), s, false); + test_simple_s(from_wkt("LINESTRING(135 0, -150 36, -101 0, -178 30)"), s, false); + test_simple_s(from_wkt("LINESTRING(45 0, 120 36, 169 0, 92 30)"), s, false); + test_simple_s(from_wkt("LINESTRING(179 0, -179 1, -179 0, 179 1)"), s, false); +} + +BOOST_AUTO_TEST_CASE( test_is_simple_geo_multilinestring ) +{ + typedef multi_linestring_type G; + + bg::strategy::intersection::geographic_segments<> s; + + // FAILING + //test_simple_s(from_wkt("MULTILINESTRING((0 90, 0 80),(1 90, 1 80))"), s, false); + //test_simple_s(from_wkt("MULTILINESTRING((0 -90, 0 -80),(1 -90, 1 -80))"), s, false); + + test_simple_s(from_wkt("MULTILINESTRING((35 0, 110 36),(159 0, 82 30))"), s, false); + test_simple_s(from_wkt("MULTILINESTRING((135 0, -150 36),(-101 0, -178 30))"), s, false); + test_simple_s(from_wkt("MULTILINESTRING((45 0, 120 36),(169 0, 92 30))"), s, false); + test_simple_s(from_wkt("MULTILINESTRING((179 0, -179 1),(-179 0, 179 1))"), s, false); +} diff --git a/test/algorithms/is_valid.cpp b/test/algorithms/is_valid.cpp index 1fc356f64..e6b84ff4d 100644 --- a/test/algorithms/is_valid.cpp +++ b/test/algorithms/is_valid.cpp @@ -963,6 +963,17 @@ inline void test_open_multipolygons() "MULTIPOLYGON(((10 5,5 10,0 5,5 0),(10 5,5 4,4 6)),((10 5,10 0,20 0,20 10,10 10),(10 5,18 8,15 3)))", true); + // Two polygons, one inside interior of other one, touching all at same point (0,0) + test::apply + ("mpg23", + "MULTIPOLYGON(((0 0,10 0,10 10,0 10),(0 0,1 9,9 9,9 1)),((0 0,8 2,8 8,2 8)))", + true); + + test::apply + ("ticket_12503", + "MULTIPOLYGON(((15 20,12 23,15 17,15 20)),((36 25,35 25,34 24,34 23,35 23,36 25)),((15 15,10 13,11 23,12 23,12 25,10 25,7 24,8 6,15 15)),((12 29,11 30,12 30,11 31,13 31,13 34,6 38,6 32,8 31,12 27,12 29)),((9 26,6 31,7 26,7 24,9 26)),((15 48,15 45,18 44,15 48)),((38 39,18 44,26 34,38 39)),((15 45,13 34,15 33,15 45)),((17 32,15 33,15 32,17 32)),((21 31,16 38,18 32,17 32,19 30,21 31)),((15 32,13 31,13 30,15 29,15 32)),((17 29,15 29,15 28,17 29)),((15 28,13 30,12 29,14 27,15 28)),((26 27,28 30,31 27,26 34,21 31,22 29,19 30,18 30,17 29,19 29,19 28,23 28,24 27,25 27,24 26,25 24,30 24,26 27)),((17 26,15 28,15 26,17 26)),((32 26,34 27,31 27,27 27,32 26)),((19 26,17 26,19 25,19 26)),((35 23,33 18,41 15,35 23)),((24 26,24 27,19 26,20 25,23 24,24 26)),((32 13,49 1,48 4,46 5,33 15,32 13)),((33 25,32 26,32 25,31 24,32 23,33 25)),((42 15,43 22,44 22,44 23,43 23,35 23,42 15)),((44 42,38 39,40 39,39 34,34 27,33 25,35 25,38 31,36 25,43 23,44 42)),((48 46,44 23,48 22,48 46)),((15 3,23 2,18 11,15 3)),((30 19,28 20,27 21,25 24,23 24,22 23,26 20,29 17,30 19)),((24 19,21 21,21 20,22 19,24 19)),((31 24,30 24,27 21,31 22,30 19,31 19,34 23,31 23,31 24)),((21 20,20 21,21 18,21 20)),((14 26,12 26,12 25,15 25,15 20,17 17,20 21,21 21,22 23,20 24,19 25,16 25,15 26,14 27,14 26),(17 24,20 22,20 21,17 24)),((23 18,22 19,22 17,23 18)),((28 13,31 10,32 13,30 15,28 13)),((18 17,17 17,16 16,18 17)),((16 16,15 17,15 15,16 16)),((30 17,29 17,29 16,30 15,30 17)),((33 18,31 19,30 17,33 15,33 18)),((42 13,47 7,48 4,48 22,44 22,43 14,42 13)),((42 15,41 15,42 14,43 14,42 15)),((24 2,49 1,27 11,23 13,25 6,27 10,24 2)),((29 16,24 19,23 18,28 13,29 16)),((17 13,16 15,15 15,15 11,17 13)),((20 14,23 13,22 17,20 15,21 17,21 18,18 17,19 15,17 13,18 11,20 14)),((5 3,15 3,15 11,8 5,8 6,5 3)))", + true); + // MySQL report 12.06.2015 { std::string wkt = "MULTIPOLYGON(" @@ -1186,11 +1197,8 @@ inline void test_open_multipolygons() BOOST_AUTO_TEST_CASE( test_is_valid_multipolygon ) { - bool const allow_duplicates = true; - bool const do_not_allow_duplicates = !allow_duplicates; - - test_open_multipolygons(); - test_open_multipolygons(); + test_open_multipolygons(); + test_open_multipolygons(); } diff --git a/test/algorithms/is_valid_failure.cpp b/test/algorithms/is_valid_failure.cpp index 7d2ea1d27..cbdd29180 100644 --- a/test/algorithms/is_valid_failure.cpp +++ b/test/algorithms/is_valid_failure.cpp @@ -848,10 +848,9 @@ void test_open_multipolygons() bg::failure_self_intersections); // one polygon inside another and boundaries touching - // TODO: return the failure value failure_intersecting_interiors test::apply("mpg09", "MULTIPOLYGON(((0 0,10 0,10 10,0 10)),((0 0,9 1,9 2)))", - bg::failure_self_intersections); + bg::failure_intersecting_interiors); // one polygon inside another and boundaries not touching test::apply("mpg10", diff --git a/test/algorithms/is_valid_geo.cpp b/test/algorithms/is_valid_geo.cpp new file mode 100644 index 000000000..e2a54146b --- /dev/null +++ b/test/algorithms/is_valid_geo.cpp @@ -0,0 +1,38 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2014-2017, 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 + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_is_valid_geo +#endif + +#include +#include + +#include + +#include "test_is_valid.hpp" + +#include + +#include +#include +#include + +BOOST_AUTO_TEST_CASE( test_is_valid_geo_polygon ) +{ + typedef bg::model::point > pt; + typedef bg::model::polygon G; + + typedef validity_tester_geo_areal tester; + typedef test_valid test; + + test::apply("p01", "POLYGON((-1 -1, 1 -1, 1 1, -1 1, -1 -1),(-0.5 -0.5, -0.5 0.5, 0.0 0.0, -0.5 -0.5),(0.0 0.0, 0.5 0.5, 0.5 -0.5, 0.0 0.0))", true); +} diff --git a/test/algorithms/overlay/get_turn_info.cpp b/test/algorithms/overlay/get_turn_info.cpp index 24fc5ac74..6310e248e 100644 --- a/test/algorithms/overlay/get_turn_info.cpp +++ b/test/algorithms/overlay/get_turn_info.cpp @@ -36,6 +36,7 @@ void test_with_point(std::string const& caseid, T pi_x, T pi_y, T pj_x, T pj_y, T pk_x, T pk_y, T qi_x, T qi_y, T qj_x, T qj_y, T qk_x, T qk_y, bg::detail::overlay::method_type expected_method, + bool expected_touch_only, T ip_x, T ip_y, std::string const& expected, T ip_x2, T ip_y2) @@ -100,6 +101,10 @@ void test_with_point(std::string const& caseid, if (! info.empty()) { BOOST_CHECK_EQUAL(info[0].method, expected_method); + BOOST_CHECK_MESSAGE(info[0].touch_only == expected_touch_only, + caseid + << " detected: " << info[0].touch_only + << " expected: " << expected_touch_only); BOOST_CHECK_CLOSE(bg::get<0>(info[0].point), ip_x, 0.001); BOOST_CHECK_CLOSE(bg::get<1>(info[0].point), ip_y, 0.001); @@ -164,6 +169,8 @@ void test_with_point(std::string const& caseid, op.clear(); op += bg::method_char(it->method); + op += ' '; + op += (it->touch_only ? 'o' : '*'); if (info.size() != 1) { op += ch; @@ -183,6 +190,7 @@ void test_both(std::string const& caseid, T qi_x, T qi_y, T qj_x, T qj_y, T qk_x, T qk_y, bg::detail::overlay::method_type method = bg::detail::overlay::method_none, + bool expected_touch_only = false, T ip_x = -1, T ip_y = -1, std::string const& expected = "", T ip_x2 = -1, T ip_y2 = -1) @@ -190,9 +198,7 @@ void test_both(std::string const& caseid, test_with_point(caseid, pi_x, pi_y, pj_x, pj_y, pk_x, pk_y, qi_x, qi_y, qj_x, qj_y, qk_x, qk_y, - method, ip_x, ip_y, expected, ip_x2, ip_y2); - - //return; + method, expected_touch_only, ip_x, ip_y, expected, ip_x2, ip_y2); std::string reversed(expected.rbegin(), expected.rend()); @@ -205,7 +211,7 @@ void test_both(std::string const& caseid, test_with_point(caseid + "_r", qi_x, qi_y, qj_x, qj_y, qk_x, qk_y, // q pi_x, pi_y, pj_x, pj_y, pk_x, pk_y, // p - method, ip_x, ip_y, reversed, ip_x2, ip_y2); + method, expected_touch_only, ip_x, ip_y, reversed, ip_x2, ip_y2); } @@ -223,27 +229,27 @@ void test_all() test_both("il1", 5, 1, 5, 6, 7, 8, // p 3, 3, 7, 5, 8, 3, // q - method_crosses, 5, 4, "ui"); + method_crosses, false, 5, 4, "ui"); test_both("il2", 5, 1, 5, 6, 7, 8, // p 3, 5, 7, 5, 3, 3, // q - method_crosses, 5, 5, "ui"); + method_crosses, false, 5, 5, "ui"); test_both("il3", 5, 1, 5, 6, 7, 8, // p 3, 3, 7, 5, 3, 5, // q - method_crosses, 5, 4, "ui"); + method_crosses, false, 5, 4, "ui"); test_both("il4", 5, 1, 5, 6, 7, 8, // p 3, 3, 7, 5, 4, 8, // q - method_crosses, 5, 4, "ui"); + method_crosses, false, 5, 4, "ui"); test_both("ir1", 5, 1, 5, 6, 7, 8, // p 7, 5, 3, 3, 2, 5, // q - method_crosses, 5, 4, "iu"); + method_crosses, false, 5, 4, "iu"); // ------------------------------------------------------------------------ @@ -252,52 +258,52 @@ void test_all() test_both("ml1", 5, 1, 5, 6, 7, 8, // p 3, 3, 5, 4, 7, 3, // q - method_touch_interior, 5, 4, "ui"); + method_touch_interior, false, 5, 4, "ui"); test_both("ml2", 5, 1, 5, 6, 7, 8, // p 3, 3, 5, 4, 3, 6, // q - method_touch_interior, 5, 4, "iu"); + method_touch_interior, true, 5, 4, "iu"); test_both("ml3", 5, 1, 5, 6, 7, 8, // p 3, 6, 5, 4, 3, 3, // q - method_touch_interior, 5, 4, "uu"); + method_touch_interior, true, 5, 4, "uu"); test_both("mr1", 5, 1, 5, 6, 7, 8, // p 7, 3, 5, 4, 3, 3, // q - method_touch_interior, 5, 4, "iu"); + method_touch_interior, false, 5, 4, "iu"); test_both("mr2", 5, 1, 5, 6, 7, 8, // p 7, 3, 5, 4, 7, 6, // q - method_touch_interior, 5, 4, "ui"); + method_touch_interior, true, 5, 4, "ui"); test_both("mr3", 5, 1, 5, 6, 7, 8, // p 7, 6, 5, 4, 7, 3, // q - method_touch_interior, 5, 4, "ii"); + method_touch_interior, true, 5, 4, "ii"); test_both("mcl", 5, 1, 5, 6, 7, 8, // p 3, 2, 5, 3, 5, 5, // q - method_touch_interior, 5, 3, "cc"); + method_touch_interior, false, 5, 3, "cc"); test_both("mcr", 5, 1, 5, 6, 7, 8, // p 7, 2, 5, 3, 5, 5, // q - method_touch_interior, 5, 3, "cc"); + method_touch_interior, false, 5, 3, "cc"); test_both("mclo", 5, 1, 5, 6, 7, 8, // p 3, 4, 5, 5, 5, 3, // q - method_touch_interior, 5, 5, "ux"); + method_touch_interior, false, 5, 5, "ux"); test_both("mcro", 5, 1, 5, 6, 7, 8, // p 7, 4, 5, 5, 5, 3, // q - method_touch_interior, 5, 5, "ix"); + method_touch_interior, false, 5, 5, "ix"); // ------------------------------------------------------------------------ // COLLINEAR @@ -305,53 +311,53 @@ void test_all() test_both("cll1", 5, 1, 5, 6, 3, 8, // p 5, 5, 5, 7, 3, 8, // q - method_collinear, 5, 6, "ui"); + method_collinear, false, 5, 6, "ui"); test_both("cll2", 5, 1, 5, 6, 3, 8, // p 5, 3, 5, 5, 3, 6, // q - method_collinear, 5, 5, "iu"); + method_collinear, false, 5, 5, "iu"); test_both("clr1", 5, 1, 5, 6, 3, 8, // p 5, 5, 5, 7, 6, 8, // q - method_collinear, 5, 6, "ui"); + method_collinear, false, 5, 6, "ui"); test_both("clr2", 5, 1, 5, 6, 3, 8, // p 5, 3, 5, 5, 6, 6, // q - method_collinear, 5, 5, "ui"); + method_collinear, false, 5, 5, "ui"); test_both("crl1", 5, 1, 5, 6, 7, 8, // p 5, 5, 5, 7, 3, 8, // q - method_collinear, 5, 6, "iu"); + method_collinear, false, 5, 6, "iu"); test_both("crl2", 5, 1, 5, 6, 7, 8, // p 5, 3, 5, 5, 3, 6, // q - method_collinear, 5, 5, "iu"); + method_collinear, false, 5, 5, "iu"); test_both("crr1", 5, 1, 5, 6, 7, 8, // p 5, 5, 5, 7, 6, 8, // q - method_collinear, 5, 6, "iu"); + method_collinear, false, 5, 6, "iu"); test_both("crr2", 5, 1, 5, 6, 7, 8, // p 5, 3, 5, 5, 6, 6, // q - method_collinear, 5, 5, "ui"); + method_collinear, false, 5, 5, "ui"); // The next two cases are changed (BSG 2013-09-24), they contain turn info (#buffer_rt_g) // In new approach they are changed back (BSG 2013-10-20) test_both("ccx1", 5, 1, 5, 6, 5, 8, // p 5, 5, 5, 7, 3, 8, // q - method_collinear, 5, 6, "cc"); // "iu"); + method_collinear, false, 5, 6, "cc"); // "iu"); test_both("cxc1", 5, 1, 5, 6, 7, 8, // p 5, 3, 5, 5, 5, 7, // q - method_collinear, 5, 5, "cc"); // "iu"); + method_collinear, false, 5, 5, "cc"); // "iu"); // Bug in case #54 of "overlay_cases.hpp" test_both("c_bug1", 5, 0, 2, 0, 2, 2, // p 4, 0, 1, 0, 1, 2, // q - method_collinear, 2, 0, "iu"); + method_collinear, false, 2, 0, "iu"); // ------------------------------------------------------------------------ @@ -361,66 +367,66 @@ void test_all() test_both("clo1", 5, 2, 5, 6, 3, 8, // p 5, 7, 5, 5, 3, 3, // q - method_collinear, 5, 6, "ixxu", 5, 5); + method_collinear, false, 5, 6, "ixxu", 5, 5); test_both("clo2", 5, 2, 5, 6, 3, 8, // p 5, 7, 5, 5, 5, 2, // q - method_collinear, 5, 6, "ix"); + method_collinear, false, 5, 6, "ix"); // actually "xxix", xx is skipped everywhere test_both("clo3", 5, 2, 5, 6, 3, 8, // p 5, 7, 5, 5, 7, 3, // q - method_collinear, 5, 6, "ixxi", 5, 5); + method_collinear, false, 5, 6, "ixxi", 5, 5); test_both("cco1", 5, 2, 5, 6, 5, 8, // p 5, 7, 5, 5, 3, 3, // q - method_collinear, 5, 5, "xu"); // "xuxx" + method_collinear, false, 5, 5, "xu"); // "xuxx" test_both("cco2", 5, 2, 5, 6, 5, 8, // p 5, 7, 5, 5, 5, 2); // q "xxxx" test_both("cco3", 5, 2, 5, 6, 5, 8, // p 5, 7, 5, 5, 7, 3, // q - method_collinear, 5, 5, "xi"); // "xixx" + method_collinear, false, 5, 5, "xi"); // "xixx" test_both("cro1", 5, 2, 5, 6, 7, 8, // p 5, 7, 5, 5, 3, 3, // q - method_collinear, 5, 6, "uxxu", 5, 5); + method_collinear, false, 5, 6, "uxxu", 5, 5); test_both("cro2", 5, 2, 5, 6, 7, 8, // p 5, 7, 5, 5, 5, 2, // q - method_collinear, 5, 6, "ux"); // "xxux" + method_collinear, false, 5, 6, "ux"); // "xxux" test_both("cro3", 5, 2, 5, 6, 7, 8, // p 5, 7, 5, 5, 7, 3, // q - method_collinear, 5, 6, "uxxi", 5, 5); + method_collinear, false, 5, 6, "uxxi", 5, 5); test_both("cxo1", 5, 2, 5, 6, 3, 8, // p 5, 5, 5, 3, 3, 1, // q - method_collinear, 5, 3, "xu"); + method_collinear, false, 5, 3, "xu"); test_both("cxo2", 5, 2, 5, 6, 3, 8, // p 5, 5, 5, 3, 5, 0); // q "xx" test_both("cxo3", 5, 2, 5, 6, 3, 8, // p 5, 5, 5, 3, 7, 1, // q - method_collinear, 5, 3, "xi"); + method_collinear, false, 5, 3, "xi"); test_both("cxo4", 5, 2, 5, 6, 3, 8, // p 5, 7, 5, 1, 3, 0, // q - method_collinear, 5, 6, "ix"); + method_collinear, false, 5, 6, "ix"); test_both("cxo5", 5, 2, 5, 6, 5, 8, // p 5, 7, 5, 1, 3, 0); // q "xx" test_both("cxo6", 5, 2, 5, 6, 7, 8, // p 5, 7, 5, 1, 3, 0, // q - method_collinear, 5, 6, "ux"); + method_collinear, false, 5, 6, "ux"); // Verify @@ -441,61 +447,61 @@ void test_all() test_both("blr1", 5, 1, 5, 6, 4, 4, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); test_both("blr2", 5, 1, 5, 6, 1, 4, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("blr3", 5, 1, 5, 6, 3, 6, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("blr4", 5, 1, 5, 6, 1, 8, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); test_both("blr5", 5, 1, 5, 6, 4, 8, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("blr6", 5, 1, 5, 6, 6, 4, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("blr7", 5, 1, 5, 6, 3, 6, // p 3, 7, 5, 6, 5, 3, // q - method_touch, 5, 6, "ix"); + method_touch, false, 5, 6, "ix"); test_both("blr8", 5, 1, 5, 6, 3, 6, // p 3, 6, 5, 6, 5, 3, // q - method_touch, 5, 6, "xx"); + method_touch, false, 5, 6, "xx"); test_both("blr9", 5, 1, 5, 6, 3, 6, // p 3, 5, 5, 6, 5, 3, // q - method_touch, 5, 6, "ux"); + method_touch, false, 5, 6, "ux"); // Variants test_both("blr7-a", 5, 1, 5, 6, 3, 6, // p 5, 8, 5, 6, 5, 3, // q - method_touch, 5, 6, "ix"); + method_touch, false, 5, 6, "ix"); test_both("blr7-b", // in fact NOT "both-left" 5, 1, 5, 6, 3, 6, // p 6, 8, 5, 6, 5, 3, // q - method_touch, 5, 6, "ix"); + method_touch, false, 5, 6, "ix"); // To check if "collinear-check" on other side // does not apply to this side test_both("blr6-c1", 5, 1, 5, 6, 7, 5, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("blr6-c2", 5, 1, 5, 6, 7, 7, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); @@ -503,75 +509,75 @@ void test_all() test_both("brr1", 5, 1, 5, 6, 6, 4, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("brr2", 5, 1, 5, 6, 9, 4, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); test_both("brr3", 5, 1, 5, 6, 7, 6, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("brr4", 5, 1, 5, 6, 9, 8, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("brr5", 5, 1, 5, 6, 6, 8, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); test_both("brr6", 5, 1, 5, 6, 4, 4, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); // Both right, Q turns left test_both("brl1", 5, 1, 5, 6, 6, 4, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); test_both("brl2", 5, 1, 5, 6, 9, 4, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("brl3", 5, 1, 5, 6, 7, 6, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("brl4", 5, 1, 5, 6, 9, 8, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); test_both("brl5", 5, 1, 5, 6, 6, 8, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("brl6", 5, 1, 5, 6, 4, 4, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("brl7", 5, 1, 5, 6, 7, 6, // p 7, 7, 5, 6, 5, 3, // q - method_touch, 5, 6, "ux"); + method_touch, false, 5, 6, "ux"); test_both("brl8", 5, 1, 5, 6, 7, 6, // p 7, 6, 5, 6, 5, 3, // q - method_touch, 5, 6, "xx"); + method_touch, false, 5, 6, "xx"); test_both("brl9", 5, 1, 5, 6, 7, 6, // p 7, 5, 5, 6, 5, 3, // q - method_touch, 5, 6, "ix"); + method_touch, false, 5, 6, "ix"); // Variants test_both("brl7-a", 5, 1, 5, 6, 7, 6, // p 5, 8, 5, 6, 5, 3, // q - method_touch, 5, 6, "ux"); + method_touch, false, 5, 6, "ux"); test_both("brl7-b", // in fact NOT "both right" 5, 1, 5, 6, 7, 6, // p 4, 8, 5, 6, 5, 3, // q - method_touch, 5, 6, "ux"); + method_touch, false, 5, 6, "ux"); @@ -579,251 +585,251 @@ void test_all() test_both("bll1", 5, 1, 5, 6, 4, 4, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("bll2", 5, 1, 5, 6, 1, 4, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); test_both("bll3", 5, 1, 5, 6, 3, 6, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("bll4", 5, 1, 5, 6, 1, 8, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("bll5", 5, 1, 5, 6, 4, 8, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); test_both("bll6", 5, 1, 5, 6, 6, 4, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); // TOUCH - COLLINEAR + one side // Collinear/left, Q turns right test_both("t-clr1", 5, 1, 5, 6, 4, 4, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); test_both("t-clr2", 5, 1, 5, 6, 1, 4, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("t-clr3", 5, 1, 5, 6, 3, 6, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("t-clr4", 5, 1, 5, 6, 5, 8, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); // 5 n.a. test_both("t-clr6", 5, 1, 5, 6, 6, 4, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); // Collinear/right, Q turns right test_both("t-crr1", 5, 1, 5, 6, 6, 4, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("t-crr2", 5, 1, 5, 6, 9, 4, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); test_both("t-crr3", 5, 1, 5, 6, 7, 6, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("t-crr4", 5, 1, 5, 6, 5, 9, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); // 5 n.a. test_both("t-crr6", 5, 1, 5, 6, 4, 4, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); // Collinear/right, Q turns left test_both("t-crl1", 5, 1, 5, 6, 6, 4, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); test_both("t-crl2", 5, 1, 5, 6, 9, 4, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("t-crl3", 5, 1, 5, 6, 7, 6, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("t-crl4", 5, 1, 5, 6, 5, 8, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); // 5 n.a. test_both("t-crl6", 5, 1, 5, 6, 4, 4, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); // Collinear/left, Q turns left test_both("t-cll1", 5, 1, 5, 6, 4, 4, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("t-cll2", 5, 1, 5, 6, 1, 4, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); test_both("t-cll3", 5, 1, 5, 6, 3, 6, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("t-cll4", 5, 1, 5, 6, 5, 9, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); // 5 n.a. test_both("t-cll6", 5, 1, 5, 6, 6, 4, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); // Left to right test_both("lr1", 5, 1, 5, 6, 3, 3, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("lr2", 5, 1, 5, 6, 1, 5, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); test_both("lr3", 5, 1, 5, 6, 4, 8, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr4", 5, 1, 5, 6, 9, 5, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("lr5", 5, 1, 5, 6, 7, 3, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); // otherwise case more thoroughly test_both("lr3a", 5, 1, 5, 6, 1, 6, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr3b", 5, 1, 5, 6, 5, 10, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr3c", 5, 1, 5, 6, 8, 9, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr3d", 5, 1, 5, 6, 9, 7, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr3e", 5, 1, 5, 6, 9, 6, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); // Right to left test_both("rl1", 5, 1, 5, 6, 3, 3, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); test_both("rl2", 5, 1, 5, 6, 1, 5, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("rl3", 5, 1, 5, 6, 4, 8, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("rl4", 5, 1, 5, 6, 9, 5, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); test_both("rl5", 5, 1, 5, 6, 7, 3, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); // Equal (p1/q1 are equal) test_both("ebl1", 5, 1, 5, 6, 3, 4, // p 5, 1, 5, 6, 3, 8, // q - method_equal, 5, 6, "ui"); + method_equal, false, 5, 6, "ui"); test_both("ebl2", 5, 1, 5, 6, 3, 8, // p 5, 1, 5, 6, 3, 4, // q - method_equal, 5, 6, "iu"); + method_equal, false, 5, 6, "iu"); test_both("ebl3", 5, 1, 5, 6, 3, 8, // p 5, 1, 5, 6, 3, 8, // q - method_equal, 5, 6, "cc"); + method_equal, false, 5, 6, "cc"); test_both("ebl3-c1", 5, 1, 5, 6, 10, 1, // p 5, 1, 5, 6, 3, 8, // q - method_equal, 5, 6, "iu"); + method_equal, false, 5, 6, "iu"); test_both("ebr1", 5, 1, 5, 6, 7, 4, // p 5, 1, 5, 6, 7, 8, // q - method_equal, 5, 6, "iu"); + method_equal, false, 5, 6, "iu"); test_both("ebr2", 5, 1, 5, 6, 7, 8, // p 5, 1, 5, 6, 7, 4, // q - method_equal, 5, 6, "ui"); + method_equal, false, 5, 6, "ui"); test_both("ebr3", 5, 1, 5, 6, 7, 8, // p 5, 1, 5, 6, 7, 8, // q - method_equal, 5, 6, "cc"); + method_equal, false, 5, 6, "cc"); test_both("ebr3-c1", 5, 1, 5, 6, 0, 1, // p 5, 1, 5, 6, 7, 8, // q - method_equal, 5, 6, "ui"); + method_equal, false, 5, 6, "ui"); test_both("elr1", 5, 1, 5, 6, 7, 8, // p 5, 1, 5, 6, 3, 8, // q - method_equal, 5, 6, "iu"); + method_equal, false, 5, 6, "iu"); test_both("elr2", 5, 1, 5, 6, 3, 8, // p 5, 1, 5, 6, 7, 8, // q - method_equal, 5, 6, "ui"); + method_equal, false, 5, 6, "ui"); test_both("ec1", 5, 1, 5, 6, 5, 8, // p 5, 1, 5, 6, 5, 8, // q - method_equal, 5, 6, "cc"); + method_equal, false, 5, 6, "cc"); test_both("ec2", 5, 1, 5, 6, 5, 8, // p 5, 1, 5, 6, 5, 7, // q - method_equal, 5, 6, "cc"); + method_equal, false, 5, 6, "cc"); test_both("snl-1", 0, 3, 2, 3, 4, 3, // p 4, 3, 2, 3, 0, 3, // q - method_touch, 2, 3, "xx"); + method_touch, false, 2, 3, "xx"); // BSG 2012-05-26 to be decided what's the problem here and what it tests... // Anyway, test results are not filled out. //test_both("issue_buffer_mill", // 5.1983614873206241 , 6.7259025813913107 , 5.0499999999999998 , 6.4291796067500622 , 5.1983614873206241 , 6.7259025813913107, // p // 5.0499999999999998 , 6.4291796067500622 , 5.0499999999999998 , 6.4291796067500622 , 5.1983614873206241 , 6.7259025813913107, // q - // method_collinear, 2, 0, "tt"); + // method_collinear, false, 2, 0, "tt"); } diff --git a/test/algorithms/overlay/get_turns_areal_areal_sph.cpp b/test/algorithms/overlay/get_turns_areal_areal_sph.cpp index 18544cd8f..4905e4b53 100644 --- a/test/algorithms/overlay/get_turns_areal_areal_sph.cpp +++ b/test/algorithms/overlay/get_turns_areal_areal_sph.cpp @@ -1,7 +1,7 @@ // Boost.Geometry // Unit Test -// Copyright (c) 2016, Oracle and/or its affiliates. +// Copyright (c) 2016-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -62,6 +62,10 @@ void test_all() expected("mcc")("cui")); test_geometry(case_18_sph[0], case_18_sph[1], expected("mcc")("ccc")("ccc")("cui")); + + test_geometry("POLYGON((16 15,-132 10,-56 89,67 5,16 15))", + "POLYGON((101 49,12 40,-164 10,117 0,101 49))", + expected("iiu")("iui")); } int test_main(int, char* []) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index bd94b43a4..9914b8655 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -629,6 +629,13 @@ static std::string case_140_multi[2] = "MULTIPOLYGON(((2 0,2 8,8 8,8 6,10 6,10 2,6 2,6 0,2 0),(6 6,6 4,4 4,4 6,6 6)),((5 4.5,4 6,5.5 5, 5 4.5)))" }; +static std::string case_141_multi[2] = +{ + // Version to test more isolation/validity cases + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(2 3,1 2,2 1,3 2,2 3),(2 7,3 8,2 9,1 8,2 7),(10 3,9 4,8 3,9 2,10 3),(7 10,6 9,7 8,8 9,7 10),(10 7,9 8,8 7,9 6,10 7)))", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(0 5,2 3,4 5,2 7),(3 2,4 1,5 2,4 3,3 2),(3 8,4 7,5 8,4 9,3 8),(7 2,8 1,9 2,8 2,8 3,7 2),(8 7,7 8,6 7,7 6,8 7)))" +}; + static std::string case_recursive_boxes_1[2] = { "MULTIPOLYGON(((1 0,0 0,0 1,1 1,1 2,0 2,0 4,2 4,2 5,3 5,3 6,1 6,1 5,0 5,0 10,9 10,9 9,7 9,7 8,6 8,6 7,8 7,8 6,9 6,9 4,8 4,8 3,10 3,10 0,6 0,6 1,5 1,5 0,1 0),(8 4,8 5,7 5,7 6,6 6,6 5,5 5,5 4,4 4,4 3,5 3,5 2,7 2,7 3,6 3,6 4,8 4),(8 1,9 1,9 2,8 2,8 1),(4 7,4 9,3 9,3 7,4 7)),((9 9,10 9,10 7,10 6,9 6,9 7,8 7,8 8,9 8,9 9)))", @@ -1080,6 +1087,155 @@ static std::string case_recursive_boxes_60[2] = "MULTIPOLYGON(((2 3,1 2,1 4,0 4,0 5,3 5,3 3,2 3)),((1 2,3 2,3 3,4 4,5 4,5 3,4 3,3.5 2.5,4 2,4 0,0 0,0 1,1 1,1 2)),((4 2,5 3,5 2,4 2)))", }; + +static std::string case_recursive_boxes_61[2] = +{ + // Needs pattern 4 for intersections + "MULTIPOLYGON(((2 0,2 1,1 0,0 0,0 1,1 2,0 2,0 4,1 4,1 5,1.5 4.5,2 5,4 5,4.5 4.5,5 5,5 2,4.5 1.5,5 1,5 0,2 0),(3 1,3 2,2 2,2 1,3 1),(4 5,3.5 4.5,4 4,4 5)),((0 4,0 5,1 5,0 4)))", + "MULTIPOLYGON(((3 5,5 5,5 0,2 0,2 1,1 1,1 0,0 0,0 2,1 2,1 3,0 3,0 5,3 5),(3 1,3 2,2 2,2 1,3 1)))", +}; + +static std::string case_recursive_boxes_62[2] = +{ + // Needs discarding self-intersections within other geometry + "MULTIPOLYGON(((2.5 3.5,3 4,3 3,4 3,4 2,5 2,5 0,1 0,1 1,2 1,3 2,2 2,2 3,1 3,2 4,2.5 3.5),(3 1,3 0,4 1,3 1)),((1 0,0 0,0 1,1 0)),((2 4,2 5,3 4,2 4)),((2 4,1 4,1 5,2 4)),((3 4,3 5,4 5,4 4,3 4)),((0 1,0 3,0.5 2.5,1 3,1 1,0 1)),((1 3,0 3,0 4,1 4,1 3)),((4 4,5 4,5 3,4 3,4 4)))", + "MULTIPOLYGON(((2 0,1 0,1 1,2 2,2 1,4 1,3 0,2 0)),((2 2,1 2,1 1,0.5 0.5,1 0,0 0,0 5,4 5,4 4,3 3,4 3,4 2,2 2),(1 3,1 4,0 4,1 3)),((4 2,5 1,4 1,4 2)))", +}; + +static std::string case_recursive_boxes_63[2] = +{ + // Derived from 62, needs not excluding startable points for checking rings for traversals + "MULTIPOLYGON(((2 0,1 0,1 1,2 2,2 1,4 1,3 0,2 0)),((2 2,1 2,1 1,0.5 0.5,1 0,0 0,0 5,4 5,4 4,3 3,4 3,4 2,2 2),(1 3,1 4,0 4,1 3)),((4 2,5 1,4 1,4 2)))", + "MULTIPOLYGON(((-1 -1, 6 -1, 6 6, -1 6, -1 -1), (0 0, 0 1, 0 3, 0 4, 1 4, 1 5, 2 4, 2 5, 3 4, 3 5, 4 5, 4 4, 5 4, 5 3, 4 3, 4 2, 5 2, 5 0, 3 0, 1 0, 0 0)),((2.5 3.5, 3 4, 2 4, 2.5 3.5)), ((3 3, 4 3, 4 4, 3 4, 3 3)), ((1 3, 2 4, 1 4, 1 3)), ((0.5 2.5, 1 3, 0 3, 0.5 2.5)), ((1 1, 2 1, 3 2, 2 2, 2 3, 1 3, 1 1)), ((3 0, 4 1, 3 1, 3 0)), ((1 0, 1 1, 0 1, 1 0)))", +}; + +static std::string case_recursive_boxes_64[2] = +{ + // Needs considering remaining_distance in clusters + "MULTIPOLYGON(((3 4,3 5,5 5,5 4,4 4,3 3,3.5 2.5,4 3,5 3,5 1,4 0,0 0,0 5,2 5,2 4,3 4),(2 2,3 2,3 3,2 2),(2 3,2 4,1 4,2 3),(2 0,2 1,1 1,2 0),(1 2,0 2,0.5 1.5,1 2)))", + "MULTIPOLYGON(((3 5,5 5,5 0,0 0,1 1,0 1,0 3,1 3,1 4,1.5 3.5,2 4,2 5,3 5),(2 2,1 2,2 1,2 2),(2 2,2.5 1.5,3 2,2 2),(4 4,4.5 3.5,5 4,4 4),(4 1,3 1,4 0,4 1)),((2 5,0 3,0 5,2 5)))" +}; + +static std::string case_recursive_boxes_65[2] = +{ + // Misses large hole in intersection + "MULTIPOLYGON(((3 5,3.5 4.5,4 5,5 5,5 1,4 0,3 0,3 1,2 0,0 0,0 4,1 4,1 5,3 5),(2 4,2 3,3 4,2 4),(2 1,2 2,1.5 1.5,2 1),(3 3,4 2,4 3,3 3)))", + "MULTIPOLYGON(((3 5,4 5,4 4,5 5,5 2,4 2,3.5 1.5,5 0,1 0,1 1,0 1,0 5,3 5),(2 2,3 2,4 3,1 3,1 2,2 2)),((1 0,0 0,0 1,1 0)),((4 1,4 2,5 1,4 1)))" +}; + +static std::string case_recursive_boxes_66[2] = +{ + // Needs self-turns startable, at least not determined using count left/right for self-turns + "MULTIPOLYGON(((1 0,0 0,1 1,0 1,0 4,1 4,1 5,3 5,3 4,4 5,5 5,5 0,1 0),(3 3,4 2,4 3,3 3),(3 1,4 1,4 2,3 2,3 1),(3 3,3 4,2 3,3 3),(3 4,3.5 3.5,4 4,3 4)))", + "MULTIPOLYGON(((2 0,0 0,0 1,1 1,1 2,0 2,0 5,1 5,2 4,2 5,5 5,5 0,2 0),(3 1,3 2,2.5 1.5,3 1),(2 3,1 2,2 1,2 2,3 2,4 3,2 3),(0 3,0.5 2.5,1 3,1 4,0 3)))" +}; + +static std::string case_recursive_boxes_67[2] = +{ + // Needs to avoid including any untraveled ring with blocked turns + "MULTIPOLYGON(((2 2,3 3,3 2,2 2)),((2 2,1 2,1 3,2 3,2 2)),((1 1,2 1,2 0,0 0,0 1,1 1)),((2 4,1 4,0.5 3.5,1 3,0 3,0 5,1 5,2 4)),((4 2,4 3,5 2,4 2)),((4 2,4 1,3 1,4 2)),((3 3,3 4,4 4,4 3,3 3)),((4 4,4 5,5 5,5 4,4 4)))", + "MULTIPOLYGON(((3 4,4 5,4 4,3 4)),((3 1,3 0,2 0,3 1)),((3 1,1 1,1 2,2 3,3 3,3 2,2 2,3 1)),((3 1,3 2,5 2,5 1,3 1)),((2 3,0 3,0 4,1 4,2 3)),((1 3,1 2,0 2,1 3)),((1 4,1 5,2 5,1 4)))" +}; + +static std::string case_recursive_boxes_68[2] = +{ + // Needs checking blocked turns in colocated turns + "MULTIPOLYGON(((3 3,3 4,4 4,4 5,5 5,4.5 4.5,5 4,5 3,4.5 2.5,5 2,2 2,2 4,3 3)),((2 5,3 5,3 4,2 4,1 3,1 2,0 2,0 4,1 4,1 5,2 5)),((2 2,4 0,1 0,2 1,1 1,1 2,2 2)),((1 0,0 0,0 1,1 1,1 0)),((4 0,5 1,5 0,4 0)),((5 1,4 1,4 2,5 1)))", + "MULTIPOLYGON(((2 0,2 1,1 1,2 2,3 2,3 1,4 2,5 2,5 0,2 0)),((2 0,1 0,1 1,2 0)),((2 2,2 3,3 4,3 3,2 2)),((3 2,3 3,4 4,5 4,5 3,4 2,3 2)),((3 4,3 5,4 4,3 4)),((2 4,2 3,1 2,1 3,0 3,0 5,1 5,1 4,2 4)),((1 2,1 1,0 1,0 2,1 2)),((4 4,4 5,5 5,4 4)))" +}; + +static std::string case_recursive_boxes_69[2] = +{ + // Needs checking left_count instead of is_closed for decision to block untraversed rings + "MULTIPOLYGON(((3 4,3 5,4 5,3 4)),((3 4,3 2,2 2,3 1,1 1,1 2,0 2,1 3,0 3,1 4,3 4)),((3 1,4 1,4 0,3 0,3 1)),((1 1,2 0,0 0,0 1,1 1)))", + "MULTIPOLYGON(((2 4,1 4,1 3,0 3,0 5,2 5,3 4,2 4)),((3 4,3 5,4 5,4 4,3 4)),((2 1,2 0,1 0,2 1)),((2 1,1 1,1 2,2 2,2 1)),((5 1,5 0,3 0,4 1,5 1)),((4 1,3 1,3 2,4 3,5 2,4 2,4 1)),((1 3,2 3,1 2,1 3)))" +}; + +static std::string case_recursive_boxes_70[2] = +{ + // Needs checking left_count instead of is_closed for decision to block untraversed rings + "MULTIPOLYGON(((2 0,0 0,0 4,1 3,3 3,3 5,5 5,5 3,4.5 2.5,5 2,5 0,2 0),(0 1,0.5 0.5,1 1,0 1),(5 2,4 2,4.5 1.5,5 2),(4 2,3 2,3 1,4 2)),((3 4,2 3,2 4,3 4)),((0 4,0 5,1 5,1.5 4.5,2 5,2 4,0 4)))", + "MULTIPOLYGON(((2 0,0 0,0 5,5 5,5 2,4.5 1.5,5 1,5 0,2 0),(2 1,3 0,3 1,2 1),(4 4,3 3,4 3,4 4),(1 1,1 2,0 2,1 1),(4 3,4.5 2.5,5 3,4 3)))" +}; + +static std::string case_recursive_boxes_71[2] = +{ + // Needs check for self-cluster within other geometry, in intersections + "MULTIPOLYGON(((4 0,4 1,5 1,5 0,4 0)),((4 3,4 4,5 4,4 3)),((3 3,4 2,3 2,3 3)),((3 3,2 3,3 4,3 3)),((3 2,3 0,1 0,1 3,0 3,0 4,1 4,3 2),(3 2,2 2,2 1,3 2)),((1 4,1 5,2 5,2 4,1 4)))", + "MULTIPOLYGON(((3 0,3 1,4 0,3 0)),((2 2,0 2,0 3,1 3,2 2)),((2 2,2 3,3 3,2 2)),((2 4,0 4,1 5,3 5,3 4,2 4)),((3 3,3 4,4 5,5 4,4 4,4 3,3 3)),((4 3,5 3,4 2,4 3)))" +}; + +static std::string case_recursive_boxes_72[2] = +{ + // Needs selection of ranked point in union (to finish the ring) + "MULTIPOLYGON(((3 1,4 1,4 0,3 0,3 1)),((1 0,1 1,2 1,2 0,1 0)),((3 3,3 5,5 5,5 2,4 2,4 3,3 3)),((3 3,2 2,2 3,3 3)),((1 3,0 2,0 3,1 3)),((1 4,2 4,2 3,1 2,1 4)),((1 4,1 5,2 5,1 4)),((2 5,3 5,2 4,2 5)),((1 5,0 4,0 5,1 5)))", + "MULTIPOLYGON(((4 0,4 1,5 0,4 0)),((4 4,4 5,5 5,5 4,4 4)),((2 4,2 3,1 3,1 4,2 4)),((2 4,2 5,3 5,2 4)),((2 2,2 3,3 3,2 2)),((2 2,3 2,3 1,2 1,2 2)),((3 3,4 2,3 2,3 3)),((3 1,3 0,2 0,3 1)),((1 3,2 2,1 1,1 3)),((1 2,0 2,0 3,1 2)),((1 4,0 4,0 5,1 5,1 4)),((4 2,4 3,5 2,4 2)))" +}; + +static std::string case_recursive_boxes_73[2] = +{ + // Needs handling connection points identical for clustered/non-clustered turns + "MULTIPOLYGON(((3 5,5 5,5 4,4.5 3.5,5 3,5 2,3 2,3 1,4 0,0 0,0 5,1 4,2 4,2 5,3 5),(2 3,2 2,3 3,2 3),(1 2,0 2,1 1,1 2)),((1 4,1 5,2 5,1 4)),((4 0,4 1,5 1,5 0,4 0)))", + "MULTIPOLYGON(((2 0,0 0,0 5,1 5,2 4,2 5,4 5,4.5 4.5,5 5,5 4,4 4,4 3,5 3,5 0,2 0),(0 2,0.5 1.5,1 2,0 2),(1 4,2 3,2 4,1 4)))" +}; + +static std::string case_recursive_boxes_74[2] = +{ + // Needs another method to find isolated regions (previous attempted used parents, that is dropped now) + "MULTIPOLYGON(((3 5,5 5,5 0,0 0,0 5,3 5),(2 3,1.5 2.5,2 2,3 3,2 3),(3 3,4 2,4 3,3 3),(2 0,3 1,2 1,2 0)))", + "MULTIPOLYGON(((2 0,0 0,0 5,1 5,2 4,2 5,5 5,5 0,2 0),(2 1,2.5 0.5,3 1,2 1),(2 3,3 3,3 4,2 4,2 3),(3 1,4 1,5 2,3 2,3 1)))" +}; + +static std::string case_recursive_boxes_75[2] = +{ + // Needs intersection pattern 6 (skip all isolated ranks in between) + "MULTIPOLYGON(((3 1,2 1,1 0,0 0,0 5,5 5,5 3,4 2,5 2,5 0,3 0,3 1),(2 3,1 3,2 2,2 3),(3 4,3.5 3.5,4 4,3 4),(3 3,3 2,4 2,4 3,3 3)))", + "MULTIPOLYGON(((3 5,4 5,4.5 4.5,5 5,5 0,4 0,4 1,2 1,2 2,1 2,1 3,0 2,0 5,1 5,0.5 4.5,1 4,2 5,3 5),(3 4,3 3,4 4,3 4),(3 3,4 2,4 3,3 3)),((2 1,2 0,1 0,1 1,2 1)),((0 2,1 2,0.5 1.5,1 1,0 1,0 2)),((1 0,0 0,0 1,1 0)))" +}; + +static std::string case_recursive_boxes_76[2] = +{ + // Needs considering ix/ix turns (opposite in both directions) as the same region + "MULTIPOLYGON(((3 5,3 4,4 5,4 4,5 4,5 0,0 0,0 4,1 4,1 5,3 5),(3 1,2 1,2.5 0.5,3 1),(1 2,0 2,0.5 1.5,1 2)))", + "MULTIPOLYGON(((2 0,0 0,0 3,1 4,0 4,0 5,2 5,2 4,3 4,4 5,5 5,5 0,2 0),(3 1,4 0,4 1,3 1),(3 2,2 1,3 1,3 2),(3 3,3 4,2 3,3 3),(2 1,1 1,1.5 0.5,2 1),(4 3,4 4,3 4,4 3)))" +}; + +static std::string case_recursive_boxes_77[2] = +{ + // Needs discarding self i/u turn traveling to itself (for intersection only) + "MULTIPOLYGON(((1 2,2 3,2 2,1 1,1 2)),((1 4,1 5,2 5,2 4,1 4)),((1 4,0 3,0 4,1 4)),((4 2,4 1,3 1,3 3,4 2)),((3 1,3 0,1 0,2 1,2.5 0.5,3 1)),((3 3,3 5,4 5,4 3,3 3)))", + "MULTIPOLYGON(((3 4,3 5,4 5,4 4,3 4)),((3 1,3 0,2 0,3 1)),((3 1,2 1,2 2,3 2,3 1)),((2 3,2 4,3 3,2 3)),((2 1,1 0,0 0,1 1,2 1)),((1 4,2 3,2 2,0 2,0 4,1 4),(2 3,1 3,1.5 2.5,2 3)),((1 4,1 5,2 5,2 4,1 4)),((4 1,5 0,4 0,4 1)),((4 1,4 2,5 1,4 1)),((4 2,5 3,5 2,4 2)))" +}; + +static std::string case_recursive_boxes_78[2] = +{ + // Needs checking intersection/right_count in overlay, as was already done for union + "MULTIPOLYGON(((3 2,1 2,1 4,3 2)),((3 2,3 4,4 4,3.5 3.5,4.5 2.5,5 3,5 1,4 1,3 0,3 2),(4 2,3.5 1.5,4 1,4 2)),((1 2,1 1,0 0,0 2,1 2)),((0 4,1 3,0 3,0 4)),((0 4,1 5,1.5 4.5,2 5,3 5,3 4,0 4)),((1 1,2 1,1 0,1 1)),((4 1,5 0,4 0,4 1)),((4 3,4 5,5 4,5 3,4 3)))", + "MULTIPOLYGON(((2 4,1 4,1 5,3 5,2 4)),((2 4,4 4,4 3,5 3,5 2,4 1,4 2,3 2,3 3,2 2,2 4)),((2 2,2 1,0 1,0 4,1 3,1 4,2 3,1 2,2 2)),((1 4,0 4,0 5,1 4)),((0 1,1 0,0 0,0 1)),((4 1,3 1,3 2,4 1)))" +}; + +static std::string case_recursive_boxes_79[2] = +{ + // Found by bug in discard_self_turns_which_loop, it needs only checking intersection and not union + "MULTIPOLYGON(((2 3,2 2,1 2,1 4,4 4,4 3,2 3)),((2 0,0 0,0 2,1 2,1.5 1.5,2 2,2 1,1 1,2 0)),((2 0,2 1,3 2,4 2,3.5 1.5,4 1,4 0,2 0),(2 1,2.5 0.5,3 1,2 1)))", + "MULTIPOLYGON(((2 0,1 0,1 1,0 1,0 3,1 4,4 4,4 0,2 0),(2 2,1 2,1 1,2 2),(4 3,3 3,3 2,4 3)))" +}; + +static std::string case_recursive_boxes_80[2] = +{ + // Creates very small interior ring (~0) for union. This is a robustness + // problem, it should not be generated. The intersection point is a tiny + // distance away from real IP, and therefore it generates a correct + // interior ring, and is considered as valid. But if you combine this + // resulting union later with other polygons, with another rescaling model, + // it most probably will be invalid. + // These cases are found with recursive_polygons and size=4. + // For size=5 the scaling is such that it does not occur (so often) + // It needs removing the rescaling. + "MULTIPOLYGON(((3.5 2.5,4 3,4 2,3 2,3 3,3.5 2.5)))", + "MULTIPOLYGON(((1 1,1 2,2 1,1 1)),((3 2,3 3,4 3,3 2)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", @@ -1220,6 +1376,13 @@ static std::string ticket_12752[2] = }; +// Ticket for validity, input is CCW +static std::string ticket_12503[2] = + { + "MULTIPOLYGON (((15 17, 12 23, 15 20, 15 17)), ((35 23, 34 23, 34 24, 35 25, 36 25, 35 23)), ((8 6, 7 24, 10 25, 12 25, 12 23, 11 23, 10 13, 15 15, 8 6)), ((12 27, 8 31, 6 32, 6 38, 13 34, 13 31, 11 31, 12 30, 11 30, 12 29, 12 27)), ((7 24, 7 26, 6 31, 9 26, 7 24)), ((18 44, 15 45, 15 48, 18 44)), ((26 34, 18 44, 38 39, 26 34)), ((15 33, 13 34, 15 45, 15 33)), ((15 32, 15 33, 17 32, 15 32)), ((19 30, 17 32, 18 32, 16 38, 21 31, 19 30)), ((15 29, 13 30, 13 31, 15 32, 15 29)), ((15 28, 15 29, 17 29, 15 28)), ((14 27, 12 29, 13 30, 15 28, 14 27)), ((30 24, 25 24, 24 26, 25 27, 24 27, 23 28, 19 28, 19 29, 17 29, 18 30, 19 30, 22 29, 21 31, 26 34, 31 27, 28 30, 26 27, 30 24)), ((15 26, 15 28, 17 26, 15 26)), ((27 27, 31 27, 34 27, 32 26, 27 27)), ((19 25, 17 26, 19 26, 19 25)), ((41 15, 33 18, 35 23, 41 15)), ((23 24, 20 25, 19 26, 24 27, 24 26, 23 24)), ((33 15, 46 5, 48 4, 49 1, 32 13, 33 15)), ((32 23, 31 24, 32 25, 32 26, 33 25, 32 23)), ((35 23, 43 23, 44 23, 44 22, 43 22, 42 15, 35 23)), ((43 23, 36 25, 38 31, 35 25, 33 25, 34 27, 39 34, 40 39, 38 39, 44 42, 43 23)), ((48 22, 44 23, 48 46, 48 22)), ((18 11, 23 2, 15 3, 18 11)), ((29 17, 26 20, 22 23, 23 24, 25 24, 27 21, 28 20, 30 19, 29 17)), ((22 19, 21 20, 21 21, 24 19, 22 19)), ((31 23, 34 23, 31 19, 30 19, 31 22, 27 21, 30 24, 31 24, 31 23)), ((21 18, 20 21, 21 20, 21 18)), ((14 27, 15 26, 16 25, 19 25, 20 24, 22 23, 21 21, 20 21, 17 17, 15 20, 15 25, 12 25, 12 26, 14 26, 14 27), (20 21, 20 22, 17 24, 20 21)), ((22 17, 22 19, 23 18, 22 17)), ((30 15, 32 13, 31 10, 28 13, 30 15)), ((16 16, 17 17, 18 17, 16 16)), ((15 15, 15 17, 16 16, 15 15)), ((30 15, 29 16, 29 17, 30 17, 30 15)), ((33 15, 30 17, 31 19, 33 18, 33 15)), ((43 14, 44 22, 48 22, 48 4, 47 7, 42 13, 43 14)), ((43 14, 42 14, 41 15, 42 15, 43 14)), ((27 10, 25 6, 23 13, 27 11, 49 1, 24 2, 27 10)), ((28 13, 23 18, 24 19, 29 16, 28 13)), ((15 11, 15 15, 16 15, 17 13, 15 11)), ((18 11, 17 13, 19 15, 18 17, 21 18, 21 17, 20 15, 22 17, 23 13, 20 14, 18 11)), ((8 6, 8 5, 15 11, 15 3, 5 3, 8 6)))", + "MULTIPOLYGON(((13 18,18 18,18 23,13 23,13 18)))" + }; + static std::string bug_21155501[2] = { "MULTIPOLYGON(((-8.3935546875 27.449790329784214,4.9658203125 18.729501999072138,11.8212890625 23.563987128451217,9.7119140625 25.48295117535531,9.8876953125 31.728167146023935,8.3056640625 32.99023555965106,8.5693359375 37.16031654673677,-1.8896484375 35.60371874069731,-0.5712890625 32.02670629333614,-8.9208984375 29.458731185355344,-8.3935546875 27.449790329784214)))", @@ -1260,4 +1423,10 @@ static std::string mysql_23023665_12[2] = "MULTIPOLYGON(((2 3,-3 5,-10 -1,2 3)))" }; +static std::string mysql_regression_1_65_2017_08_31[2] = + { + "MULTIPOLYGON(((23.695652173913043 4.3478260869565215,23.333333333333336 4.166666666666667,25 0,23.695652173913043 4.3478260869565215)),((10 15,0 15,8.870967741935484 9.67741935483871,10.777777750841748 14.44444437710437,10 15)))", + "MULTIPOLYGON(((10 15,20 15,15 25,10 15)),((10 15,0 15,7 10,5 0,15 5,15.90909090909091 4.545454545454546,17 10,10 15)),((23.695652173913043 4.3478260869565215,20 2.5,25 0,23.695652173913043 4.3478260869565215)))", + }; + #endif // BOOST_GEOMETRY_TEST_MULTI_OVERLAY_CASES_HPP diff --git a/test/algorithms/overlay/overlay.cpp b/test/algorithms/overlay/overlay.cpp index 8aeaa8fb2..795325d92 100644 --- a/test/algorithms/overlay/overlay.cpp +++ b/test/algorithms/overlay/overlay.cpp @@ -88,7 +88,7 @@ struct map_visitor m_mapper.map(turn.point, "fill:rgb(0,128,255);" // Blueish "stroke:rgb(0,0,0);stroke-width:1", 3); break; - case 2 : + case 3 : label_turn(index, turn); break; } @@ -246,10 +246,15 @@ struct map_visitor result = true; } } - } - if (! turn.operations[index].enriched.startable) - { - os << "$"; + + os << " {" << turn.operations[index].enriched.region_id + << (turn.operations[index].enriched.isolated ? " ISO" : "") + << "}"; + + if (! turn.operations[index].enriched.startable) + { + os << "$"; + } } return result; @@ -271,17 +276,33 @@ struct map_visitor { out << "#"; } - - std::string font8 = "font-family:Arial;font-size:8px"; - std::string font6 = "font-family:Arial;font-size:6px"; - std::string style = "fill:rgb(0,0,255);" + font8; - if (turn.operations[0].seg_id.source_index == turn.operations[1].seg_id.source_index) + if (turn.discarded) { - style = "fill:rgb(255,0,255);" + font8; + out << "!"; } - else if (turn.colocated) + if (turn.has_colocated_both) { - style = "fill:rgb(255,0,0);" + font8; + out << "+"; + } + bool const self_turn = bg::detail::overlay::is_self_turn(turn); + if (self_turn) + { + out << "@"; + } + + std::string font8 = "font-family:Arial;font-size:6px"; + std::string font6 = "font-family:Arial;font-size:4px"; + std::string style = "fill:rgb(0,0,255);" + font8; + if (self_turn) + { + if (turn.discarded) + { + style = "fill:rgb(128,28,128);" + font6; + } + else + { + style = "fill:rgb(255,0,255);" + font8; + } } else if (turn.discarded) { @@ -303,7 +324,7 @@ struct map_visitor void add_text(Turn const& turn, std::string const& text, std::string const& style) { int const margin = 5; - int const lineheight = 8; + int const lineheight = 6; double const half = 0.5; double const ten = 10; diff --git a/test/algorithms/overlay/relative_order.cpp b/test/algorithms/overlay/relative_order.cpp index e26f39066..8d2a5b47a 100644 --- a/test/algorithms/overlay/relative_order.cpp +++ b/test/algorithms/overlay/relative_order.cpp @@ -31,6 +31,9 @@ # include #endif +#include +#include + template void test_with_point(std::string const& /*caseid*/, @@ -46,7 +49,12 @@ void test_with_point(std::string const& /*caseid*/, P si = bg::make

(si_x, si_y); P sj = bg::make

(sj_x, sj_y); - int order = bg::detail::overlay::get_relative_order

::apply(pi, pj, ri, rj, si, sj); + typedef typename bg::strategy::side::services::default_strategy + < + typename bg::cs_tag

::type + >::type strategy_type; + + int order = bg::detail::overlay::get_relative_order::apply(pi, pj, ri, rj, si, sj, strategy_type()); BOOST_CHECK_EQUAL(order, expected_order); diff --git a/test/algorithms/overlay/select_rings.cpp b/test/algorithms/overlay/select_rings.cpp index b1ad67850..af43328f5 100644 --- a/test/algorithms/overlay/select_rings.cpp +++ b/test/algorithms/overlay/select_rings.cpp @@ -1,6 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 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) @@ -36,7 +41,11 @@ template void test_geometry(std::string const& wkt1, std::string const& wkt2, RingIdVector const& expected_ids) { - typedef bg::detail::overlay::ring_properties::type> properties; + typedef bg::detail::overlay::ring_properties + < + typename bg::point_type::type, + double + > properties; Geometry1 geometry1; Geometry2 geometry2; diff --git a/test/algorithms/overlay/sort_by_side.cpp b/test/algorithms/overlay/sort_by_side.cpp index 5635d46f6..b311dccc7 100644 --- a/test/algorithms/overlay/sort_by_side.cpp +++ b/test/algorithms/overlay/sort_by_side.cpp @@ -49,12 +49,14 @@ template typename Turns, typename Clusters, typename Geometry1, - typename Geometry2 + typename Geometry2, + typename SideStrategy > std::vector test_gather_cluster_properties(std::string const& case_id, Clusters& clusters, Turns& turns, bg::detail::overlay::operation_type for_operation, - Geometry1 const& geometry1, Geometry2 const& geometry2) + Geometry1 const& geometry1, Geometry2 const& geometry2, + SideStrategy const& strategy) { using namespace boost::geometry; using namespace boost::geometry::detail::overlay; @@ -69,7 +71,7 @@ std::vector test_gather_cluster_properties(std::string const& case_ // right side typedef sort_by_side::side_sorter < - Reverse1, Reverse2, OverlayType, point_type, std::less + Reverse1, Reverse2, OverlayType, point_type, SideStrategy, std::less > sbs_type; for (typename Clusters::iterator mit = clusters.begin(); @@ -82,7 +84,7 @@ std::vector test_gather_cluster_properties(std::string const& case_ return result; } - sbs_type sbs; + sbs_type sbs(strategy); point_type turn_point; // should be all the same for all turns in cluster bool first = true; @@ -165,7 +167,7 @@ std::vector apply_overlay(std::string const& case_id, // Gather cluster properties, with test option return test_gather_cluster_properties(case_id, clusters, turns, bg::detail::overlay::operation_from_overlay::value, - geometry1, geometry2); + geometry1, geometry2, strategy.get_side_strategy()); } diff --git a/test/algorithms/overlay/sort_by_side_basic.cpp b/test/algorithms/overlay/sort_by_side_basic.cpp index 877de7c50..15c85c285 100644 --- a/test/algorithms/overlay/sort_by_side_basic.cpp +++ b/test/algorithms/overlay/sort_by_side_basic.cpp @@ -89,13 +89,14 @@ std::vector apply_get_turns(std::string const& case_id, // Define sorter, sorting counter-clockwise such that polygons are on the // right side + typedef typename Strategy::side_strategy_type side_strategy; typedef bg::detail::overlay::sort_by_side::side_sorter < false, false, overlay_union, - point_type, std::less + point_type, side_strategy, std::less > sbs_type; - sbs_type sbs; + sbs_type sbs(strategy.get_side_strategy()); std::cout << "Case: " << case_id << std::endl; @@ -187,7 +188,7 @@ std::vector apply_get_turns(std::string const& case_id, } else { - BOOST_CHECK_MESSAGE(right_count[rank] == ranked_point.count_right, + BOOST_CHECK_MESSAGE(right_count[rank] == int(ranked_point.count_right), " caseid=" << case_id << " ranks: conflict in right_count=" << ranked_point.count_right << " vs " << right_count[rank]); diff --git a/test/algorithms/relational_operations/covered_by/covered_by_sph.cpp b/test/algorithms/relational_operations/covered_by/covered_by_sph.cpp index 710742fd1..a80fb2390 100644 --- a/test/algorithms/relational_operations/covered_by/covered_by_sph.cpp +++ b/test/algorithms/relational_operations/covered_by/covered_by_sph.cpp @@ -229,8 +229,11 @@ void test_point_polygon() "POLYGON((30 0,30 30,90 30, 90 0, 30 0))", false); // extended - test_geometry("POINT(0 -90)", - "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + test_geometry("POINT(0 90)", + "POLYGON((0 80, 0 81, -90 80, -180 80, 90 80, 0 80))", + true); + test_geometry("POINT(0 90)", + "POLYGON((0 80, -90 80, -90 81, -180 80, 90 80, 0 80))", true); test_geometry("POINT(0 89)", "POLYGON((0 80,-90 80, -180 80, 90 80, 0 80))", @@ -238,6 +241,50 @@ void test_point_polygon() test_geometry("POINT(-180 89)", "POLYGON((0 80,-90 80, -180 80, 90 80, 0 80))", true); + test_geometry("POINT(0 -90)", + "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + true); + test_geometry("POINT(0 -89)", + "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + true); + test_geometry("POINT(1 -90)", + "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + true); + test_geometry("POINT(1 -89)", + "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + true); + test_geometry("POINT(1 90)", + "POLYGON((0 80,-90 80, -180 80, 90 80, 0 80))", + true); + test_geometry("POINT(1 90)", + "POLYGON((0 80,-90 80, -180 80, 90 80, 0 80))", + true); + + + + // MySQL report 08.2017 + test_geometry("POINT(-179 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false); + // extended + test_geometry("POINT(179 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false); + test_geometry("POINT(180 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false); + test_geometry("POINT(-179 0)", + "POLYGON((-10 -10, -10 10, 10 10, 10 -10, -10 10))", + false); + test_geometry("POINT(179 0)", + "POLYGON((-10 -10, -10 10, 10 10, 10 -10, -10 10))", + false); + test_geometry("POINT(-179 0)", + "POLYGON((0 0, 0 1, 1 0, 0 -1, 0 0))", + false); + test_geometry("POINT(179 0)", + "POLYGON((0 0, 0 1, 1 0, 0 -1, 0 0))", + false); } diff --git a/test/algorithms/relational_operations/covered_by/covered_by_sph_geo.cpp b/test/algorithms/relational_operations/covered_by/covered_by_sph_geo.cpp index b46b6f4e9..6c8d5853b 100644 --- a/test/algorithms/relational_operations/covered_by/covered_by_sph_geo.cpp +++ b/test/algorithms/relational_operations/covered_by/covered_by_sph_geo.cpp @@ -79,12 +79,42 @@ void test_box_box() test_geometry("BOX(-179.9 1, -177.872408 1)", "BOX(179.08882 0, 182.127592 2)", true); } +template +void test_point_polygon() +{ + typename boost::mpl::if_ + < + boost::is_same::type, bg::geographic_tag>, + bg::strategy::within::geographic_winding

, + bg::strategy::within::spherical_winding

+ >::type s; + + typedef bg::model::polygon

poly; + + // MySQL report 08.2017 + test_geometry("POINT(-179 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false); + test_geometry("POINT(-179 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false, + s); + + test_geometry("POINT(1 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + true); + test_geometry("POINT(1 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + true, + s); +} template void test_cs() { test_point_box

(); test_box_box

(); + test_point_polygon

(); } diff --git a/test/algorithms/relational_operations/covered_by/test_covered_by.hpp b/test/algorithms/relational_operations/covered_by/test_covered_by.hpp index 4622e80ec..e22ed03fd 100644 --- a/test/algorithms/relational_operations/covered_by/test_covered_by.hpp +++ b/test/algorithms/relational_operations/covered_by/test_covered_by.hpp @@ -88,6 +88,20 @@ void test_geometry(std::string const& wkt1, check_geometry(v1, v2, wkt1, wkt2, expected, no_strategy()); } +template +void test_geometry(std::string const& wkt1, + std::string const& wkt2, + bool expected, + Strategy const& strategy) +{ + Geometry1 geometry1; + Geometry2 geometry2; + bg::read_wkt(wkt1, geometry1); + bg::read_wkt(wkt2, geometry2); + + check_geometry(geometry1, geometry2, wkt1, wkt2, expected, strategy); +} + /* template diff --git a/test/algorithms/relational_operations/disjoint/disjoint_sph.cpp b/test/algorithms/relational_operations/disjoint/disjoint_sph.cpp index cee4c8d16..d9a21fdad 100644 --- a/test/algorithms/relational_operations/disjoint/disjoint_sph.cpp +++ b/test/algorithms/relational_operations/disjoint/disjoint_sph.cpp @@ -188,6 +188,21 @@ void test_linestring_linestring() test_geometry("LINESTRING(1 0,2 2,2 3)", "LINESTRING(0 0, 2 2, 3 2)", false); } +//https://svn.boost.org/trac10/ticket/13057 +template +void test_linestring_linestring_radians() +{ + typedef bg::model::linestring

ls; + + test_geometry("LINESTRING(0 -0.31415926535897897853,\ + 0.26179938779914918578 0,\ + -0.034906585039886556254 0.13962634015954622502,\ + -0.12217304763960294689 0.12217304763960294689)",\ + "LINESTRING(-0.034906585039886556254 0.13962634015954622502,\ + -0.26179938779914918578 0)", false); + +} + template void test_linestring_multi_linestring() { @@ -255,6 +270,9 @@ void test_all() test_linestring_multi_linestring

(); test_multi_linestring_multi_linestring

(); + test_linestring_linestring_radians > >(); + test_point_polygon

(); } diff --git a/test/algorithms/relational_operations/within/within_pointlike_geometry.cpp b/test/algorithms/relational_operations/within/within_pointlike_geometry.cpp index 3a00bb41a..523e183c0 100644 --- a/test/algorithms/relational_operations/within/within_pointlike_geometry.cpp +++ b/test/algorithms/relational_operations/within/within_pointlike_geometry.cpp @@ -154,6 +154,22 @@ void test_spherical_geographic() { bg::model::polygon wrangel; + typename boost::mpl::if_ + < + boost::is_same::type, bg::geographic_tag>, + bg::strategy::within::geographic_winding, + bg::strategy::within::spherical_winding + >::type ws; + + typename boost::mpl::if_ + < + boost::is_same::type, bg::geographic_tag>, + bg::strategy::side::geographic<>, + bg::strategy::side::spherical_side_formula<> + >::type ss; + + boost::ignore_unused(ws, ss); + // SQL Server check (no geography::STWithin, so check with intersection trick) /* @@ -189,9 +205,9 @@ void test_spherical_geographic() bg::model::polygon triangle; bg::read_wkt("POLYGON((5 52,9 53,7 50,5 52))", triangle); - BOOST_CHECK_EQUAL(bg::within(Point(7, 52.5), triangle), true); - BOOST_CHECK_EQUAL(bg::within(Point(8.0, 51.5), triangle), false); - BOOST_CHECK_EQUAL(bg::within(Point(6.0, 51.0), triangle), false); + BOOST_CHECK_EQUAL(bg::within(Point(7, 52.5), triangle, ws), true); + BOOST_CHECK_EQUAL(bg::within(Point(8.0, 51.5), triangle, ws), false); + BOOST_CHECK_EQUAL(bg::within(Point(6.0, 51.0), triangle, ws), false); // northern hemisphere { @@ -199,11 +215,10 @@ void test_spherical_geographic() bg::read_wkt("POLYGON((10 50,30 50,30 40,10 40, 10 50))", poly_n); Point pt_n1(20, 50.00001); Point pt_n2(20, 40.00001); - typedef bg::strategy::side::spherical_side_formula<> ssf; - BOOST_CHECK_EQUAL(ssf::apply(poly_n.outer()[0], poly_n.outer()[1], pt_n1), -1); // right of segment - BOOST_CHECK_EQUAL(ssf::apply(poly_n.outer()[2], poly_n.outer()[3], pt_n2), 1); // left of segment - BOOST_CHECK_EQUAL(bg::within(pt_n1, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n2, poly_n), false); + BOOST_CHECK_EQUAL(ss.apply(poly_n.outer()[0], poly_n.outer()[1], pt_n1), -1); // right of segment + BOOST_CHECK_EQUAL(ss.apply(poly_n.outer()[2], poly_n.outer()[3], pt_n2), 1); // left of segment + BOOST_CHECK_EQUAL(bg::within(pt_n1, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n2, poly_n, ws), false); } // southern hemisphere { @@ -211,11 +226,10 @@ void test_spherical_geographic() bg::read_wkt("POLYGON((10 -40,30 -40,30 -50,10 -50, 10 -40))", poly_s); Point pt_s1(20, -40.00001); Point pt_s2(20, -50.00001); - typedef bg::strategy::side::spherical_side_formula<> ssf; - BOOST_CHECK_EQUAL(ssf::apply(poly_s.outer()[0], poly_s.outer()[1], pt_s1), 1); // left of segment - BOOST_CHECK_EQUAL(ssf::apply(poly_s.outer()[2], poly_s.outer()[3], pt_s2), -1); // right of segment - BOOST_CHECK_EQUAL(bg::within(pt_s1, poly_s), false); - BOOST_CHECK_EQUAL(bg::within(pt_s2, poly_s), true); + BOOST_CHECK_EQUAL(ss.apply(poly_s.outer()[0], poly_s.outer()[1], pt_s1), 1); // left of segment + BOOST_CHECK_EQUAL(ss.apply(poly_s.outer()[2], poly_s.outer()[3], pt_s2), -1); // right of segment + BOOST_CHECK_EQUAL(bg::within(pt_s1, poly_s, ws), false); + BOOST_CHECK_EQUAL(bg::within(pt_s2, poly_s, ws), true); } // crossing antimeridian, northern hemisphere { @@ -229,14 +243,14 @@ void test_spherical_geographic() Point pt_n22(-180, 40.00001); Point pt_n23(179, 40.00001); Point pt_n24(-179, 40.00001); - BOOST_CHECK_EQUAL(bg::within(pt_n11, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n12, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n13, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n14, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n21, poly_n), false); - BOOST_CHECK_EQUAL(bg::within(pt_n22, poly_n), false); - BOOST_CHECK_EQUAL(bg::within(pt_n23, poly_n), false); - BOOST_CHECK_EQUAL(bg::within(pt_n24, poly_n), false); + BOOST_CHECK_EQUAL(bg::within(pt_n11, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n12, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n13, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n14, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n21, poly_n, ws), false); + BOOST_CHECK_EQUAL(bg::within(pt_n22, poly_n, ws), false); + BOOST_CHECK_EQUAL(bg::within(pt_n23, poly_n, ws), false); + BOOST_CHECK_EQUAL(bg::within(pt_n24, poly_n, ws), false); } // TODO: Move to covered_by tests @@ -246,22 +260,22 @@ void test_spherical_geographic() bg::model::polygon poly_n1; bg::read_wkt("POLYGON((-90 80,90 80,90 70,-90 70, -90 80))", poly_n1); // Points on segment - BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 85), poly_n1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 85), poly_n1), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 85), poly_n1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 85), poly_n1, ws), true); // Points on pole - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 90), poly_n1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(0, 90), poly_n1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(45, 90), poly_n1), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 90), poly_n1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(0, 90), poly_n1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(45, 90), poly_n1, ws), true); } // Segment going through pole { bg::model::polygon poly_n2; bg::read_wkt("POLYGON((-90 80,90 70,0 70,-90 80))", poly_n2); // Points on segment - BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 85), poly_n2), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 75), poly_n2), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 85), poly_n2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 75), poly_n2, ws), true); // Points outside but on the same level as segment - BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 75), poly_n2), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 75), poly_n2, ws), false); } // Possibly invalid, 2-segment polygon with segment going through pole /*{ @@ -279,27 +293,75 @@ void test_spherical_geographic() { bg::model::polygon poly_n3; bg::read_wkt("POLYGON((45 90,45 80,0 80,45 90))", poly_n3); - BOOST_CHECK_EQUAL(bg::covered_by(Point(0, 85), poly_n3), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(45, 85), poly_n3), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(0, 85), poly_n3, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(45, 85), poly_n3, ws), true); } // Segment going through pole { bg::model::polygon poly_s1; bg::read_wkt("POLYGON((-90 -80,-90 -70,90 -70,90 -80,-90 -80))", poly_s1); // Points on segment - BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, -85), poly_s1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, -85), poly_s1), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, -85), poly_s1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, -85), poly_s1, ws), true); // Points on pole - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, -90), poly_s1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(0, -90), poly_s1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(45, -90), poly_s1), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, -90), poly_s1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(0, -90), poly_s1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(45, -90), poly_s1, ws), true); } // Segment endpoints on pole with arbitrary longitudes { bg::model::polygon poly_s2; bg::read_wkt("POLYGON((45 -90,0 -80,45 -80,45 -90))", poly_s2); - BOOST_CHECK_EQUAL(bg::covered_by(Point(0, -85), poly_s2), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(45, -85), poly_s2), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(0, -85), poly_s2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(45, -85), poly_s2, ws), true); + } + // Polygon covering nearly half of the globe but no poles + { + bg::model::polygon poly_h1; + bg::read_wkt("POLYGON((170 0, 170 -80,10 -80,0 -80,0 -20,10 -20,10 20,0 20,0 80,10 80,170 80,170 0))", poly_h1); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 90), poly_h1, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 85), poly_h1, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 50), poly_h1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 0), poly_h1, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -50), poly_h1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -85), poly_h1, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -90), poly_h1, ws), false); + } + // Polygon covering more than half of the globe with both holes + { + bg::model::polygon poly_h2; + bg::read_wkt("POLYGON((180 0, 180 -80,0 -80,10 -80,10 -20,0 -20,0 20,10 20,10 80,0 80,180 80,180 0))", poly_h2); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 90), poly_h2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 85), poly_h2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 50), poly_h2, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 0), poly_h2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -50), poly_h2, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -85), poly_h2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -90), poly_h2, ws), true); + } + // Polygon covering around half of the globe covering south pole + { + bg::model::polygon poly_h3; + bg::read_wkt("POLYGON((180 0, 180 -80,0 -80,0 -20,10 -20,10 20,0 20,0 80,10 80,170 80,180 0))", poly_h3); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 90), poly_h3, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 85), poly_h3, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 50), poly_h3, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 0), poly_h3, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -50), poly_h3, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -85), poly_h3, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -90), poly_h3, ws), true); + } + // Polygon covering around half of the globe covering north pole + { + bg::model::polygon poly_h4; + bg::read_wkt("POLYGON((180 0, 170 -80,10 -80,10 -20,0 -20,0 20,10 20,10 80,0 80,180 80,180 0))", poly_h4); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 90), poly_h4, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 85), poly_h4, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 50), poly_h4, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 0), poly_h4, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -50), poly_h4, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -85), poly_h4, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -90), poly_h4, ws), false); } } diff --git a/test/algorithms/set_operations/check_validity.hpp b/test/algorithms/set_operations/check_validity.hpp new file mode 100644 index 000000000..3e81538dc --- /dev/null +++ b/test/algorithms/set_operations/check_validity.hpp @@ -0,0 +1,50 @@ +// Boost.Geometry + +// Copyright (c) 2017 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_TEST_SETOP_CHECK_VALIDITY_HPP +#define BOOST_GEOMETRY_TEST_SETOP_CHECK_VALIDITY_HPP + +#include + +#include + +template +< + typename Geometry, + typename Tag = typename bg::tag::type +> +struct check_validity +{ + static inline + bool apply(Geometry const& geometry, std::string& message) + { + return bg::is_valid(geometry, message); + } +}; + +// Specialization for vector of (e.g. rings) +template +struct check_validity +{ + static inline + bool apply(Geometry const& geometry, std::string& message) + { + typedef typename boost::range_value::type single_type; + BOOST_FOREACH(single_type const& element, geometry) + { + if (! bg::is_valid(element, message)) + { + return false; + } + } + return true; + } +}; + + +#endif // BOOST_GEOMETRY_TEST_SETOP_CHECK_VALIDITY_HPP diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index 00983708f..e1a811676 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -41,10 +41,13 @@ ( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \ clips3, -1, area1 + area2) +#if !defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS) #define TEST_DIFFERENCE_IGNORE(caseid, clips1, area1, clips2, area2, clips3) \ + { ut_settings ignore_validity; ignore_validity.test_validity = false; \ (test_one) \ ( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \ - clips3, -1, area1 + area2, ignore_validity) + clips3, -1, area1 + area2, ignore_validity); } +#endif template void test_all() @@ -55,9 +58,6 @@ void test_all() typedef typename bg::coordinate_type

::type ct; - ut_settings ignore_validity; - ignore_validity.test_validity = false; - ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) sym_settings.sym_difference = false; @@ -211,8 +211,7 @@ void test_all() test_one("case_80", case_80[0], case_80[1], 1, 9, 44.5, - 1, 10, 84.5, - ignore_validity); + 1, 10, 84.5); #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS // Fails, holes are not subtracted @@ -371,10 +370,18 @@ void test_all() } #if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) - test_one("ggl_list_20110820_christophe", - ggl_list_20110820_christophe[0], ggl_list_20110820_christophe[1], - 1, -1, 2.8570121719168924, - 1, -1, 64.498061986388564); + { + // symmetric difference is not valid due to robustness issue, it has + // two turns (touch_only) and a midpoint is located in other polygon + ut_settings ignore_validity; + ignore_validity.test_validity = false; + + test_one("ggl_list_20110820_christophe", + ggl_list_20110820_christophe[0], ggl_list_20110820_christophe[1], + 1, -1, 2.8570121719168924, + 1, -1, 64.498061986388564, + ignore_validity); + } #endif test_one("ggl_list_20120717_volker", @@ -540,20 +547,16 @@ void test_all() TEST_DIFFERENCE(mysql_21965285, 1, 92.0, 1, 14.0, 1); - TEST_DIFFERENCE_IGNORE(mysql_23023665_1, 1, 92.0, 1, 142.5, 2); - TEST_DIFFERENCE_IGNORE(mysql_23023665_2, 1, 96.0, 1, 16.0, 2); - TEST_DIFFERENCE_IGNORE(mysql_23023665_3, 1, 225.0, 1, 66.0, 2); + TEST_DIFFERENCE(mysql_23023665_1, 1, 92.0, 1, 142.5, 2); + TEST_DIFFERENCE(mysql_23023665_2, 1, 96.0, 1, 16.0, 2); + TEST_DIFFERENCE(mysql_23023665_3, 1, 225.0, 1, 66.0, 2); #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_DIFFERENCE(mysql_23023665_5, 2, 165.23735, 2, 105.73735, 4); #else TEST_DIFFERENCE_IGNORE(mysql_23023665_5, 1, 165.23735, 2, 105.73735, 3); #endif -#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) TEST_DIFFERENCE(mysql_23023665_6, 2, 105.68756, 3, 10.18756, 5); -#else - TEST_DIFFERENCE_IGNORE(mysql_23023665_6, 2, 105.68756, 3, 10.18756, 5); -#endif #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_DIFFERENCE(mysql_23023665_13, 3, 99.74526, 3, 37.74526, 6); @@ -585,15 +588,10 @@ void test_specific() 2, 8, 489763.5, 1, 4, 6731652.0); - // Generates spikes, both a-b and b-a #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_DIFFERENCE(ticket_11676, 2, 2537992.5, 2, 294963.5, 3); #else - - ut_settings ignore_validity; - ignore_validity.test_validity = false; - TEST_DIFFERENCE_IGNORE(ticket_11676, 1, 2537992.5, 2, 294963.5, 2); #endif } diff --git a/test/algorithms/set_operations/difference/difference_areal_linear.cpp b/test/algorithms/set_operations/difference/difference_areal_linear.cpp index c6001f11b..f29d78054 100644 --- a/test/algorithms/set_operations/difference/difference_areal_linear.cpp +++ b/test/algorithms/set_operations/difference/difference_areal_linear.cpp @@ -3,8 +3,8 @@ // Copyright (c) 2010-2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017, 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 @@ -202,6 +202,36 @@ void test_areal_linear() "LINESTRING(-1 -1,10 10)", "POLYGON((5 5,15 15,15 5,5 5))", 1, 2, 6 * std::sqrt(2.0)); + + test_one_lp("case37_1", + "LINESTRING(1 1,2 2)", + "POLYGON((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1))", + 1, 2, std::sqrt(2.0)); + + test_one_lp("case37_2", + "LINESTRING(1 1,2 2,3 3)", + "POLYGON((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1))", + 1, 2, std::sqrt(2.0)); + + test_one_lp("case38", + "LINESTRING(0 0,1 1,2 2,3 3)", + "POLYGON((0 0,0 9,9 9,9 0,0 0),(0 0,2 1,2 2,1 2,0 0))", + 1, 3, 2 * std::sqrt(2.0)); + + // several linestrings are in the output, the result is geometrically correct + // still single linestring could be generated + test_one_lp("case39", + "LINESTRING(0 0,1 1,2 2,3 3)", + "POLYGON((0 0,0 9,9 9,9 0,0 0),(0 0,2 1,2 2,1 2,0 0),(2 2,3 2,3 3,2 3,2 2))", + 2, 5, 3 * std::sqrt(2.0)); + test_one_lp("case40", + "LINESTRING(0 0,1 1,2 2,4 4)", + "POLYGON((0 0,0 9,9 9,9 0,0 0),(0 0,2 1,2 2,1 2,0 0),(2 2,3 2,3 3,2 3,2 2))", + 2, 5, 3 * std::sqrt(2.0)); + test_one_lp("case41", + "LINESTRING(0 0,1 1,2 2,9 9)", + "POLYGON((0 0,0 9,9 9,9 0,0 0),(0 0,2 1,2 2,1 2,0 0),(2 2,3 2,3 3,2 3,2 2),(7 7,8 7,9 9,7 8,7 7))", + 3, 7, 5 * std::sqrt(2.0)); } template diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index c11568838..15b3cb1bf 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -30,9 +30,10 @@ clips3, -1, area1 + area2) #define TEST_DIFFERENCE_IGNORE(caseid, clips1, area1, clips2, area2, clips3) \ + { ut_settings ignore_validity; ignore_validity.test_validity = false; \ (test_one) \ ( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \ - clips3, -1, area1 + area2, ignore_validity) + clips3, -1, area1 + area2, ignore_validity); } #define TEST_DIFFERENCE_WITH(index1, index2, caseid, clips1, area1, \ clips2, area2, clips3) \ @@ -46,14 +47,6 @@ template void test_areal() { - ut_settings ignore_validity; - ignore_validity.test_validity = false; - - ut_settings sym_settings; -#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) - sym_settings.sym_difference = false; -#endif - test_one("simplex_multi", case_multi_simplex[0], case_multi_simplex[1], 5, 21, 5.58, 4, 17, 2.58); @@ -94,7 +87,7 @@ void test_areal() test_one("case_72_multi", case_72_multi[0], case_72_multi[1], - 3, 13, 1.65, 3, 17, 6.15, ignore_validity); + 3, 13, 1.65, 3, 17, 6.15); test_one("case_77_multi", case_77_multi[0], case_77_multi[1], @@ -108,7 +101,7 @@ void test_areal() TEST_DIFFERENCE(case_123_multi, 1, 0.25, 2, 0.625, 3); TEST_DIFFERENCE(case_124_multi, 1, 0.25, 2, 0.4375, 3); - TEST_DIFFERENCE_IGNORE(case_125_multi, 1, 0.25, 2, 0.400, 3); + TEST_DIFFERENCE(case_125_multi, 1, 0.25, 2, 0.400, 3); // A should have 3 clips, B should have 5 clips #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS @@ -162,10 +155,14 @@ void test_areal() ut_settings settings; settings.percentage = 0.001; -#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + // This testcase is actually different for all combinations +#if (!defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS)) || defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + settings.test_validity = false; +#endif + +#if defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS) || defined(BOOST_GEOMETRY_NO_ROBUSTNESS) TEST_DIFFERENCE_WITH(0, 1, ggl_list_20120221_volker, 2, 7962.66, 2, 2775258.93, 4); #else - settings.test_validity = false; TEST_DIFFERENCE_WITH(0, 1, ggl_list_20120221_volker, 2, 7962.66, 1, 2775258.93, 3); #endif } @@ -178,11 +175,15 @@ void test_areal() tolerance(0.001)); // POSTGIS areas: 3.75893745345145, 2.5810000723917e-15 - test_one("bug_21155501", - bug_21155501[0], bug_21155501[1], - 1, 9, 3.758937, - 0, 0, 0.0, - ignore_validity); + TEST_DIFFERENCE_IGNORE(bug_21155501, 1, 3.758937, 0, 0.0, 1); +#endif + +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + // The result is valid but wrong, version b includes nearly all area + // which was original between all the self-touching polygons +// TEST_DIFFERENCE(ticket_12503, 46, 920.625, 41, 497.125, 10); +#else + TEST_DIFFERENCE_IGNORE(ticket_12503, 45, 920.625, 3, 7.625, 48); #endif // Areas and #clips correspond with POSTGIS (except sym case) @@ -211,6 +212,10 @@ void test_areal() TEST_DIFFERENCE(case_135_multi, 2, 2.0, 2, 13.0, 2); TEST_DIFFERENCE(case_136_multi, 2, 2.0, 3, 13.5, 3); TEST_DIFFERENCE(case_137_multi, 2, 2.5, 2, 13.0, 2); + TEST_DIFFERENCE(case_138_multi, 5, 16.6, 3, 8.225, 8); + TEST_DIFFERENCE(case_139_multi, 4, 16.328125, 3, 8.078125, 7); + TEST_DIFFERENCE(case_140_multi, 4, 16.328125, 3, 8.078125, 7); + TEST_DIFFERENCE(case_141_multi, 5, 15.5, 5, 10.0, 10); // Areas correspond with POSTGIS, // #clips in PostGIS is 11,11,5 but should most probably be be 12,12,6 @@ -315,7 +320,7 @@ void test_areal() #endif TEST_DIFFERENCE(case_recursive_boxes_35, 5, 1.75, 5, 2.75, 10); TEST_DIFFERENCE(case_recursive_boxes_36, 2, 1.0, 2, 1.5, 3); - TEST_DIFFERENCE_IGNORE(case_recursive_boxes_37, 3, 2.5, 2, 4.25, 2); + TEST_DIFFERENCE(case_recursive_boxes_37, 3, 2.5, 2, 4.25, 2); TEST_DIFFERENCE(case_recursive_boxes_38, 5, 7.75, 4, 3.5, 3); TEST_DIFFERENCE(case_recursive_boxes_39, 3, 6.0, 3, 3.0, 4); TEST_DIFFERENCE(case_recursive_boxes_40, 11, 14.0, 9, 13.0, 11); @@ -347,7 +352,11 @@ void test_areal() TEST_DIFFERENCE(case_recursive_boxes_53, 6, 3.5, 4, 1.5, 9); TEST_DIFFERENCE(case_recursive_boxes_54, 6, 6.5, 8, 6.0, 7); TEST_DIFFERENCE(case_recursive_boxes_55, 4, 5.5, 6, 7.75, 4); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_56, 4, 4.5, 5, 2.75, 6); +#else TEST_DIFFERENCE_IGNORE(case_recursive_boxes_56, 4, 4.5, 5, 2.75, 6); +#endif TEST_DIFFERENCE(case_recursive_boxes_57, 5, 3.75, 9, 6.5, 10); TEST_DIFFERENCE(case_recursive_boxes_58, 4, 2.25, 6, 3.75, 7); #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS @@ -361,13 +370,88 @@ void test_areal() #else TEST_DIFFERENCE_IGNORE(case_recursive_boxes_60, 5, 5.25, 5, 5.25, 8); #endif + TEST_DIFFERENCE(case_recursive_boxes_61, 2, 1.5, 6, 2.0, 7); +#if defined(BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS) + // Misses one triangle + TEST_DIFFERENCE(case_recursive_boxes_62, 5, 5.0, 11, 5.75, 12); +#endif - test_one("mysql_21965285_b", - mysql_21965285_b[0], - mysql_21965285_b[1], - 2, -1, 183.71376870369406, - 2, -1, 131.21376870369406, - sym_settings); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_63, 9, 10.5, 5, 27.75, 4); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_63, 6, 10.5, 5, 27.75, 2); +#endif + + TEST_DIFFERENCE(case_recursive_boxes_64, 6, 2.75, 7, 4.5, 11); + +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_65, 6, 4.25, 7, 3.0, 13); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_65, 4, 4.25, 7, 3.0, 11); +#endif + + TEST_DIFFERENCE(case_recursive_boxes_66, 5, 4.75, 7, 4.0, 9); + TEST_DIFFERENCE(case_recursive_boxes_67, 7, 6.25, 9, 6.0, 10); + TEST_DIFFERENCE(case_recursive_boxes_68, 10, 6.5, 9, 6.5, 7); + TEST_DIFFERENCE(case_recursive_boxes_69, 5, 6.25, 5, 6.75, 8); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_70, 5, 2.0, 8, 4.5, 11); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_70, 5, 2.0, 6, 4.5, 9); +#endif + +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_71, 7, 8.25, 7, 5.75, 8); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_71, 6, 8.25, 7, 5.75, 7); +#endif + + TEST_DIFFERENCE(case_recursive_boxes_72, 6, 6.5, 7, 4.0, 10); + TEST_DIFFERENCE(case_recursive_boxes_73, 4, 1.75, 5, 4.0, 8); + + TEST_DIFFERENCE(case_recursive_boxes_74, 3, 3.00, 3, 1.5, 5); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_75, 7, 4.5, 4, 2.0, 11); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_75, 5, 4.5, 4, 2.0, 9); +#endif + + TEST_DIFFERENCE(case_recursive_boxes_76, 7, 3.75, 4, 2.5, 9); + TEST_DIFFERENCE(case_recursive_boxes_77, 4, 3.75, 7, 6.25, 8); + TEST_DIFFERENCE(case_recursive_boxes_78, 11, 5.5, 8, 4.5, 14); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_79, 2, 1.25, 6, 4.5, 8); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_79, 2, 1.25, 5, 4.5, 7); +#endif + +#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + TEST_DIFFERENCE(case_recursive_boxes_80, 1, 0.5, 2, 0.75, 2); +#else + // one polygon is divided into two, for same reason as union creates a small + // interior ring there + TEST_DIFFERENCE(case_recursive_boxes_80, 1, 0.5, 2, 0.75, 3); +#endif + + { + ut_settings sym_settings; + #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + sym_settings.sym_difference = false; + #endif + test_one("mysql_21965285_b", + mysql_21965285_b[0], + mysql_21965285_b[1], + 2, -1, 183.71376870369406, + 2, -1, 131.21376870369406, + sym_settings); + } + +#if defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS) && ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + TEST_DIFFERENCE(mysql_regression_1_65_2017_08_31, 1, 4.30697514e-7, 3, 152.0642, 4); +#else + // Misses one turn which is actually weird because there are no self-turns involved + TEST_DIFFERENCE(mysql_regression_1_65_2017_08_31, 0, 0, 3, 152.0642, 3); +#endif } @@ -389,11 +473,9 @@ void test_specific_areal() // Spikes in a-b and b-a, failure in symmetric difference ut_settings settings; +#if !defined(BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS) settings.sym_difference = false; settings.test_validity = false; -#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - settings.test_validity = true; - settings.sym_difference = true; #endif TEST_DIFFERENCE_WITH(0, 1, ticket_11674, 3, 9105781.5, 5, 119059.5, -1); @@ -418,7 +500,8 @@ void test_specific_areal() #else // Testing consistency of testcase itself - BOOST_CHECK_EQUAL(a_min_b, ticket_12751[2]); + boost::ignore_unused(a_min_b); + // BOOST_CHECK_EQUAL(a_min_b, ticket_12751[2]); TEST_DIFFERENCE_WITH(2, 3, ticket_12751, 1, 2537992.5, 2, 294963.5, 3); #endif @@ -428,12 +511,16 @@ void test_specific_areal() { // Ticket 12752 (Volker) // Spikes in a-b and b-a, failure in symmetric difference - ut_settings settings; + settings.remove_spikes = true; settings.sym_difference = false; +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE_WITH(0, 1, ticket_12752, 3, 2776692.0, 3, 7893.0, 2); +#else + // If self-intersections are not tested, result is not valid settings.test_validity = false; - TEST_DIFFERENCE_WITH(0, 1, ticket_12752, 3, 2776692.0, 3, 7893.0, 6); +#endif } { diff --git a/test/algorithms/set_operations/difference/test_difference.hpp b/test/algorithms/set_operations/difference/test_difference.hpp index 7321c482b..cc7179db1 100644 --- a/test/algorithms/set_operations/difference/test_difference.hpp +++ b/test/algorithms/set_operations/difference/test_difference.hpp @@ -426,6 +426,7 @@ void test_one_lp(std::string const& caseid, bg::read_wkt(wkt2, g2); bg::correct(g1); + bg::correct(g2); typedef typename setop_output_type::type result_type; result_type pieces; diff --git a/test/algorithms/set_operations/intersection/intersection.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index 781fb03f1..b9c55de93 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -5,8 +5,8 @@ // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, 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 @@ -48,8 +48,7 @@ BOOST_GEOMETRY_REGISTER_LINESTRING_TEMPLATED(std::vector) #if ! defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS) #define TEST_INTERSECTION_IGNORE(caseid, clips, points, area) \ - { ut_settings ignore_validity; \ - ignore_validity.test_validity = false; \ + { ut_settings ignore_validity; ignore_validity.test_validity = false; \ (test_one) \ ( #caseid, caseid[0], caseid[1], clips, points, area, ignore_validity); } #endif @@ -603,6 +602,27 @@ void test_areal_linear() typedef typename bg::point_type::type Point; test_one, LineString>("simplex", poly_simplex, "LINESTRING(0 2,4 2)", 1, 2, 2.0); + test_one_lp("case30", + "POLYGON((25 0,0 15,30 15,22 10,25 0))", + "LINESTRING(10 15,20 15)", + 1, 2, 10.0); + + test_one_lp("case31", + "POLYGON((25 0,0 15,30 15,22 10,25 0))", + "LINESTRING(0 15,20 15)", + 1, 2, 20.0); + + test_one_lp("case32", + "POLYGON((25 0,0 15,30 15,22 10,25 0))", + "LINESTRING(25 0, 0 15,20 15)", + 1, 3, 49.15475947422650 /*sqrt(25^2+15^2)+20*/); + + typedef typename bg::point_type::type P; + + test_one_lp("case30p", + "POLYGON((25 0,0 15,30 15,22 10,25 0))", + "LINESTRING(10 15,20 15)", + 2, 2, 0); } diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 10a6ff5d1..4992897c3 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -33,15 +33,13 @@ ( #caseid, caseid[0], caseid[1], clips, points, area) #define TEST_INTERSECTION_IGNORE(caseid, clips, points, area) \ + { ut_settings ignore_validity; ignore_validity.test_validity = false; \ (test_one) \ - ( #caseid, caseid[0], caseid[1], clips, points, area, ignore_validity) + ( #caseid, caseid[0], caseid[1], clips, points, area, ignore_validity); } template void test_areal() { - ut_settings ignore_validity; - ignore_validity.test_validity = false; - test_one("simplex_multi", case_multi_simplex[0], case_multi_simplex[1], 2, 12, 6.42); @@ -164,6 +162,10 @@ void test_areal() TEST_INTERSECTION(case_139_multi, 2, 23, 40.546875); TEST_INTERSECTION(case_140_multi, 2, 23, 40.546875); + // TODO: isolated region with multiple connection should be handled + // differently + TEST_INTERSECTION_IGNORE(case_141_multi, 2, -1, 74.5); + #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_INTERSECTION(case_recursive_boxes_1, 10, 97, 47.0); #else @@ -178,7 +180,7 @@ void test_areal() 19, 87, 12.5); // Area from SQL Server #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS - TEST_INTERSECTION_IGNORE(case_recursive_boxes_4, 11, 177, 67.0); + TEST_INTERSECTION_IGNORE(case_recursive_boxes_4, 13, 169, 67.0); #else TEST_INTERSECTION_IGNORE(case_recursive_boxes_4, 8, 178, 67.0); #endif @@ -307,6 +309,48 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_58, 1, -1, 0.25); TEST_INTERSECTION(case_recursive_boxes_59, 8, -1, 8.25); TEST_INTERSECTION(case_recursive_boxes_60, 8, -1, 10.0); + TEST_INTERSECTION(case_recursive_boxes_61, 2, -1, 20.0); + TEST_INTERSECTION(case_recursive_boxes_62, 9, -1, 10.5); + + TEST_INTERSECTION(case_recursive_boxes_63, 11, -1, 5.75); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_INTERSECTION(case_recursive_boxes_64, 5, -1, 17.25); +#else + TEST_INTERSECTION_IGNORE(case_recursive_boxes_64, 4, -1, 17.25); +#endif + TEST_INTERSECTION(case_recursive_boxes_65, 3, -1, 17.25); + +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_INTERSECTION(case_recursive_boxes_66, 4, -1, 16.0); +#else + TEST_INTERSECTION_IGNORE(case_recursive_boxes_66, 2, -1, 16.0); +#endif + + TEST_INTERSECTION(case_recursive_boxes_67, 5, -1, 2.5); + TEST_INTERSECTION(case_recursive_boxes_68, 8, -1, 9.5); + TEST_INTERSECTION(case_recursive_boxes_69, 6, -1, 3.25); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_INTERSECTION(case_recursive_boxes_70, 6, -1, 18.5); +#else + // Misses a necessary self-turn and therefore a ring + TEST_INTERSECTION_IGNORE(case_recursive_boxes_70, 3, -1, 18.0); +#endif + + TEST_INTERSECTION(case_recursive_boxes_71, 3, -1, 1.75); + TEST_INTERSECTION(case_recursive_boxes_72, 8, -1, 4.5); + TEST_INTERSECTION(case_recursive_boxes_73, 3, -1, 18.5); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_INTERSECTION(case_recursive_boxes_74, 3, -1, 20.25); +#else + TEST_INTERSECTION_IGNORE(case_recursive_boxes_74, 2, -1, 20.25); +#endif + + TEST_INTERSECTION(case_recursive_boxes_75, 5, -1, 16.75); + TEST_INTERSECTION(case_recursive_boxes_76, 2, -1, 18.25); + TEST_INTERSECTION(case_recursive_boxes_77, 5, -1, 3.5); + TEST_INTERSECTION(case_recursive_boxes_78, 9, -1, 8.0); + TEST_INTERSECTION(case_recursive_boxes_79, 5, -1, 9.0); + TEST_INTERSECTION(case_recursive_boxes_80, 1, -1, 0.25); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], @@ -331,6 +375,8 @@ void test_areal() ); + TEST_INTERSECTION(ticket_12503, 2, 13, 17.375); + test_one("mysql_23023665_7", mysql_23023665_7[0], mysql_23023665_7[1], 2, 11, 9.80505786783); @@ -340,6 +386,8 @@ void test_areal() #else TEST_INTERSECTION_IGNORE(mysql_23023665_12, 1, -1, 11.812440191387557); #endif + + TEST_INTERSECTION(mysql_regression_1_65_2017_08_31, 2, -1, 29.9022122); } template diff --git a/test/algorithms/set_operations/intersection/test_intersection.hpp b/test/algorithms/set_operations/intersection/test_intersection.hpp index 8c8f68aa2..4afe42f5b 100644 --- a/test/algorithms/set_operations/intersection/test_intersection.hpp +++ b/test/algorithms/set_operations/intersection/test_intersection.hpp @@ -41,6 +41,7 @@ #include #include "../setop_output_type.hpp" +#include "../check_validity.hpp" struct ut_settings { @@ -56,7 +57,13 @@ struct ut_settings }; -template +template +< + typename G1, + typename G2, + typename ResultType, + typename IntersectionOutput +> typename bg::default_area_result::type check_result( IntersectionOutput const& intersection_output, @@ -96,15 +103,15 @@ check_result( { std::cout << std::setprecision(20) << bg::wkt(*it) << std::endl; } + } - if (settings.test_validity) - { - std::string message; - bool const valid = bg::is_valid(*it, message); - BOOST_CHECK_MESSAGE(valid, - "intersection: " << caseid << " not valid: " << message - << " type: " << (type_for_assert_message())); - } + if (settings.test_validity) + { + std::string message; + bool const valid = check_validity::apply(intersection_output, message); + BOOST_CHECK_MESSAGE(valid, + "intersection: " << caseid << " not valid: " << message + << " type: " << (type_for_assert_message())); } #if ! defined(BOOST_GEOMETRY_NO_BOOST_TEST) @@ -194,7 +201,7 @@ typename bg::default_area_result::type test_intersection(std::string const& result_type intersection_output; bg::intersection(g1, g2, intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); @@ -203,21 +210,21 @@ typename bg::default_area_result::type test_intersection(std::string const& intersection_output.clear(); bg::intersection(boost::variant(g1), g2, intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); intersection_output.clear(); bg::intersection(g1, boost::variant(g2), intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); intersection_output.clear(); bg::intersection(boost::variant(g1), boost::variant(g2), intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); #endif diff --git a/test/algorithms/set_operations/union/test_union.hpp b/test/algorithms/set_operations/union/test_union.hpp index 9b32292c5..352521eb2 100644 --- a/test/algorithms/set_operations/union/test_union.hpp +++ b/test/algorithms/set_operations/union/test_union.hpp @@ -19,6 +19,7 @@ #include #include "../setop_output_type.hpp" +#include "../check_validity.hpp" #include #include @@ -71,42 +72,6 @@ inline void check_input_validity(std::string const& caseid, int case_index, } #endif -template -< - typename Geometry, - typename Tag = typename bg::tag::type -> -struct check_validity -{ - static inline - bool apply(Geometry const& geometry, std::string& message) - { - if (! bg::is_valid(geometry, message)) - { - std::cout << bg::wkt(geometry) << std::endl; - } - return bg::is_valid(geometry, message); - } -}; - -// Specialization for vector of (e.g. rings) -template -struct check_validity -{ - static inline - bool apply(Geometry const& geometry, std::string& message) - { - typedef typename boost::range_value::type single_type; - BOOST_FOREACH(single_type const& element, geometry) - { - if (! bg::is_valid(element, message)) - { - return false; - } - } - return true; - } -}; template diff --git a/test/algorithms/set_operations/union/union.cpp b/test/algorithms/set_operations/union/union.cpp index d36c842f4..40644c6c0 100644 --- a/test/algorithms/set_operations/union/union.cpp +++ b/test/algorithms/set_operations/union/union.cpp @@ -5,8 +5,8 @@ // Copyright (c) 2008-2016 Bruno Lalande, Paris, France. // Copyright (c) 2009-2016 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2016. -// Modifications copyright (c) 2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2016,2017. +// Modifications copyright (c) 2016-2017, 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 @@ -398,9 +398,9 @@ void test_areal() // Robustness issues, followed out buffer-robustness-tests, test them also reverse #if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) test_one("buffer_rt_f", buffer_rt_f[0], buffer_rt_f[1], - 1, 0, if_typed(18, 23), 4.60853); + 1, 0, 23, 4.60853); test_one("buffer_rt_f_rev", buffer_rt_f[1], buffer_rt_f[0], - 1, 0, if_typed(18, 23), 4.60853); + 1, 0, 23, 4.60853); test_one("buffer_rt_g", buffer_rt_g[0], buffer_rt_g[1], 1, 0, if_typed(18, 17), 16.571); test_one("buffer_rt_g_rev", buffer_rt_g[1], buffer_rt_g[0], @@ -482,6 +482,33 @@ void test_areal() 1, 1, -1, 220.5); } +void test_geographic() +{ + typedef bg::model::point > point; + typedef bg::model::polygon polygon; + typedef bg::model::multi_polygon multipolygon; + + bg::srs::spheroid sph(6378137.0000000000, 6356752.3142451793); + + bg::strategy::intersection::geographic_segments<> is(sph); + bg::strategy::area::geographic as(sph); + + polygon p1, p2; + + boost::geometry::read_wkt("POLYGON((16 15,-132 10,-56 89,67 5,16 15))", p1); + boost::geometry::read_wkt("POLYGON((101 49,12 40,-164 10,117 0,101 49))", p2); + + multipolygon result; + boost::geometry::union_(p1, p2, result, is); + + double result_area = bg::area(result, as); + + BOOST_CHECK(boost::size(result) == 1 + && boost::size(bg::exterior_ring(bg::range::at(result, 0))) == 9 + && boost::size(bg::interior_rings(bg::range::at(result, 0))) == 0); + BOOST_CHECK_CLOSE(result_area, 144265751613509.06, 0.001); +} + template void test_all() { @@ -557,5 +584,7 @@ int test_main(int, char* []) #endif #endif + test_geographic(); + return 0; } diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index a3b1c2ac1..eb98884df 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -204,6 +204,10 @@ void test_areal() TEST_UNION(case_135_multi, 1, 2, -1, 22.0); TEST_UNION(case_136_multi, 1, 2, -1, 22.0); TEST_UNION(case_137_multi, 1, 2, -1, 22.0); + TEST_UNION(case_138_multi, 2, 1, -1, 65.225); + TEST_UNION(case_139_multi, 2, 1, -1, 64.953); + TEST_UNION(case_140_multi, 2, 1, -1, 64.953); + TEST_UNION(case_141_multi, 1, 0, -1, 100.0); test_one("case_recursive_boxes_1", case_recursive_boxes_1[0], case_recursive_boxes_1[1], @@ -382,6 +386,33 @@ void test_areal() #endif TEST_UNION(case_recursive_boxes_60, 3, 0, -1, 20.5); + TEST_UNION(case_recursive_boxes_61, 1, 1, -1, 23.5); + TEST_UNION(case_recursive_boxes_62, 2, 3, -1, 21.25); + TEST_UNION(case_recursive_boxes_63, 2, 3, -1, 44.0); + TEST_UNION(case_recursive_boxes_64, 1, 2, -1, 24.5); + TEST_UNION(case_recursive_boxes_65, 1, 1, -1, 24.5); + TEST_UNION(case_recursive_boxes_66, 1, 1, -1, 24.75); + TEST_UNION(case_recursive_boxes_67, 4, 0, -1, 14.75); + TEST_UNION(case_recursive_boxes_68, 1, 4, -1, 22.5); + TEST_UNION(case_recursive_boxes_69, 4, 0, -1, 16.25); + TEST_UNION(case_recursive_boxes_70, 1, 0, -1, 25.0); + TEST_UNION(case_recursive_boxes_71, 4, 2, -1, 15.75); + TEST_UNION(case_recursive_boxes_72, 10, 0, -1, 15.0); + TEST_UNION(case_recursive_boxes_73, 1, 2, -1, 24.25); + TEST_UNION(case_recursive_boxes_74, 1, 1, -1, 24.75); + TEST_UNION(case_recursive_boxes_75, 1, 2, -1, 23.25); + TEST_UNION(case_recursive_boxes_76, 1, 0, -1, 24.5); + TEST_UNION(case_recursive_boxes_77, 8, 1, -1, 13.5); + TEST_UNION(case_recursive_boxes_78, 2, 5, -1, 18.0); + TEST_UNION(case_recursive_boxes_79, 1, 2, -1, 14.75); + +#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + // This is correct: no holes generated + TEST_UNION(case_recursive_boxes_80, 2, 0, -1, 1.5); +#else + // See comment for this testcase + TEST_UNION(case_recursive_boxes_80, 2, 1, -1, 1.5); +#endif test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], @@ -421,6 +452,8 @@ void test_areal() 1, 0, -1, 575.831180350007); #endif + TEST_UNION(ticket_12503, 42, 1, -1, 945.625); + // Should have 1 hole. Needs self turns. #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_UNION(mysql_23023665_7, 1, 1, -1, 99.19494); @@ -438,6 +471,8 @@ void test_areal() test_one("mysql_23023665_9", mysql_23023665_9[0], mysql_23023665_9[1], 1, 9, -1, 1250.0); + + TEST_UNION(mysql_regression_1_65_2017_08_31, 3, 0, -1, 181.966397646608); } // Test cases (generic) diff --git a/test/algorithms/test_correct.hpp b/test/algorithms/test_correct.hpp index 2d2723cd5..1576b1602 100644 --- a/test/algorithms/test_correct.hpp +++ b/test/algorithms/test_correct.hpp @@ -27,12 +27,11 @@ #include #include - template void check_geometry(Geometry const& geometry, std::string const& expected) { std::ostringstream out; - out << bg::wkt(geometry); + out << bg::wkt_manipulator(geometry, false); BOOST_CHECK_EQUAL(out.str(), expected); } diff --git a/test/algorithms/test_is_simple.hpp b/test/algorithms/test_is_simple.hpp new file mode 100644 index 000000000..d4162d04b --- /dev/null +++ b/test/algorithms/test_is_simple.hpp @@ -0,0 +1,121 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2014-2017, 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 + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +#include "pretty_print_geometry.hpp" +#endif + +namespace bg = ::boost::geometry; + +template +void test_simple_s(Geometry const& geometry, + Strategy const& strategy, + bool expected_result, + bool check_validity = true) +{ + bool simple = bg::is_simple(geometry, strategy); + bool valid = ! check_validity || bg::is_valid(geometry, strategy); + + BOOST_CHECK_MESSAGE( valid == true, + "Expected valid geometry, " + << " wkt: " << bg::wkt(geometry) ); + + BOOST_CHECK_MESSAGE( simple == expected_result, + "Expected: " << expected_result + << " detected: " << simple + << " wkt: " << bg::wkt(geometry) ); +} + +template +void test_simple(Geometry const& geometry, bool expected_result, + bool check_validity = true) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "=======" << std::endl; +#endif + + bool simple = bg::is_simple(geometry); + bool valid = ! check_validity || bg::is_valid(geometry); + + BOOST_CHECK_MESSAGE( valid == true, + "Expected valid geometry, " + << " wkt: " << bg::wkt(geometry) ); + + BOOST_CHECK_MESSAGE( simple == expected_result, + "Expected: " << expected_result + << " detected: " << simple + << " wkt: " << bg::wkt(geometry) ); + + typedef typename bg::strategy::intersection::services::default_strategy + < + CSTag + >::type strategy_type; + + test_simple_s(geometry, strategy_type(), expected_result, check_validity); + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "Geometry: "; + pretty_print_geometry::apply(std::cout, geometry); + std::cout << std::endl; + std::cout << std::boolalpha; + std::cout << "is simple: " << simple << std::endl; + std::cout << "expected result: " << expected_result << std::endl; + std::cout << "=======" << std::endl; + std::cout << std::endl << std::endl; + std::cout << std::noboolalpha; +#endif +} + +template +void test_simple(Geometry const& geometry, + bool expected_result, + bool check_validity = true) +{ + typedef typename bg::cs_tag::type cs_tag; + test_simple(geometry, expected_result, check_validity); +} + +template +void test_simple(boost::variant const& variant_geometry, + bool expected_result, + bool check_validity = true) +{ + typedef typename bg::cs_tag::type cs_tag; + test_simple(variant_geometry, expected_result, check_validity); +} diff --git a/test/algorithms/test_is_valid.hpp b/test/algorithms/test_is_valid.hpp index 2776327d9..df0be5b81 100644 --- a/test/algorithms/test_is_valid.hpp +++ b/test/algorithms/test_is_valid.hpp @@ -325,6 +325,30 @@ struct validity_tester_areal }; +template +struct validity_tester_geo_areal +{ + template + static inline bool apply(Geometry const& geometry) + { + bg::is_valid_default_policy visitor; + bg::strategy::intersection::geographic_segments<> s; + return bg::is_valid(geometry, visitor, s); + } + + template + static inline std::string reason(Geometry const& geometry) + { + std::ostringstream oss; + bg::failing_reason_policy visitor(oss); + bg::strategy::intersection::geographic_segments<> s; + bg::is_valid(geometry, visitor, s); + return oss.str(); + } + +}; + + //---------------------------------------------------------------------------- @@ -376,7 +400,7 @@ public: bool expected_result) { std::stringstream sstr; - sstr << case_id << "-original"; + sstr << case_id << "-original"; // which is: CCW open base_test(sstr.str(), geometry, expected_result); if ( is_convertible_to_closed::apply(geometry) ) diff --git a/test/formulas/inverse.cpp b/test/formulas/inverse.cpp index 8fa06d80a..e72cf3bb5 100644 --- a/test/formulas/inverse.cpp +++ b/test/formulas/inverse.cpp @@ -1,7 +1,7 @@ // Boost.Geometry // Unit Test -// Copyright (c) 2016 Oracle and/or its affiliates. +// Copyright (c) 2016-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -9,6 +9,9 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) + +#include + #include "test_formula.hpp" #include "inverse_cases.hpp" @@ -16,14 +19,27 @@ #include #include -template -void check_inverse(Result const& result, expected_result const& expected, expected_result const& reference, double reference_error) + +void check_inverse(std::string const& name, + expected_results const& results, + bg::formula::result_inverse const& result, + expected_result const& expected, + expected_result const& reference, + double reference_error) { - check_one(result.distance, expected.distance, reference.distance, reference_error); - check_one(result.azimuth, expected.azimuth, reference.azimuth, reference_error, true); - check_one(result.reverse_azimuth, expected.reverse_azimuth, reference.reverse_azimuth, reference_error, true); - check_one(result.reduced_length, expected.reduced_length, reference.reduced_length, reference_error); - check_one(result.geodesic_scale, expected.geodesic_scale, reference.geodesic_scale, reference_error); + std::stringstream ss; + ss << "(" << results.p1.lon << " " << results.p1.lat << ")->(" << results.p2.lon << " " << results.p2.lat << ")"; + + check_one(name + "_d " + ss.str(), + result.distance, expected.distance, reference.distance, reference_error); + check_one(name + "_a " + ss.str(), + result.azimuth, expected.azimuth, reference.azimuth, reference_error, true); + check_one(name + "_ra " + ss.str(), + result.reverse_azimuth, expected.reverse_azimuth, reference.reverse_azimuth, reference_error, true); + check_one(name + "_rl " + ss.str(), + result.reduced_length, expected.reduced_length, reference.reduced_length, reference_error); + check_one(name + "_gs " + ss.str(), + result.geodesic_scale, expected.geodesic_scale, reference.geodesic_scale, reference_error); } void test_all(expected_results const& results) @@ -45,19 +61,19 @@ void test_all(expected_results const& results) result_v = vi_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); result_v.azimuth *= r2d; result_v.reverse_azimuth *= r2d; - check_inverse(result_v, results.vincenty, results.reference, 0.0000001); + check_inverse("vincenty", results, result_v, results.vincenty, results.reference, 0.0000001); typedef bg::formula::thomas_inverse th_t; result_t = th_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); result_t.azimuth *= r2d; result_t.reverse_azimuth *= r2d; - check_inverse(result_t, results.thomas, results.reference, 0.00001); + check_inverse("thomas", results, result_t, results.thomas, results.reference, 0.00001); typedef bg::formula::andoyer_inverse an_t; result_a = an_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); result_a.azimuth *= r2d; result_a.reverse_azimuth *= r2d; - check_inverse(result_a, results.andoyer, results.reference, 0.001); + check_inverse("andoyer", results, result_a, results.andoyer, results.reference, 0.001); } int test_main(int, char*[]) diff --git a/test/formulas/test_formula.hpp b/test/formulas/test_formula.hpp index 60502281d..8efb428cc 100644 --- a/test/formulas/test_formula.hpp +++ b/test/formulas/test_formula.hpp @@ -24,9 +24,17 @@ void normalize_deg(double & deg) deg += 360.0; } -void check_one(double result, double expected, double reference, double reference_error, + +#define BOOST_GEOMETRY_CHECK_CLOSE( L, R, T, M ) BOOST_TEST_TOOL_IMPL( 0, \ + ::boost::test_tools::check_is_close_t(), M, CHECK, CHECK_MSG, (L)(R)(::boost::math::fpc::percent_tolerance(T)) ) + + +void check_one(std::string const& name, + double result, double expected, double reference, double reference_error, bool normalize = false, bool check_reference_only = false) { + std::string id = name.empty() ? "" : (name + " : "); + if (normalize) { normalize_deg(result); @@ -45,19 +53,22 @@ void check_one(double result, double expected, double reference, double referenc { bool is_close = abs_result <= 30 * eps && abs_expected <= 30 * eps; BOOST_CHECK_MESSAGE((is_close), - std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } else if (res_max > 100 * eps) { - BOOST_CHECK_CLOSE(result, expected, 0.1); + BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 0.1, + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } else if (res_max > 10 * eps) { - BOOST_CHECK_CLOSE(result, expected, 10); + BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 10, + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } else if (res_max > eps) { - BOOST_CHECK_CLOSE(result, expected, 1000); + BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 1000, + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } } @@ -66,7 +77,14 @@ void check_one(double result, double expected, double reference, double referenc double ref_diff = bg::math::abs(result - reference); double ref_max = (std::max)(bg::math::abs(result), bg::math::abs(reference)); bool is_ref_close = ref_diff <= reference_error || ref_diff <= reference_error * ref_max; - BOOST_CHECK_MESSAGE((is_ref_close), std::setprecision(20) << "result {" << result << "} and reference {" << reference << "} not close enough."); + BOOST_CHECK_MESSAGE((is_ref_close), + id << std::setprecision(20) << "result {" << result << "} and reference {" << reference << "} not close enough."); +} + +void check_one(double result, double expected, double reference, double reference_error, + bool normalize = false, bool check_reference_only = false) +{ + check_one("", result, expected, reference, reference_error, normalize, check_reference_only); } #endif // BOOST_GEOMETRY_TEST_FORMULA_HPP diff --git a/test/policies/compare.cpp b/test/policies/compare.cpp index 3e4b1b021..60c4021d5 100644 --- a/test/policies/compare.cpp +++ b/test/policies/compare.cpp @@ -3,6 +3,10 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle 2017. +// Modifications copyright (c) 2017, 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) @@ -200,29 +204,37 @@ void test_2d_sort() template void test_spherical() { - typedef typename bg::coordinate_type

::type ct; + //typedef typename bg::coordinate_type

::type ct; std::vector

v; + v.push_back(bg::make

( 180.00, 70.56)); v.push_back(bg::make

( 179.73, 71.56)); // east v.push_back(bg::make

( 177.47, 71.23)); // less east - v.push_back(bg::make

(-178.78, 70.78)); // further east, = west, this is the most right point + v.push_back(bg::make

(-178.78, 72.78)); // further east, = west, this is the most left point + v.push_back(bg::make

(-180.00, 73.12)); // Sort on coordinates in order x,y,z std::sort(v.begin(), v.end(), bg::less

()); std::string s = coordinates(v); - BOOST_CHECK_EQUAL(s, "(177.47, 71.23)(179.73, 71.56)(-178.78, 70.78)"); + BOOST_CHECK_EQUAL(s, "(-178.78, 72.78)(177.47, 71.23)(179.73, 71.56)(180, 70.56)(-180, 73.12)"); + + // Sort ascending on only y-coordinate + std::sort(v.begin(), v.end(), bg::less()); + s = coordinates(v); + BOOST_CHECK_EQUAL(s, "(180, 70.56)(177.47, 71.23)(179.73, 71.56)(-178.78, 72.78)(-180, 73.12)"); // Sort ascending on only x-coordinate std::sort(v.begin(), v.end(), bg::less()); s = coordinates(v); - BOOST_CHECK_EQUAL(s, "(177.47, 71.23)(179.73, 71.56)(-178.78, 70.78)"); + BOOST_CHECK((s == "(-178.78, 72.78)(177.47, 71.23)(179.73, 71.56)(180, 70.56)(-180, 73.12)" + || s == "(-178.78, 72.78)(177.47, 71.23)(179.73, 71.56)(-180, 73.12)(180, 70.56)")); // Sort ascending on only x-coordinate, but override with std-comparison, // (so this is the normal sorting behaviour that would have been used // if it would not have been spherical) - std::sort(v.begin(), v.end(), bg::less >()); - s = coordinates(v); - BOOST_CHECK_EQUAL(s, "(-178.78, 70.78)(177.47, 71.23)(179.73, 71.56)"); + //std::sort(v.begin(), v.end(), bg::less >()); + //s = coordinates(v); + //BOOST_CHECK_EQUAL(s, "(-178.78, 70.78)(177.47, 71.23)(179.73, 71.56)"); } @@ -237,6 +249,8 @@ int test_main(int, char* []) test_2d_sort >(); test_spherical > >(); + test_spherical > >(); + test_spherical > >(); return 0; } diff --git a/test/robustness/overlay/areal_areal/test_overlay_p_q.hpp b/test/robustness/overlay/areal_areal/test_overlay_p_q.hpp index c4c590c67..ac9ffa3e3 100644 --- a/test/robustness/overlay/areal_areal/test_overlay_p_q.hpp +++ b/test/robustness/overlay/areal_areal/test_overlay_p_q.hpp @@ -43,6 +43,7 @@ struct p_q_settings bool also_difference; bool validity; bool wkt; + bool verify_area; double tolerance; p_q_settings() @@ -50,6 +51,7 @@ struct p_q_settings , also_difference(false) , validity(false) , wkt(false) + , verify_area(false) , tolerance(1.0e-3) // since rescaling to integer the tolerance should be less. Was originally 1.0e-6 {} }; @@ -67,6 +69,49 @@ inline typename bg::default_area_result::type p_q_area(Geometry const& } } +struct verify_area +{ + template + static inline bool check_ring(Iterator begin, Iterator end) + { + for (Iterator it = begin; it != end; ++it) + { + double const area = bg::area(*it); + if (fabs(area) < 0.01) + { + return false; + } + } + return true; + } + + template + static inline bool check_rings(Interiors const& rings) + { + return check_ring(boost::begin(rings), boost::end(rings)); + } + + template + static inline bool check_polys(Iterator begin, Iterator end) + { + for (Iterator it = begin; it != end; ++it) + { + // If necessary, exterior_ring can be checked too + if (! check_rings(bg::interior_rings(*it))) + { + return false; + } + } + return true; + } + + template + static inline bool apply(Geometry const& g) + { + return check_polys(boost::begin(g), boost::end(g)); + } +}; + template static bool test_overlay_p_q(std::string const& caseid, G1 const& p, G2 const& q, @@ -136,6 +181,17 @@ static bool test_overlay_p_q(std::string const& caseid, wrong = true; } } + + if (settings.verify_area && ! verify_area::apply(out_u)) + { + std::cout << "Union/interior area incorrect" << std::endl; + wrong = true; + } + if (settings.verify_area && ! verify_area::apply(out_i)) + { + std::cout << "Intersection/interior area incorrect" << std::endl; + wrong = true; + } } if (true)