diff --git a/doc/doxy/Doxyfile b/doc/doxy/Doxyfile index 4c9aa41d3..aab8e217b 100644 --- a/doc/doxy/Doxyfile +++ b/doc/doxy/Doxyfile @@ -211,6 +211,7 @@ INPUT = . .. ../../../../boost/geometry/core \ ../../../../boost/geometry/strategies/agnostic \ ../../../../boost/geometry/strategies/cartesian \ ../../../../boost/geometry/strategies/spherical \ + ../../../../boost/geometry/strategies/geographic \ ../../../../boost/geometry/strategies/transform \ ../../../../boost/geometry/util \ ../../../../boost/geometry/views \ diff --git a/doc/geometry.qbk b/doc/geometry.qbk index 442604534..6c30c833a 100644 --- a/doc/geometry.qbk +++ b/doc/geometry.qbk @@ -1,9 +1,10 @@ [/============================================================================ Boost.Geometry (aka GGL, Generic Geometry Library) - Copyright (c) 2009-2015 Barend Gehrels, Amsterdam, the Netherlands. - Copyright (c) 2009-2015 Mateusz Loskot, London, UK. - Copyright (c) 2009-2015 Bruno Lalande, Paris, France. + Copyright (c) 2009-2017 Barend Gehrels, Amsterdam, the Netherlands. + Copyright (c) 2009-2017 Mateusz Loskot, London, UK. + Copyright (c) 2009-2017 Bruno Lalande, Paris, France. + Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -12,8 +13,8 @@ [library Geometry [quickbook 1.5] - [authors [Gehrels, Barend], [Lalande, Bruno], [Loskot, Mateusz], [Wulkiewicz, Adam], [Karavelas, Menelaos]] - [copyright 2009-2015 Barend Gehrels, Bruno Lalande, Mateusz Loskot, Adam Wulkiewicz, Oracle and/or its affiliates] + [authors [Gehrels, Barend], [Lalande, Bruno], [Loskot, Mateusz], [Wulkiewicz, Adam], [Karavelas, Menelaos], [Fisikopoulos, Vissarion]] + [copyright 2009-2017 Barend Gehrels, Bruno Lalande, Mateusz Loskot, Adam Wulkiewicz, Oracle and/or its affiliates] [purpose Documentation of Boost.Geometry library] [license Distributed under the Boost Software License, Version 1.0. diff --git a/doc/html/index.html b/doc/html/index.html index 841fbf5b1..d8afcf098 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -37,8 +37,12 @@

Menelaos Karavelas

-
+

+Vissarion Fisikopoulos +

+

Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -72,6 +76,7 @@

Constants
Coordinate Systems
Core Metafunctions
+
DE-9IM
Enumerations
Exceptions
IO (input/output)
@@ -81,11 +86,9 @@
Strategies
Views
-
Indexes
-
-
Reference Matrix
-
Alphabetical Index
-
+
Reference Matrix
+
Reference Alphabetical + Index
Examples
Example: @@ -124,6 +127,9 @@
  • Mats Taraldsvik (documentation: adapting a legacy model)
  • +
  • + Matt Amos (fixes for point_on_surface) +
  • Samuel Debionne (variant support for distance, assign, crosses, intersection, ...) @@ -131,7 +137,7 @@
  • - +

    Last revised: July 17, 2014 at 20:45:09 GMT

    Last revised: February 26, 2017 at 00:30:21 GMT


    diff --git a/doc/index/rtree/query.qbk b/doc/index/rtree/query.qbk index 0cf80acc7..75f40ec29 100644 --- a/doc/index/rtree/query.qbk +++ b/doc/index/rtree/query.qbk @@ -1,7 +1,7 @@ [/============================================================================ Boost.Geometry Index - Copyright (c) 2011-2013 Adam Wulkiewicz. + Copyright (c) 2011-2017 Adam Wulkiewicz. Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -57,7 +57,7 @@ Query iterators returned by free functions __box__ box_region(...); std::copy(bgi::qbegin(rt, bgi::intersects(box_region)), bgi::qend(rt), std::back_inserter(returned_values)); -[h4 Spatial predicates] +[h4 Spatial queries] Queries using spatial predicates returns `__value__`s which are related somehow to some Geometry - box, polygon, etc. Names of spatial predicates correspond to names of __boost_geometry__ algorithms (boolean operations). @@ -129,6 +129,9 @@ The same way different query Geometries can be used: Segment seg(/*...*/); rt.query(bgi::nearest(seg, k), std::back_inserter(returned_values)); +[note In case of k-NN queries performed with `query()` function it's not guaranteed that the returned values will be sorted according to the distance. + It's different in case of k-NN queries performed with query iterator returned by `qbegin()` function which guarantees the iteration over the closest `__value__`s first. ] + [h4 User-defined unary predicate] The user may pass a `UnaryPredicate` - function, function object or lambda expression taking const reference to Value and returning bool. @@ -175,7 +178,7 @@ may use `index::satisfies()` function like on the example below: rt.query(index::intersects(box) && !index::satisfies(is_not_red), std::back_inserter(result)); -[h4 Passing a set of predicates] +[h4 Passing set of predicates] It's possible to use some number of predicates in one query by connecting them with `operator&&` e.g. `Pred1 && Pred2 && Pred3 && ...`. @@ -190,7 +193,7 @@ left-to-right so placing most restrictive predicates first should accelerate the rt.query(index::intersects(box1) && !index::within(box2) && index::overlaps(box3), std::back_inserter(result)); -Of course it's possible to connect different types of predicates together. +It's possible to connect different types of predicates together. index::query(rt, index::nearest(pt, k) && index::within(b), std::back_inserter(returned_values)); @@ -211,13 +214,11 @@ or may be stopped at some point, when all interesting values were gathered. The break; } -[note In the case of iterative k-NN queries it's guaranteed to iterate over the closest `__value__`s first. ] - [warning The modification of the `rtree`, e.g. insertion or removal of `__value__`s may invalidate the iterators. ] -[h4 Inserting query results into the other R-tree] +[h4 Inserting query results into another R-tree] -There are several ways of inserting Values returned by a query to the other R-tree container. +There are several ways of inserting Values returned by a query into another R-tree container. The most basic way is creating a temporary container for Values and insert them later. namespace bgi = boost::geometry::index; @@ -231,14 +232,13 @@ The most basic way is creating a temporary container for Values and insert them rt1.query(bgi::intersects(Box(/*...*/)), std::back_inserter(result)); RTree rt2(result.begin(), result.end()); -However there are better ways. One of these methods is mentioned in the "Creation and modification" section. +However there are other ways. One of these methods is mentioned in the "Creation and modification" section. The insert iterator may be passed directly into the query. RTree rt3; rt1.query(bgi::intersects(Box(/*...*/))), bgi::inserter(rt3)); -If you're a user of Boost.Range you'll appreciate the third option. You may pass the result Range directly into the -constructor or `insert()` member function. +You may also pass the resulting Range directly into the constructor or `insert()` member function using Boost.Range adaptor syntax. RTree rt4(rt1 | bgi::adaptors::queried(bgi::intersects(Box(/*...*/))))); diff --git a/doc/make_qbk.py b/doc/make_qbk.py index db6cc0ab9..184e42223 100755 --- a/doc/make_qbk.py +++ b/doc/make_qbk.py @@ -4,6 +4,7 @@ # Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. # Copyright (c) 2008-2012 Bruno Lalande, Paris, France. # Copyright (c) 2009-2012 Mateusz Loskot (mateusz@loskot.net), London, UK +# Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland # # Use, modification and distribution is subject to the Boost Software License, # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -123,7 +124,8 @@ strategies = ["distance::pythagoras", "distance::pythagoras_box_box" , "distance::cross_track", "distance::cross_track_point_box" , "distance::projected_point" , "within::winding", "within::franklin", "within::crossings_multiply" - , "area::surveyor", "area::huiller" + , "area::surveyor", "area::spherical" + #, "area::geographic" , "buffer::point_circle", "buffer::point_square" , "buffer::join_round", "buffer::join_miter" , "buffer::end_round", "buffer::end_flat" diff --git a/doc/quickref.xml b/doc/quickref.xml index 16047e897..755fdd255 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -10,10 +10,10 @@ Copyright (c) 2009-2015 Bruno Lalande, Paris, France. Copyright (c) 2013-2015 Adam Wulkiewicz, Lodz, Poland. - This file was modified by Oracle on 2014, 2015. - Modifications copyright (c) 2014-2015, Oracle and/or its affiliates. - + This file was modified by Oracle on 2014, 2015, 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 Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -521,7 +521,8 @@ Area strategy::area::surveyor - strategy::area::huiller + strategy::area::spherical + diff --git a/doc/reference.qbk b/doc/reference.qbk index a39f0b5ff..e84115ece 100644 --- a/doc/reference.qbk +++ b/doc/reference.qbk @@ -1,15 +1,16 @@ [/============================================================================ Boost.Geometry (aka GGL, Generic Geometry Library) - Copyright (c) 2009-2014 Barend Gehrels, Amsterdam, the Netherlands. - Copyright (c) 2009-2014 Mateusz Loskot, London, UK. - Copyright (c) 2009-2014 Bruno Lalande, Paris, France. - Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + Copyright (c) 2009-2017 Barend Gehrels, Amsterdam, the Netherlands. + Copyright (c) 2009-2017 Mateusz Loskot, London, UK. + Copyright (c) 2009-2017 Bruno Lalande, Paris, France. + Copyright (c) 2013-2017 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 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 @@ -94,10 +95,17 @@ [include generated/covered_by.qbk] [endsect] +[section:crosses crosses] [include generated/crosses.qbk] +[endsect] +[section:difference difference] [include generated/difference.qbk] +[endsect] + +[section:disjoint disjoint] [include generated/disjoint.qbk] +[endsect] [section:distance distance] [include generated/distance.qbk] @@ -107,16 +115,21 @@ [include generated/envelope.qbk] [endsect] +[section:equals equals] [include generated/equals.qbk] +[endsect] + +[section:expand expand] [include generated/expand.qbk] +[endsect] [section:for_each for_each] [include generated/for_each.qbk] [endsect] -[/section:intersection intersection] +[section:intersection intersection] [include generated/intersection.qbk] -[/endsect] +[endsect] [section:intersects intersects] [include generated/intersects.qbk] @@ -124,7 +137,9 @@ [include generated/is_empty.qbk] +[section:is_simple is_simple] [include generated/is_simple.qbk] +[endsect] [section:is_valid is_valid] [include generated/is_valid.qbk] @@ -143,16 +158,21 @@ [include generated/num_points.qbk] [include generated/num_segments.qbk] - +[section:overlaps overlaps] [include generated/overlaps.qbk] +[endsect] [section:perimeter perimeter] [include generated/perimeter.qbk] [endsect] +[section:relate relate] [include generated/relate.qbk] +[endsect] +[section:relation relation] [include generated/relation.qbk] +[endsect] [include generated/reverse.qbk] @@ -160,9 +180,9 @@ [include generated/simplify.qbk] [endsect] -[/section:sym_difference sym_difference] +[section:sym_difference sym_difference] [include generated/sym_difference.qbk] -[/endsect] +[endsect] [section:touches touches] [include generated/touches.qbk] @@ -172,9 +192,9 @@ [include generated/transform.qbk] [endsect] -[/section:union union] +[section:union_ union_] [include generated/union.qbk] -[/endsect] +[endsect] [include generated/unique.qbk] @@ -317,7 +337,8 @@ [include generated/distance_cross_track.qbk] [include generated/distance_cross_track_point_box.qbk] [include generated/area_surveyor.qbk] -[include generated/area_huiller.qbk] +[include generated/area_spherical.qbk] +[/include generated/area_geographic.qbk] [include generated/buffer_join_round.qbk] [include generated/buffer_join_miter.qbk] [include generated/buffer_end_round.qbk] diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 56fcb8ecc..a640ab0e9 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -1,13 +1,13 @@ [/============================================================================ Boost.Geometry (aka GGL, Generic Geometry Library) - Copyright (c) 2009-2016 Barend Gehrels, Geodan, Amsterdam, the Netherlands. - Copyright (c) 2009-2016 Bruno Lalande, Paris, France. - Copyright (c) 2009-2016 Mateusz Loskot , London, UK. - Copyright (c) 2011-2016 Adam Wulkiewicz, Lodz, Poland. + Copyright (c) 2009-2017 Barend Gehrels, Geodan, Amsterdam, the Netherlands. + Copyright (c) 2009-2017 Bruno Lalande, Paris, France. + Copyright (c) 2009-2017 Mateusz Loskot , London, UK. + Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. - 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, 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 @@ -29,6 +29,8 @@ [*Breaking changes] * ublas_transformer is renamed to matrix_transformer +* explicit modifier is added to constructors of rtree index::dynamic_* parameters +* strategy::area::huiller replaced by strategy::area::spherical [*Solved issues] @@ -38,6 +40,7 @@ [*Solved tickets] * [@https://svn.boost.org/trac/boost/ticket/12566 12566] Ambiguous template instantiation in equal_to<> when pair value contains raw pointer +* [@https://svn.boost.org/trac/boost/ticket/12861 12566] Segmentation fault in stdlibc++ (gcc 4.8.2) affecting rtree [/=================] [heading Boost 1.63] diff --git a/doc/src/examples/algorithms/append.cpp b/doc/src/examples/algorithms/append.cpp index f5a3085b5..fd90a6a04 100644 --- a/doc/src/examples/algorithms/append.cpp +++ b/doc/src/examples/algorithms/append.cpp @@ -12,12 +12,12 @@ #include -#include - #include #include #include +#include /*< At the end to avoid conflicts with Boost.QVM >*/ + BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian) int main() diff --git a/doc/src/examples/core/tag.cpp b/doc/src/examples/core/tag.cpp index bb5b765ec..5e4c70687 100644 --- a/doc/src/examples/core/tag.cpp +++ b/doc/src/examples/core/tag.cpp @@ -12,13 +12,13 @@ #include -#include - #include #include #include #include +#include /*< At the end to avoid conflicts with Boost.QVM >*/ + BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian) template struct dispatch {}; diff --git a/doc/src/examples/geometries/adapted/boost_range/sliced.cpp b/doc/src/examples/geometries/adapted/boost_range/sliced.cpp index bdfd74c62..45cab8195 100644 --- a/doc/src/examples/geometries/adapted/boost_range/sliced.cpp +++ b/doc/src/examples/geometries/adapted/boost_range/sliced.cpp @@ -12,13 +12,13 @@ #include -#include - #include #include #include #include +#include /*< At the end to avoid conflicts with Boost.QVM >*/ + int main() { diff --git a/doc/src/examples/geometries/adapted/boost_range/strided.cpp b/doc/src/examples/geometries/adapted/boost_range/strided.cpp index 8b9dc6865..1ed9b5ecc 100644 --- a/doc/src/examples/geometries/adapted/boost_range/strided.cpp +++ b/doc/src/examples/geometries/adapted/boost_range/strided.cpp @@ -12,13 +12,13 @@ #include -#include - #include #include #include #include +#include /*< At the end to avoid conflicts with Boost.QVM >*/ + int main() { diff --git a/doc/src/examples/quick_start.cpp b/doc/src/examples/quick_start.cpp index f77c8909b..41d3beb32 100644 --- a/doc/src/examples/quick_start.cpp +++ b/doc/src/examples/quick_start.cpp @@ -19,6 +19,8 @@ //#pragma warning( disable : 4244 ) #endif // defined(_MSC_VER) +#include + //[quickstart_include #include diff --git a/extensions/test/gis/io/wkb/read_wkb.cpp b/extensions/test/gis/io/wkb/read_wkb.cpp index eae9301e8..95e7f525a 100644 --- a/extensions/test/gis/io/wkb/read_wkb.cpp +++ b/extensions/test/gis/io/wkb/read_wkb.cpp @@ -27,6 +27,12 @@ #include #include +#include +#include +#include + +#include + namespace bg = boost::geometry; namespace { // anonymous @@ -42,6 +48,15 @@ void test_geometry_wrong_wkb(std::string const& wkbhex, std::string const& wkt) BOOST_MESSAGE("read_wkb: " << bg::read_wkb(wkb.begin(), wkb.end(), g_wkb)); } +template +void test_geometry_parse_failure(std::string const& wkbhex, std::string const& wkt) +{ + byte_vector wkb; + BOOST_CHECK( bg::hex2wkb(wkbhex, std::back_inserter(wkb)) ); + Geometry g_wkb; + BOOST_CHECK( bg::read_wkb(wkb.begin(), wkb.end(), g_wkb) == false ); +} + template void test_geometry_equals_old(std::string const& wkbhex, std::string const& wkt) { @@ -130,16 +145,17 @@ int test_main(int, char* []) // "POINT (1.234 5.678)"); } + + typedef bg::model::point point_type; + typedef bg::model::point point3d_type; + + typedef bg::model::linestring linestring_type; + //typedef bg::model::linestring linestring3d_type; + + typedef bg::model::polygon polygon_type; + //typedef bg::model::polygon polygon3d_type; { - typedef bg::model::point point_type; - typedef bg::model::point point3d_type; - - typedef bg::model::linestring linestring_type; - //typedef bg::model::linestring linestring3d_type; - - typedef bg::model::polygon polygon_type; - //typedef bg::model::polygon polygon3d_type; // // POINT @@ -165,6 +181,16 @@ int test_main(int, char* []) ); } + { + point3d_type point(1.234, 5.678, 99.0); + + test_geometry_equals + ( + point, + "01e90300005839b4c876bef33f83c0caa145b616400000000000c05840" + ); + } + // // LINESTRING // @@ -185,6 +211,30 @@ int test_main(int, char* []) "010200000003000000000000000000F03F00000000000000400000000000000040000000000000084000000000000010400000000000001440" ); } + + + { + linestring_type linestring; + + bg::append(linestring, + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + test_geometry_equals + ( + linestring, + "0102000000030000005839B4C876BEF33F83C0CAA145B616404F401361C333224062A1D634EF3824409CC420B072482A40EB73B515FB2B3040" + ); + + test_geometry_equals + ( + linestring, + "0102000080030000005839B4C876BEF33F83C0CAA145B616400000000000C058404F401361C333224062A1D634EF3824400000000000C058409CC420B072482A40EB73B515FB2B30400000000000C05840" + ); + } // { // linestring3d_type linestring; @@ -225,7 +275,26 @@ int test_main(int, char* []) "010300000001000000050000000000000000004940000000000000494000000000000049400000000000005940000000000000594000000000000059400000000000005940000000000000494000000000000049400000000000004940" ); } + + { + polygon_type polygon; + bg::append(polygon, + boost::assign::list_of + (point_type(100.0, 200.0)) + (point_type(200.0, 200.0)) + (point_type(200.0, 400.0)) + (point_type(100.0, 400.0)) + (point_type(100.0, 200.0)) + ); + + test_geometry_equals + ( + polygon, + "010300000001000000050000000000000000005940000000000000694000000000000069400000000000006940000000000000694000000000000079400000000000005940000000000000794000000000000059400000000000006940" + ); + } + // { // polygon3d_type polygon; // @@ -251,7 +320,7 @@ int test_main(int, char* []) ); // Create an interior ring (append does not do this automatically) - boost::geometry::interior_rings(polygon).resize(1); + polygon.inners().resize(1); bg::append(polygon, boost::assign::list_of @@ -269,6 +338,158 @@ int test_main(int, char* []) } } + + // + // Multi Geometries + // + + typedef bg::model::multi_point multipoint_type; + typedef bg::model::multi_linestring multilinestring_type; + typedef bg::model::multi_polygon multipolygon_type; + + // + // MultiPoint + // + + { + multipoint_type multipoint; + + bg::append(multipoint, + boost::assign::list_of + (point_type(1.234, 5.678)) + ); + test_geometry_equals + ( + multipoint, + "01040000000100000001010000005839b4c876bef33f83c0caa145b61640" + ); + } + + { + multipoint_type multipoint; + + bg::append(multipoint, + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(10.1112, 13.1415)) + ); + + test_geometry_equals + ( + multipoint, + "01040000000200000001010000005839b4c876bef33f83c0caa145b61640010100000062a1d634ef3824409cc420b072482a40" + ); + } + + // + // MultiLineString + // + + { + multilinestring_type multilinestring; + + // Create linestrings (append does not do this automatically) + multilinestring.resize(1); + + bg::append(multilinestring[0], + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + test_geometry_equals + ( + multilinestring, + "0105000000010000000102000000030000005839b4c876bef33f83c0caa145b616404f401361c333224062a1d634ef3824409cc420b072482a40eb73b515fb2b3040" + ); + } + + { + multilinestring_type multilinestring; + + // Create linestrings (append does not do this automatically) + multilinestring.resize(2); + + bg::append(multilinestring[0], + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + bg::append(multilinestring[1], + boost::assign::list_of + (point_type(19.2, 21.22)) + (point_type(23.24, 25.26)) + ); + + test_geometry_equals + ( + multilinestring, +"0105000000020000000102000000030000005839b4c876bef33f83c0caa145b616404f401361c333224062a1d634ef3824409cc420b072482a40eb73b515fb2b30400102000000020000003333333333333340b81e85eb513835403d0ad7a3703d3740c3f5285c8f423940" + ); + } + + // + // MultiPolygon + // + + { + multipolygon_type multipolygon; + + // Create polygons (append does not do this automatically) + multipolygon.resize(1); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(100, 200)) + (point_type(200, 200)) + (point_type(200, 400)) + (point_type(100, 400)) + (point_type(100, 200)) + ); + + test_geometry_equals + ( + multipolygon, +"010600000001000000010300000001000000050000000000000000005940000000000000694000000000000069400000000000006940000000000000694000000000000079400000000000005940000000000000794000000000000059400000000000006940" + ); + } + + { + multipolygon_type multipolygon; + + // Create polygons (append does not do this automatically) + multipolygon.resize(1); + // Create an interior ring (append does not do this automatically) + multipolygon[0].inners().resize(1); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(35, 10)) + (point_type(10, 20)) + (point_type(15, 40)) + (point_type(45, 45)) + (point_type(35, 10)) + ); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(20, 30)) + (point_type(35, 35)) + (point_type(30, 20)) + (point_type(20, 30)), + 0 + ); + + test_geometry_equals + ( + multipolygon, +"0106000000010000000103000000020000000500000000000000008041400000000000002440000000000000244000000000000034400000000000002e40000000000000444000000000008046400000000000804640000000000080414000000000000024400400000000000000000034400000000000003e40000000000080414000000000008041400000000000003e40000000000000344000000000000034400000000000003e40" + ); + } + return 0; } diff --git a/extensions/test/gis/io/wkb/write_wkb.cpp b/extensions/test/gis/io/wkb/write_wkb.cpp index 3b71c0f17..1e87eae90 100644 --- a/extensions/test/gis/io/wkb/write_wkb.cpp +++ b/extensions/test/gis/io/wkb/write_wkb.cpp @@ -27,9 +27,17 @@ #include +#include +#include +#include + +#include + #include #include +#include + namespace bg = boost::geometry; namespace { // anonymous @@ -42,10 +50,11 @@ void test_geometry_equals(Geometry const& geometry, std::string const& wkbhex) std::string hex_out; BOOST_CHECK( bg::wkb2hex(wkb_out.begin(), wkb_out.end(), hex_out) ); - + + boost::algorithm::to_lower(hex_out); + BOOST_CHECK_EQUAL( wkbhex, hex_out); } - } // namespace anonymous int test_main(int, char* []) @@ -67,7 +76,7 @@ int test_main(int, char* []) test_geometry_equals ( point, - "0101000000000000000000F03F0000000000000040" + "0101000000000000000000f03f0000000000000040" ); } @@ -77,7 +86,7 @@ int test_main(int, char* []) test_geometry_equals ( point, - "01E9030000000000000000F03F00000000000000400000000000000840" + "01e9030000000000000000f03f00000000000000400000000000000840" ); } @@ -98,7 +107,7 @@ int test_main(int, char* []) test_geometry_equals ( linestring, - "010200000003000000000000000000F03F00000000000000400000000000000040000000000000084000000000000010400000000000001440" + "010200000003000000000000000000f03f00000000000000400000000000000040000000000000084000000000000010400000000000001440" ); } @@ -115,7 +124,7 @@ int test_main(int, char* []) test_geometry_equals ( linestring, - "01EA03000003000000000000000000F03F00000000000000400000000000000840000000000000004000000000000008400000000000001040000000000000104000000000000014400000000000001840" + "01ea03000003000000000000000000f03f00000000000000400000000000000840000000000000004000000000000008400000000000001040000000000000104000000000000014400000000000001840" ); } @@ -157,7 +166,7 @@ int test_main(int, char* []) test_geometry_equals ( polygon, - "01EB0300000100000005000000000000000000494000000000000049400000000000001440000000000000494000000000000059400000000000002440000000000000594000000000000059400000000000001440000000000000594000000000000049400000000000002440000000000000494000000000000049400000000000001440" + "01eb0300000100000005000000000000000000494000000000000049400000000000001440000000000000494000000000000059400000000000002440000000000000594000000000000059400000000000001440000000000000594000000000000049400000000000002440000000000000494000000000000049400000000000001440" ); } @@ -187,9 +196,161 @@ int test_main(int, char* []) test_geometry_equals ( polygon, - "0103000000020000000500000000000000008041400000000000002440000000000080464000000000008046400000000000002E40000000000000444000000000000024400000000000003440000000000080414000000000000024400400000000000000000034400000000000003E40000000000080414000000000008041400000000000003E40000000000000344000000000000034400000000000003E40" + "0103000000020000000500000000000000008041400000000000002440000000000080464000000000008046400000000000002e40000000000000444000000000000024400000000000003440000000000080414000000000000024400400000000000000000034400000000000003e40000000000080414000000000008041400000000000003e40000000000000344000000000000034400000000000003e40" ); } + // + // Multi Geometries + // + + typedef bg::model::multi_point multipoint_type; + typedef bg::model::multi_linestring multilinestring_type; + typedef bg::model::multi_polygon multipolygon_type; + + // + // MultiPoint + // + + { + multipoint_type multipoint; + + bg::append(multipoint, + boost::assign::list_of + (point_type(1.234, 5.678)) + ); + + test_geometry_equals + ( + multipoint, + "01040000000100000001010000005839b4c876bef33f83c0caa145b61640" + ); + } + + { + multipoint_type multipoint; + + bg::append(multipoint, + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(10.1112, 13.1415)) + ); + + test_geometry_equals + ( + multipoint, + "01040000000200000001010000005839b4c876bef33f83c0caa145b61640010100000062a1d634ef3824409cc420b072482a40" + ); + } + + // + // MultiLineString + // + + { + multilinestring_type multilinestring; + + // Create linestrings (append does not do this automatically) + multilinestring.resize(1); + + bg::append(multilinestring[0], + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + test_geometry_equals + ( + multilinestring, + "0105000000010000000102000000030000005839b4c876bef33f83c0caa145b616404f401361c333224062a1d634ef3824409cc420b072482a40eb73b515fb2b3040" + ); + } + + { + multilinestring_type multilinestring; + + // Create linestrings (append does not do this automatically) + multilinestring.resize(2); + + bg::append(multilinestring[0], + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + bg::append(multilinestring[1], + boost::assign::list_of + (point_type(19.2, 21.22)) + (point_type(23.24, 25.26)) + ); + + test_geometry_equals + ( + multilinestring, +"0105000000020000000102000000030000005839b4c876bef33f83c0caa145b616404f401361c333224062a1d634ef3824409cc420b072482a40eb73b515fb2b30400102000000020000003333333333333340b81e85eb513835403d0ad7a3703d3740c3f5285c8f423940" + ); + } + + // + // MultiPolygon + // + + { + multipolygon_type multipolygon; + + // Create polygons (append does not do this automatically) + multipolygon.resize(1); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(100, 200)) + (point_type(200, 200)) + (point_type(200, 400)) + (point_type(100, 400)) + (point_type(100, 200)) + ); + + test_geometry_equals + ( + multipolygon, +"010600000001000000010300000001000000050000000000000000005940000000000000694000000000000069400000000000006940000000000000694000000000000079400000000000005940000000000000794000000000000059400000000000006940" + ); + } + + { + multipolygon_type multipolygon; + + // Create polygons (append does not do this automatically) + multipolygon.resize(1); + // Create an interior ring (append does not do this automatically) + multipolygon[0].inners().resize(1); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(35, 10)) + (point_type(10, 20)) + (point_type(15, 40)) + (point_type(45, 45)) + (point_type(35, 10)) + ); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(20, 30)) + (point_type(35, 35)) + (point_type(30, 20)) + (point_type(20, 30)), + 0 + ); + + test_geometry_equals + ( + multipolygon, +"0106000000010000000103000000020000000500000000000000008041400000000000002440000000000000244000000000000034400000000000002e40000000000000444000000000008046400000000000804640000000000080414000000000000024400400000000000000000034400000000000003e40000000000080414000000000008041400000000000003e40000000000000344000000000000034400000000000003e40" + ); + } + return 0; } diff --git a/include/boost/geometry/algorithms/area.hpp b/include/boost/geometry/algorithms/area.hpp index 4751d4e74..18aea2403 100644 --- a/include/boost/geometry/algorithms/area.hpp +++ b/include/boost/geometry/algorithms/area.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. @@ -303,7 +307,8 @@ inline typename default_area_result::type area(Geometry const& geometr [heading Available Strategies] \* [link geometry.reference.strategies.strategy_area_surveyor Surveyor (cartesian)] -\* [link geometry.reference.strategies.strategy_area_huiller Huiller (spherical)] +\* [link geometry.reference.strategies.strategy_area_spherical Spherical] +[/link geometry.reference.strategies.strategy_area_geographic Geographic] } */ template diff --git a/include/boost/geometry/algorithms/crosses.hpp b/include/boost/geometry/algorithms/crosses.hpp index 8a906b2e1..c9e3651ab 100644 --- a/include/boost/geometry/algorithms/crosses.hpp +++ b/include/boost/geometry/algorithms/crosses.hpp @@ -228,6 +228,7 @@ namespace resolve_variant \param strategy \param_strategy{crosses} \return \return_check2{crosses} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/crosses.qbk]} */ template diff --git a/include/boost/geometry/algorithms/detail/disjoint/interface.hpp b/include/boost/geometry/algorithms/detail/disjoint/interface.hpp index 9b505e560..64898e35f 100644 --- a/include/boost/geometry/algorithms/detail/disjoint/interface.hpp +++ b/include/boost/geometry/algorithms/detail/disjoint/interface.hpp @@ -203,6 +203,7 @@ struct disjoint \param strategy \param_strategy{disjoint} \return \return_check2{are disjoint} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/disjoint.qbk]} */ template diff --git a/include/boost/geometry/algorithms/detail/envelope/interface.hpp b/include/boost/geometry/algorithms/detail/envelope/interface.hpp index d768c2bbd..8e9c35b39 100644 --- a/include/boost/geometry/algorithms/detail/envelope/interface.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/interface.hpp @@ -135,6 +135,7 @@ struct envelope > \param mbr \param_box \param_set{envelope} \param strategy \param_strategy{envelope} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/envelope.qbk]} \qbk{ [heading Example] @@ -142,7 +143,7 @@ struct envelope > } */ template -inline void envelope(Geometry const& geometry, Box& mbr, Strategy& strategy) +inline void envelope(Geometry const& geometry, Box& mbr, Strategy const& strategy) { resolve_variant::envelope::apply(geometry, mbr, strategy); } @@ -169,6 +170,32 @@ inline void envelope(Geometry const& geometry, Box& mbr) } +/*! +\brief \brief_calc{envelope} +\ingroup envelope +\details \details_calc{return_envelope,\det_envelope}. \details_return{envelope} +\tparam Box \tparam_box +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Envelope} +\param geometry \param_geometry +\param strategy \param_strategy{envelope} +\return \return_calc{envelope} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/envelope.qbk]} +\qbk{ +[heading Example] +[return_envelope] [return_envelope_output] +} +*/ +template +inline Box return_envelope(Geometry const& geometry, Strategy const& strategy) +{ + Box mbr; + resolve_variant::envelope::apply(geometry, mbr, strategy); + return mbr; +} + /*! \brief \brief_calc{envelope} \ingroup envelope diff --git a/include/boost/geometry/algorithms/detail/expand/interface.hpp b/include/boost/geometry/algorithms/detail/expand/interface.hpp index dc383d4b9..5aacd8e72 100644 --- a/include/boost/geometry/algorithms/detail/expand/interface.hpp +++ b/include/boost/geometry/algorithms/detail/expand/interface.hpp @@ -160,10 +160,13 @@ inline void expand(Box& box, Geometry const& geometry, \ingroup expand \tparam Box type of the box \tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{expand} \param box box to be expanded using another geometry, mutable \param geometry \param_geometry geometry which envelope (bounding box) +\param strategy \param_strategy{expand} will be added to the box +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/expand.qbk]} */ template diff --git a/include/boost/geometry/algorithms/detail/intersection/interface.hpp b/include/boost/geometry/algorithms/detail/intersection/interface.hpp index ddf49ad03..0efc9731b 100644 --- a/include/boost/geometry/algorithms/detail/intersection/interface.hpp +++ b/include/boost/geometry/algorithms/detail/intersection/interface.hpp @@ -15,12 +15,14 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_INTERFACE_HPP -// TODO: those headers probably may be removed -#include -#include +#include +#include +#include #include #include +#include +#include namespace boost { namespace geometry @@ -98,33 +100,46 @@ struct intersection } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH - -namespace resolve_variant -{ - -template + +namespace resolve_strategy { + struct intersection { - template - static inline bool - apply( - const Geometry1& geometry1, - const Geometry2& geometry2, - GeometryOut& geometry_out) + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename GeometryOut, + typename Strategy + > + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + GeometryOut & geometry_out, + Strategy const& strategy) { - concepts::check(); - concepts::check(); - - typedef typename geometry::rescale_overlay_policy_type + return dispatch::intersection < Geometry1, Geometry2 - >::type rescale_policy_type; - - rescale_policy_type robust_policy - = geometry::get_rescale_policy(geometry1, - geometry2); - + >::apply(geometry1, geometry2, robust_policy, geometry_out, + strategy); + } + + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename GeometryOut + > + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + GeometryOut & geometry_out, + default_strategy) + { typedef typename strategy::relate::services::default_strategy < Geometry1, Geometry2 @@ -139,44 +154,83 @@ struct intersection } }; +} // resolve_strategy + + +namespace resolve_variant +{ + +template +struct intersection +{ + template + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) + { + concepts::check(); + concepts::check(); + + typedef typename geometry::rescale_overlay_policy_type + < + Geometry1, + Geometry2 + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, + geometry2); + + return resolve_strategy::intersection::apply(geometry1, + geometry2, + robust_policy, + geometry_out, + strategy); + } +}; + template struct intersection, Geometry2> { - template + template struct visitor: static_visitor { Geometry2 const& m_geometry2; GeometryOut& m_geometry_out; + Strategy const& m_strategy; visitor(Geometry2 const& geometry2, - GeometryOut& geometry_out) + GeometryOut& geometry_out, + Strategy const& strategy) : m_geometry2(geometry2) , m_geometry_out(geometry_out) + , m_strategy(strategy) {} template - result_type operator()(Geometry1 const& geometry1) const + bool operator()(Geometry1 const& geometry1) const { return intersection - < - Geometry1, - Geometry2 - >::template apply - < - GeometryOut - > - (geometry1, m_geometry2, m_geometry_out); + < + Geometry1, + Geometry2 + >::apply(geometry1, m_geometry2, m_geometry_out, m_strategy); } }; - template + template static inline bool apply(variant const& geometry1, Geometry2 const& geometry2, - GeometryOut& geometry_out) + GeometryOut& geometry_out, + Strategy const& strategy) { - return boost::apply_visitor(visitor(geometry2, geometry_out), geometry1); + return boost::apply_visitor(visitor(geometry2, + geometry_out, + strategy), + geometry1); } }; @@ -184,40 +238,43 @@ struct intersection, Geometry2> template struct intersection > { - template + template struct visitor: static_visitor { Geometry1 const& m_geometry1; GeometryOut& m_geometry_out; + Strategy const& m_strategy; visitor(Geometry1 const& geometry1, - GeometryOut& geometry_out) + GeometryOut& geometry_out, + Strategy const& strategy) : m_geometry1(geometry1) , m_geometry_out(geometry_out) + , m_strategy(strategy) {} template - result_type operator()(Geometry2 const& geometry2) const + bool operator()(Geometry2 const& geometry2) const { return intersection - < - Geometry1, - Geometry2 - >::template apply - < - GeometryOut - > - (m_geometry1, geometry2, m_geometry_out); + < + Geometry1, + Geometry2 + >::apply(m_geometry1, geometry2, m_geometry_out, m_strategy); } }; - template + template static inline bool apply(Geometry1 const& geometry1, - const variant& geometry2, - GeometryOut& geometry_out) + variant const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) { - return boost::apply_visitor(visitor(geometry1, geometry_out), geometry2); + return boost::apply_visitor(visitor(geometry1, + geometry_out, + strategy), + geometry2); } }; @@ -225,44 +282,83 @@ struct intersection > template struct intersection, variant > { - template + template struct visitor: static_visitor { GeometryOut& m_geometry_out; + Strategy const& m_strategy; - visitor(GeometryOut& geometry_out) + visitor(GeometryOut& geometry_out, Strategy const& strategy) : m_geometry_out(geometry_out) + , m_strategy(strategy) {} template - result_type operator()(Geometry1 const& geometry1, - Geometry2 const& geometry2) const + bool operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const { return intersection - < - Geometry1, - Geometry2 - >::template apply - < - GeometryOut - > - (geometry1, geometry2, m_geometry_out); + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, m_geometry_out, m_strategy); } }; - template + template static inline bool - apply(const variant& geometry1, - const variant& geometry2, - GeometryOut& geometry_out) + apply(variant const& geometry1, + variant const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) { - return boost::apply_visitor(visitor(geometry_out), geometry1, geometry2); + return boost::apply_visitor(visitor(geometry_out, + strategy), + geometry1, geometry2); } }; } // namespace resolve_variant +/*! +\brief \brief_calc2{intersection} +\ingroup intersection +\details \details_calc2{intersection, spatial set theoretic intersection}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam GeometryOut Collection of geometries (e.g. std::vector, std::deque, boost::geometry::multi*) of which + the value_type fulfills a \p_l_or_c concept, or it is the output geometry (e.g. for a box) +\tparam Strategy \tparam_strategy{Intersection} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param geometry_out The output geometry, either a multi_point, multi_polygon, + multi_linestring, or a box (for intersection of two boxes) +\param strategy \param_strategy{intersection} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/intersection.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename GeometryOut, + typename Strategy +> +inline bool intersection(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) +{ + return resolve_variant::intersection + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, geometry_out, strategy); +} + + /*! \brief \brief_calc2{intersection} \ingroup intersection @@ -285,18 +381,14 @@ template typename GeometryOut > inline bool intersection(Geometry1 const& geometry1, - Geometry2 const& geometry2, - GeometryOut& geometry_out) + Geometry2 const& geometry2, + GeometryOut& geometry_out) { return resolve_variant::intersection < - Geometry1, - Geometry2 - >::template apply - < - GeometryOut - > - (geometry1, geometry2, geometry_out); + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, geometry_out, default_strategy()); } diff --git a/include/boost/geometry/algorithms/detail/is_simple/always_simple.hpp b/include/boost/geometry/algorithms/detail/is_simple/always_simple.hpp index 91e2ef76b..5cec5e192 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/always_simple.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/always_simple.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, 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 @@ -27,7 +28,8 @@ namespace detail { namespace is_simple template struct always_simple { - static inline bool apply(Geometry const&) + template + static inline bool apply(Geometry const&, Strategy const&) { return true; } diff --git a/include/boost/geometry/algorithms/detail/is_simple/areal.hpp b/include/boost/geometry/algorithms/detail/is_simple/areal.hpp index a2322e483..d4d6db9bc 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/areal.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/areal.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 @@ -37,6 +38,12 @@ namespace detail { namespace is_simple template struct is_simple_ring { + template + static inline bool apply(Ring const& ring, Strategy const&) + { + return apply(ring); + } + static inline bool apply(Ring const& ring) { simplicity_failure_policy policy; @@ -69,6 +76,12 @@ private: } public: + template + static inline bool apply(Polygon const& polygon, Strategy const&) + { + return apply(polygon); + } + static inline bool apply(Polygon const& polygon) { return @@ -119,7 +132,8 @@ struct is_simple template struct is_simple { - static inline bool apply(MultiPolygon const& multipolygon) + template + static inline bool apply(MultiPolygon const& multipolygon, Strategy const&) { return detail::check_iterator_range diff --git a/include/boost/geometry/algorithms/detail/is_simple/interface.hpp b/include/boost/geometry/algorithms/detail/is_simple/interface.hpp index 6d425232b..af0127dc7 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/interface.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/interface.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, 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 @@ -17,46 +18,106 @@ #include #include +#include +#include +#include namespace boost { namespace geometry { +namespace resolve_strategy +{ -namespace resolve_variant { +struct is_simple +{ + template + static inline bool apply(Geometry const& geometry, + Strategy const& strategy) + { + return dispatch::is_simple::apply(geometry, strategy); + } + + template + static inline bool apply(Geometry const& geometry, + default_strategy) + { + // NOTE: Currently the strategy is only used for Linear geometries + typedef typename strategy::intersection::services::default_strategy + < + typename cs_tag::type + >::type strategy_type; + + return dispatch::is_simple::apply(geometry, strategy_type()); + } +}; + +} // namespace resolve_strategy + +namespace resolve_variant +{ template struct is_simple { - static inline bool apply(Geometry const& geometry) + template + static inline bool apply(Geometry const& geometry, Strategy const& strategy) { concepts::check(); - return dispatch::is_simple::apply(geometry); + + return resolve_strategy::is_simple::apply(geometry, strategy); } }; template struct is_simple > { + 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_simple::apply(geometry); + return is_simple::apply(geometry, m_strategy); } }; + template static inline bool - apply(boost::variant const& geometry) + apply(boost::variant const& geometry, + Strategy const& strategy) { - return boost::apply_visitor(visitor(), geometry); + return boost::apply_visitor(visitor(strategy), geometry); } }; } // namespace resolve_variant +/*! +\brief \brief_check{is simple} +\ingroup is_simple +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Is_simple} +\param geometry \param_geometry +\param strategy \param_strategy{is_simple} +\return \return_check{is simple} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/is_simple.qbk]} +*/ +template +inline bool is_simple(Geometry const& geometry, Strategy const& strategy) +{ + return resolve_variant::is_simple::apply(geometry, strategy); +} + /*! \brief \brief_check{is simple} @@ -70,7 +131,7 @@ struct is_simple > template inline bool is_simple(Geometry const& geometry) { - return resolve_variant::is_simple::apply(geometry); + return resolve_variant::is_simple::apply(geometry, default_strategy()); } diff --git a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp index 6c469f07f..52b9d9d1c 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -189,8 +189,8 @@ private: }; -template -inline bool has_self_intersections(Linear const& linear) +template +inline bool has_self_intersections(Linear const& linear, Strategy const& strategy) { typedef typename point_type::type point_type; @@ -217,16 +217,11 @@ inline bool has_self_intersections(Linear const& linear) is_acceptable_turn > interrupt_policy(predicate); - typedef typename strategy::intersection::services::default_strategy - < - typename cs_tag::type - >::type strategy_type; - detail::self_get_turn_points::get_turns < turn_policy >::apply(linear, - strategy_type(), + strategy, detail::no_rescale_policy(), turns, interrupt_policy); @@ -252,8 +247,19 @@ struct is_simple_linestring && ! detail::is_valid::has_spikes < Linestring, closed - >::apply(linestring, policy) - && ! (CheckSelfIntersections && has_self_intersections(linestring)); + >::apply(linestring, policy); + } +}; + +template +struct is_simple_linestring +{ + template + static inline bool apply(Linestring const& linestring, + Strategy const& strategy) + { + return is_simple_linestring::apply(linestring) + && ! has_self_intersections(linestring, strategy); } }; @@ -261,7 +267,9 @@ struct is_simple_linestring template struct is_simple_multilinestring { - static inline bool apply(MultiLinestring const& multilinestring) + template + static inline bool apply(MultiLinestring const& multilinestring, + Strategy const& strategy) { // check each of the linestrings for simplicity // but do not compute self-intersections yet; these will be @@ -281,7 +289,7 @@ struct is_simple_multilinestring return false; } - return ! has_self_intersections(multilinestring); + return ! has_self_intersections(multilinestring, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/is_simple/multipoint.hpp b/include/boost/geometry/algorithms/detail/is_simple/multipoint.hpp index f9f43d1cd..61f0bc913 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/multipoint.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/multipoint.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 @@ -38,7 +39,8 @@ namespace detail { namespace is_simple template struct is_simple_multipoint { - static inline bool apply(MultiPoint const& multipoint) + template + static inline bool apply(MultiPoint const& multipoint, Strategy const&) { if (boost::empty(multipoint)) { diff --git a/include/boost/geometry/algorithms/detail/is_valid/box.hpp b/include/boost/geometry/algorithms/detail/is_valid/box.hpp index 863ce625f..69a4d4e78 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/box.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/box.hpp @@ -1,6 +1,6 @@ // 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 @@ -71,8 +71,8 @@ struct has_valid_corners template struct is_valid_box { - template - static inline bool apply(Box const& box, VisitPolicy& visitor) + template + static inline bool apply(Box const& box, VisitPolicy& visitor, Strategy const&) { return ! has_invalid_coordinate::apply(box, visitor) diff --git a/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp b/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp index 9f278ac04..b91dc6a69 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp @@ -48,11 +48,6 @@ class has_valid_self_turns private: typedef typename point_type::type point_type; - typedef typename strategy::intersection::services::default_strategy - < - typename cs_tag::type - >::type intersection_strategy_type; - typedef typename geometry::rescale_policy_type < point_type @@ -75,15 +70,14 @@ public: > turn_type; // returns true if all turns are valid - template + template static inline bool apply(Geometry const& geometry, Turns& turns, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { boost::ignore_unused(visitor); - intersection_strategy_type intersection_strategy; - rescale_policy_type robust_policy = geometry::get_rescale_policy(geometry); @@ -93,7 +87,7 @@ public: > interrupt_policy; geometry::self_turns(geometry, - intersection_strategy, + strategy, robust_policy, turns, interrupt_policy); @@ -110,11 +104,11 @@ public: } // returns true if all turns are valid - template - static inline bool apply(Geometry const& geometry, VisitPolicy& visitor) + template + static inline bool apply(Geometry const& geometry, VisitPolicy& visitor, Strategy const& strategy) { std::vector turns; - return apply(geometry, turns, visitor); + return apply(geometry, turns, visitor, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/is_valid/interface.hpp b/include/boost/geometry/algorithms/detail/is_valid/interface.hpp index 5a04a9282..ee013377c 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/interface.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/interface.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 @@ -23,48 +24,88 @@ #include #include #include +#include +#include namespace boost { namespace geometry { + +namespace resolve_strategy +{ +struct is_valid +{ + template + static inline bool apply(Geometry const& geometry, + VisitPolicy& visitor, + Strategy const& strategy) + { + return dispatch::is_valid::apply(geometry, visitor, strategy); + } -namespace resolve_variant { + template + static inline bool apply(Geometry const& geometry, + VisitPolicy& visitor, + default_strategy) + { + // NOTE: Currently the strategy is only used for Areal geometries + typedef typename strategy::intersection::services::default_strategy + < + typename cs_tag::type + >::type strategy_type; + + return dispatch::is_valid::apply(geometry, visitor, strategy_type()); + } +}; + +} // namespace resolve_strategy + +namespace resolve_variant +{ template struct is_valid { - template - static inline bool apply(Geometry const& geometry, VisitPolicy& visitor) + template + static inline bool apply(Geometry const& geometry, + VisitPolicy& visitor, + Strategy const& strategy) { concepts::check(); - return dispatch::is_valid::apply(geometry, visitor); + + return resolve_strategy::is_valid::apply(geometry, visitor, strategy); } }; template struct is_valid > { - template + template struct visitor : boost::static_visitor { - visitor(VisitPolicy& policy) : m_policy(policy) {} + visitor(VisitPolicy& policy, Strategy const& strategy) + : m_policy(policy) + , m_strategy(strategy) + {} template bool operator()(Geometry const& geometry) const { - return is_valid::apply(geometry, m_policy); + return is_valid::apply(geometry, m_policy, m_strategy); } VisitPolicy& m_policy; + Strategy const& m_strategy; }; - template + template static inline bool apply(boost::variant const& geometry, - VisitPolicy& policy_visitor) + VisitPolicy& policy_visitor, + Strategy const& strategy) { - return boost::apply_visitor(visitor(policy_visitor), + return boost::apply_visitor(visitor(policy_visitor, strategy), geometry); } }; @@ -73,13 +114,38 @@ struct is_valid > // Undocumented for now -template -inline bool is_valid(Geometry const& geometry, VisitPolicy& visitor) +template +inline bool is_valid(Geometry const& geometry, + VisitPolicy& visitor, + Strategy const& strategy) { - return resolve_variant::is_valid::apply(geometry, visitor); + return resolve_variant::is_valid::apply(geometry, visitor, strategy); } +/*! +\brief \brief_check{is valid (in the OGC sense)} +\ingroup is_valid +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Is_valid} +\param geometry \param_geometry +\param strategy \param_strategy{is_valid} +\return \return_check{is valid (in the OGC sense); +furthermore, the following geometries are considered valid: +multi-geometries with no elements, +linear geometries containing spikes, +areal geometries with duplicate (consecutive) points} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/is_valid.qbk]} +*/ +template +inline bool is_valid(Geometry const& geometry, Strategy const& strategy) +{ + is_valid_default_policy<> visitor; + return resolve_variant::is_valid::apply(geometry, visitor, strategy); +} + /*! \brief \brief_check{is valid (in the OGC sense)} \ingroup is_valid @@ -96,11 +162,37 @@ inline bool is_valid(Geometry const& geometry, VisitPolicy& visitor) template inline bool is_valid(Geometry const& geometry) { - is_valid_default_policy<> policy_visitor; - return geometry::is_valid(geometry, policy_visitor); + return is_valid(geometry, default_strategy()); } +/*! +\brief \brief_check{is valid (in the OGC sense)} +\ingroup is_valid +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Is_valid} +\param geometry \param_geometry +\param failure An enumeration value indicating that the geometry is + valid or not, and if not valid indicating the reason why +\param strategy \param_strategy{is_valid} +\return \return_check{is valid (in the OGC sense); + furthermore, the following geometries are considered valid: + multi-geometries with no elements, + linear geometries containing spikes, + areal geometries with duplicate (consecutive) points} + +\qbk{distinguish,with failure value and strategy} +\qbk{[include reference/algorithms/is_valid_with_failure.qbk]} +*/ +template +inline bool is_valid(Geometry const& geometry, validity_failure_type& failure, Strategy const& strategy) +{ + failure_type_policy<> visitor; + bool result = resolve_variant::is_valid::apply(geometry, visitor, strategy); + failure = visitor.failure(); + return result; +} + /*! \brief \brief_check{is valid (in the OGC sense)} \ingroup is_valid @@ -120,13 +212,38 @@ inline bool is_valid(Geometry const& geometry) template inline bool is_valid(Geometry const& geometry, validity_failure_type& failure) { - failure_type_policy<> policy_visitor; - bool result = geometry::is_valid(geometry, policy_visitor); - failure = policy_visitor.failure(); - return result; + return is_valid(geometry, failure, default_strategy()); } +/*! +\brief \brief_check{is valid (in the OGC sense)} +\ingroup is_valid +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Is_valid} +\param geometry \param_geometry +\param message A string containing a message stating if the geometry + is valid or not, and if not valid a reason why +\param strategy \param_strategy{is_valid} +\return \return_check{is valid (in the OGC sense); + furthermore, the following geometries are considered valid: + multi-geometries with no elements, + linear geometries containing spikes, + areal geometries with duplicate (consecutive) points} + +\qbk{distinguish,with message and strategy} +\qbk{[include reference/algorithms/is_valid_with_message.qbk]} +*/ +template +inline bool is_valid(Geometry const& geometry, std::string& message, Strategy const& strategy) +{ + std::ostringstream stream; + failing_reason_policy<> visitor(stream); + bool result = resolve_variant::is_valid::apply(geometry, visitor, strategy); + message = stream.str(); + return result; +} + /*! \brief \brief_check{is valid (in the OGC sense)} \ingroup is_valid @@ -146,11 +263,7 @@ inline bool is_valid(Geometry const& geometry, validity_failure_type& failure) template inline bool is_valid(Geometry const& geometry, std::string& message) { - std::ostringstream stream; - failing_reason_policy<> policy_visitor(stream); - bool result = geometry::is_valid(geometry, policy_visitor); - message = stream.str(); - return result; + return is_valid(geometry, message, default_strategy()); } diff --git a/include/boost/geometry/algorithms/detail/is_valid/linear.hpp b/include/boost/geometry/algorithms/detail/is_valid/linear.hpp index a49e07723..6bc6b86cf 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/linear.hpp @@ -1,6 +1,6 @@ // 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 @@ -77,6 +77,14 @@ struct is_valid_linestring } return ! has_spikes::apply(linestring, visitor); } + + template + static inline bool apply(Linestring const& linestring, + VisitPolicy& visitor, + Strategy const&) + { + return apply(linestring, visitor); + } }; @@ -142,9 +150,10 @@ private: }; public: - template + template static inline bool apply(MultiLinestring const& multilinestring, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const&) { if (BOOST_GEOMETRY_CONDITION( AllowEmptyMultiGeometries && boost::empty(multilinestring))) diff --git a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp index eafe07841..84dacc57f 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp @@ -237,23 +237,28 @@ private: } - template + template struct per_polygon { - per_polygon(VisitPolicy& policy) : m_policy(policy) {} + per_polygon(VisitPolicy& policy, Strategy const& strategy) + : m_policy(policy) + , m_strategy(strategy) + {} template inline bool apply(Polygon const& polygon) const { - return base::apply(polygon, m_policy); + return base::apply(polygon, m_policy, m_strategy); } VisitPolicy& m_policy; + Strategy const& m_strategy; }; public: - template + template static inline bool apply(MultiPolygon const& multipolygon, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { typedef debug_validity_phase debug_phase; @@ -268,11 +273,11 @@ public: if (! detail::check_iterator_range < - per_polygon, + per_polygon, false // do not check for empty multipolygon (done above) >::apply(boost::begin(multipolygon), boost::end(multipolygon), - per_polygon(visitor))) + per_polygon(visitor, strategy))) { return false; } @@ -285,7 +290,7 @@ public: std::deque turns; bool has_invalid_turns = - ! has_valid_turns::apply(multipolygon, turns, visitor); + ! has_valid_turns::apply(multipolygon, turns, visitor, strategy); debug_print_turns(turns.begin(), turns.end()); if (has_invalid_turns) diff --git a/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp b/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp index 51035f7a7..f77f7a35e 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp @@ -1,6 +1,6 @@ // 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 @@ -36,8 +36,8 @@ namespace dispatch template struct is_valid { - template - static inline bool apply(Point const& point, VisitPolicy& visitor) + template + static inline bool apply(Point const& point, VisitPolicy& visitor, Strategy const&) { boost::ignore_unused(visitor); return ! detail::is_valid::has_invalid_coordinate @@ -56,9 +56,10 @@ struct is_valid template struct is_valid { - template + template static inline bool apply(MultiPoint const& multipoint, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const&) { boost::ignore_unused(multipoint, visitor); diff --git a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp index 182571c56..f7e22fb8d 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/polygon.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 @@ -74,10 +75,13 @@ class is_valid_polygon protected: typedef debug_validity_phase debug_phase; - template + template struct per_ring { - per_ring(VisitPolicy& policy) : m_policy(policy) {} + per_ring(VisitPolicy& policy, Strategy const& strategy) + : m_policy(policy) + , m_strategy(strategy) + {} template inline bool apply(Ring const& ring) const @@ -85,30 +89,34 @@ protected: return detail::is_valid::is_valid_ring < Ring, false, true - >::apply(ring, m_policy); + >::apply(ring, m_policy, m_strategy); } VisitPolicy& m_policy; + Strategy const& m_strategy; }; - template + template static bool has_valid_interior_rings(InteriorRings const& interior_rings, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { return detail::check_iterator_range < - per_ring, + per_ring, true // allow for empty interior ring range >::apply(boost::begin(interior_rings), boost::end(interior_rings), - per_ring(visitor)); + per_ring(visitor, strategy)); } struct has_valid_rings { - template - static inline bool apply(Polygon const& polygon, VisitPolicy& visitor) + template + static inline bool apply(Polygon const& polygon, + VisitPolicy& visitor, + Strategy const& strategy) { typedef typename ring_type::type ring_type; @@ -119,7 +127,7 @@ protected: < ring_type, false // do not check self intersections - >::apply(exterior_ring(polygon), visitor)) + >::apply(exterior_ring(polygon), visitor, strategy)) { return false; } @@ -128,7 +136,8 @@ protected: debug_phase::apply(2); return has_valid_interior_rings(geometry::interior_rings(polygon), - visitor); + visitor, + strategy); } }; @@ -344,10 +353,12 @@ protected: }; public: - template - static inline bool apply(Polygon const& polygon, VisitPolicy& visitor) + template + static inline bool apply(Polygon const& polygon, + VisitPolicy& visitor, + Strategy const& strategy) { - if (! has_valid_rings::apply(polygon, visitor)) + if (! has_valid_rings::apply(polygon, visitor, strategy)) { return false; } @@ -364,7 +375,7 @@ public: std::deque turns; bool has_invalid_turns - = ! has_valid_turns::apply(polygon, turns, visitor); + = ! has_valid_turns::apply(polygon, turns, visitor, strategy); debug_print_turns(turns.begin(), turns.end()); if (has_invalid_turns) diff --git a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp index 925c03a47..9ab68fdc4 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp @@ -1,6 +1,6 @@ // 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 @@ -101,35 +101,33 @@ struct ring_area_predicate template struct is_properly_oriented { - typedef typename point_type::type point_type; - - typedef typename strategy::area::services::default_strategy - < - typename cs_tag::type, - point_type - >::type strategy_type; - - typedef detail::area::ring_area - < - order_as_direction::value>::value, - geometry::closure::value - > ring_area_type; - - typedef typename default_area_result::type area_result_type; - - template - static inline bool apply(Ring const& ring, VisitPolicy& visitor) + template + static inline bool apply(Ring const& ring, VisitPolicy& visitor, + Strategy const& strategy) { boost::ignore_unused(visitor); + typedef typename point_type::type point_type; + + typedef detail::area::ring_area + < + order_as_direction::value>::value, + geometry::closure::value + > ring_area_type; + + typedef typename default_area_result::type area_result_type; + typename ring_area_predicate < area_result_type, IsInteriorRing >::type predicate; // Check area - area_result_type const zero = area_result_type(); - if (predicate(ring_area_type::apply(ring, strategy_type()), zero)) + area_result_type const zero = 0; + area_result_type const area + = ring_area_type::apply(ring, + strategy.template get_area_strategy()); + if (predicate(area, zero)) { return visitor.template apply(); } @@ -150,8 +148,9 @@ template > struct is_valid_ring { - template - static inline bool apply(Ring const& ring, VisitPolicy& visitor) + template + static inline bool apply(Ring const& ring, VisitPolicy& visitor, + Strategy const& strategy) { // return invalid if any of the following condition holds: // (a) the ring's point coordinates are not invalid (e.g., NaN) @@ -198,8 +197,8 @@ struct is_valid_ring && ! has_duplicates::apply(ring, visitor) && ! has_spikes::apply(ring, visitor) && (! CheckSelfIntersections - || has_valid_self_turns::apply(ring, visitor)) - && is_properly_oriented::apply(ring, visitor); + || has_valid_self_turns::apply(ring, visitor, strategy)) + && is_properly_oriented::apply(ring, visitor, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/is_valid/segment.hpp b/include/boost/geometry/algorithms/detail/is_valid/segment.hpp index f92f73381..30cbf7afd 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/segment.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/segment.hpp @@ -1,6 +1,6 @@ // 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 @@ -44,8 +44,8 @@ namespace dispatch template struct is_valid { - template - static inline bool apply(Segment const& segment, VisitPolicy& visitor) + template + static inline bool apply(Segment const& segment, VisitPolicy& visitor, Strategy const&) { boost::ignore_unused(visitor); 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 61f2c737b..8540ef98a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -272,6 +272,7 @@ struct self_get_turn_points \tparam Turns type of intersection container (e.g. vector of "intersection/turn point"'s) \param geometry geometry + \param strategy strategy to be used \param robust_policy policy to handle robustness issues \param turns container which will contain intersection points \param interrupt_policy policy determining if process is stopped diff --git a/include/boost/geometry/algorithms/detail/relate/interface.hpp b/include/boost/geometry/algorithms/detail/relate/interface.hpp index 29d346681..3575fe2bc 100644 --- a/include/boost/geometry/algorithms/detail/relate/interface.hpp +++ b/include/boost/geometry/algorithms/detail/relate/interface.hpp @@ -375,6 +375,7 @@ struct relate< \param strategy \param_strategy{relate} \return true if the relation is compatible with the mask, false otherwise. +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/relate.qbk]} */ template diff --git a/include/boost/geometry/algorithms/detail/relation/interface.hpp b/include/boost/geometry/algorithms/detail/relation/interface.hpp index 00a784b3d..83d27ed72 100644 --- a/include/boost/geometry/algorithms/detail/relation/interface.hpp +++ b/include/boost/geometry/algorithms/detail/relation/interface.hpp @@ -175,6 +175,7 @@ struct relation \param strategy \param_strategy{relation} \return The DE-9IM matrix expressing the relation between geometries. +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/relation.qbk]} */ template diff --git a/include/boost/geometry/algorithms/difference.hpp b/include/boost/geometry/algorithms/difference.hpp index 2d0afffe8..c11ceca24 100644 --- a/include/boost/geometry/algorithms/difference.hpp +++ b/include/boost/geometry/algorithms/difference.hpp @@ -4,6 +4,7 @@ // 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, @@ -13,10 +14,16 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DIFFERENCE_HPP #define BOOST_GEOMETRY_ALGORITHMS_DIFFERENCE_HPP -#include + +#include +#include +#include #include #include +#include +#include + namespace boost { namespace geometry { @@ -101,18 +108,14 @@ inline OutputIterator difference_insert(Geometry1 const& geometry1, RobustPolicy const& robust_policy, OutputIterator out) { - concepts::check(); - concepts::check(); - concepts::check(); - - typename strategy::relate::services::default_strategy + typedef typename strategy::relate::services::default_strategy < Geometry1, Geometry2 - >::type strategy; + >::type strategy_type; return difference_insert(geometry1, geometry2, - robust_policy, out, strategy); + robust_policy, out, strategy_type()); } @@ -120,6 +123,251 @@ inline OutputIterator difference_insert(Geometry1 const& geometry1, #endif // DOXYGEN_NO_DETAIL +namespace resolve_strategy { + +struct difference +{ + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection, + typename Strategy + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + Strategy const& strategy) + { + typedef typename boost::range_value::type geometry_out; + + detail::difference::difference_insert( + geometry1, geometry2, robust_policy, + range::back_inserter(output_collection), + strategy); + } + + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + default_strategy) + { + typedef typename boost::range_value::type geometry_out; + + detail::difference::difference_insert( + geometry1, geometry2, robust_policy, + range::back_inserter(output_collection)); + } +}; + +} // resolve_strategy + + +namespace resolve_variant +{ + +template +struct difference +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + typedef typename geometry::rescale_overlay_policy_type + < + Geometry1, + Geometry2 + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, + geometry2); + + resolve_strategy::difference::apply(geometry1, geometry2, + robust_policy, + output_collection, + strategy); + } +}; + + +template +struct difference, Geometry2> +{ + template + struct visitor: static_visitor<> + { + Geometry2 const& m_geometry2; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + : m_geometry2(geometry2) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1) const + { + difference + < + Geometry1, + Geometry2 + >::apply(geometry1, m_geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry2, + output_collection, + strategy), + geometry1); + } +}; + + +template +struct difference > +{ + template + struct visitor: static_visitor<> + { + Geometry1 const& m_geometry1; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, + Collection& output_collection, + Strategy const& strategy) + : m_geometry1(geometry1) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry2 const& geometry2) const + { + difference + < + Geometry1, + Geometry2 + >::apply(m_geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(Geometry1 const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry1, + output_collection, + strategy), + geometry2); + } +}; + + +template +struct difference, variant > +{ + template + struct visitor: static_visitor<> + { + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Collection& output_collection, Strategy const& strategy) + : m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + difference + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(output_collection, + strategy), + geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief_calc2{difference} +\ingroup difference +\details \details_calc2{difference, spatial set theoretic difference}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Collection \tparam_output_collection +\tparam Strategy \tparam_strategy{Difference} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param output_collection the output collection +\param strategy \param_strategy{difference} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/difference.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename Collection, + typename Strategy +> +inline void difference(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) +{ + resolve_variant::difference + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, output_collection, strategy); +} + /*! \brief_calc2{difference} @@ -144,25 +392,11 @@ inline void difference(Geometry1 const& geometry1, Geometry2 const& geometry2, Collection& output_collection) { - concepts::check(); - concepts::check(); - - typedef typename boost::range_value::type geometry_out; - concepts::check(); - - typedef typename geometry::rescale_overlay_policy_type + resolve_variant::difference < Geometry1, Geometry2 - >::type rescale_policy_type; - - rescale_policy_type robust_policy - = geometry::get_rescale_policy(geometry1, - geometry2); - - detail::difference::difference_insert( - geometry1, geometry2, robust_policy, - range::back_inserter(output_collection)); + >::apply(geometry1, geometry2, output_collection, default_strategy()); } diff --git a/include/boost/geometry/algorithms/equals.hpp b/include/boost/geometry/algorithms/equals.hpp index 47eb7134c..1479ea66f 100644 --- a/include/boost/geometry/algorithms/equals.hpp +++ b/include/boost/geometry/algorithms/equals.hpp @@ -138,24 +138,32 @@ struct segment_segment struct area_check { - template - static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + template + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) { return geometry::math::equals( - geometry::area(geometry1), - geometry::area(geometry2)); + geometry::area(geometry1, + strategy.template get_area_strategy()), + geometry::area(geometry2, + strategy.template get_area_strategy())); } }; struct length_check { - template - static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + template + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) { return geometry::math::equals( - geometry::length(geometry1), - geometry::length(geometry2)); + geometry::length(geometry1, + strategy.template get_distance_strategy()), + geometry::length(geometry2, + strategy.template get_distance_strategy())); } }; @@ -184,9 +192,11 @@ template struct equals_by_collection { template - static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const&) + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) { - if (! TrivialCheck::apply(geometry1, geometry2)) + if (! TrivialCheck::apply(geometry1, geometry2, strategy)) { return false; } @@ -321,6 +331,20 @@ struct equals : detail::equals::point_point<0, DimensionCount> {}; +template +struct equals + : detail::equals::equals_by_relate +{}; + +template +struct equals + : detail::equals::equals_by_relate +{}; + +template +struct equals + : detail::equals::equals_by_relate +{}; template struct equals @@ -601,8 +625,8 @@ struct equals< \param strategy \param_strategy{equals} \return \return_check2{are spatially equal} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/equals.qbk]} - */ template inline bool equals(Geometry1 const& geometry1, @@ -631,7 +655,6 @@ inline bool equals(Geometry1 const& geometry1, \return \return_check2{are spatially equal} \qbk{[include reference/algorithms/equals.qbk]} - */ template inline bool equals(Geometry1 const& geometry1, Geometry2 const& geometry2) diff --git a/include/boost/geometry/algorithms/intersects.hpp b/include/boost/geometry/algorithms/intersects.hpp index e54b00503..12ae169f1 100644 --- a/include/boost/geometry/algorithms/intersects.hpp +++ b/include/boost/geometry/algorithms/intersects.hpp @@ -97,7 +97,7 @@ inline bool intersects(Geometry const& geometry) \param strategy \param_strategy{intersects} \return \return_check2{intersect each other} -\qbk{distinguish,two geometries} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/intersects.qbk]} */ template diff --git a/include/boost/geometry/algorithms/overlaps.hpp b/include/boost/geometry/algorithms/overlaps.hpp index 800d6dac1..bedf17599 100644 --- a/include/boost/geometry/algorithms/overlaps.hpp +++ b/include/boost/geometry/algorithms/overlaps.hpp @@ -180,6 +180,7 @@ struct overlaps \param strategy \param_strategy{overlaps} \return \return_check2{overlap} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/overlaps.qbk]} */ template diff --git a/include/boost/geometry/algorithms/sym_difference.hpp b/include/boost/geometry/algorithms/sym_difference.hpp index 5d5bd904b..725230cd5 100644 --- a/include/boost/geometry/algorithms/sym_difference.hpp +++ b/include/boost/geometry/algorithms/sym_difference.hpp @@ -15,13 +15,21 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_SYM_DIFFERENCE_HPP #define BOOST_GEOMETRY_ALGORITHMS_SYM_DIFFERENCE_HPP + #include #include #include +#include +#include +#include + #include #include #include +#include +#include +#include namespace boost { namespace geometry @@ -289,6 +297,253 @@ inline OutputIterator sym_difference_insert(Geometry1 const& geometry1, #endif // DOXYGEN_NO_DETAIL +namespace resolve_strategy { + +struct sym_difference +{ + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection, + typename Strategy + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + Strategy const& strategy) + { + typedef typename boost::range_value::type geometry_out; + + detail::sym_difference::sym_difference_insert( + geometry1, geometry2, robust_policy, + range::back_inserter(output_collection), + strategy); + } + + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + default_strategy) + { + typedef typename boost::range_value::type geometry_out; + + detail::sym_difference::sym_difference_insert( + geometry1, geometry2, robust_policy, + range::back_inserter(output_collection)); + } +}; + +} // resolve_strategy + + +namespace resolve_variant +{ + +template +struct sym_difference +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + typedef typename geometry::rescale_overlay_policy_type + < + Geometry1, + Geometry2 + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, + geometry2); + + resolve_strategy::sym_difference::apply(geometry1, geometry2, + robust_policy, + output_collection, + strategy); + } +}; + + +template +struct sym_difference, Geometry2> +{ + template + struct visitor: static_visitor<> + { + Geometry2 const& m_geometry2; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + : m_geometry2(geometry2) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1) const + { + sym_difference + < + Geometry1, + Geometry2 + >::apply(geometry1, m_geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry2, + output_collection, + strategy), + geometry1); + } +}; + + +template +struct sym_difference > +{ + template + struct visitor: static_visitor<> + { + Geometry1 const& m_geometry1; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, + Collection& output_collection, + Strategy const& strategy) + : m_geometry1(geometry1) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry2 const& geometry2) const + { + sym_difference + < + Geometry1, + Geometry2 + >::apply(m_geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(Geometry1 const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry1, + output_collection, + strategy), + geometry2); + } +}; + + +template +struct sym_difference, variant > +{ + template + struct visitor: static_visitor<> + { + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Collection& output_collection, Strategy const& strategy) + : m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + sym_difference + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(output_collection, + strategy), + geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief \brief_calc2{symmetric difference} +\ingroup sym_difference +\details \details_calc2{symmetric difference, spatial set theoretic symmetric difference (XOR)}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Collection output collection, either a multi-geometry, + or a std::vector / std::deque etc +\tparam Strategy \tparam_strategy{Sym_difference} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param output_collection the output collection +\param strategy \param_strategy{sym_difference} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/sym_difference.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename Collection, + typename Strategy +> +inline void sym_difference(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) +{ + resolve_variant::sym_difference + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, output_collection, strategy); +} + + /*! \brief \brief_calc2{symmetric difference} \ingroup sym_difference @@ -310,26 +565,14 @@ template typename Collection > inline void sym_difference(Geometry1 const& geometry1, - Geometry2 const& geometry2, Collection& output_collection) + Geometry2 const& geometry2, + Collection& output_collection) { - concepts::check(); - concepts::check(); - - typedef typename boost::range_value::type geometry_out; - concepts::check(); - - typedef typename geometry::rescale_overlay_policy_type + resolve_variant::sym_difference < Geometry1, Geometry2 - >::type rescale_policy_type; - - rescale_policy_type robust_policy - = geometry::get_rescale_policy(geometry1, geometry2); - - detail::sym_difference::sym_difference_insert( - geometry1, geometry2, robust_policy, - range::back_inserter(output_collection)); + >::apply(geometry1, geometry2, output_collection, default_strategy()); } diff --git a/include/boost/geometry/algorithms/touches.hpp b/include/boost/geometry/algorithms/touches.hpp index 83f2b1dfa..49e104d25 100644 --- a/include/boost/geometry/algorithms/touches.hpp +++ b/include/boost/geometry/algorithms/touches.hpp @@ -687,7 +687,7 @@ inline bool touches(Geometry1 const& geometry1, Geometry2 const& geometry2) \param strategy \param_strategy{touches} \return \return_check2{touch each other} -\qbk{distinguish,two geometries} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/touches.qbk]} */ template diff --git a/include/boost/geometry/algorithms/union.hpp b/include/boost/geometry/algorithms/union.hpp index e2a7d2b96..d3a2daf66 100644 --- a/include/boost/geometry/algorithms/union.hpp +++ b/include/boost/geometry/algorithms/union.hpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -234,6 +236,266 @@ inline OutputIterator union_insert(Geometry1 const& geometry1, #endif // DOXYGEN_NO_DETAIL +namespace resolve_strategy { + +struct union_ +{ + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection, + typename Strategy + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + Strategy const& strategy) + { + typedef typename boost::range_value::type geometry_out; + + dispatch::union_insert + < + Geometry1, Geometry2, geometry_out + >::apply(geometry1, geometry2, robust_policy, + range::back_inserter(output_collection), + strategy); + } + + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + default_strategy) + { + typedef typename boost::range_value::type geometry_out; + + typedef typename strategy::intersection::services::default_strategy + < + typename cs_tag::type + >::type strategy_type; + + dispatch::union_insert + < + Geometry1, Geometry2, geometry_out + >::apply(geometry1, geometry2, robust_policy, + range::back_inserter(output_collection), + strategy_type()); + } +}; + +} // resolve_strategy + + +namespace resolve_variant +{ + +template +struct union_ +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + concepts::check(); + concepts::check(); + concepts::check::type>(); + + typedef typename geometry::rescale_overlay_policy_type + < + Geometry1, + Geometry2 + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, + geometry2); + + resolve_strategy::union_::apply(geometry1, geometry2, + robust_policy, + output_collection, + strategy); + } +}; + + +template +struct union_, Geometry2> +{ + template + struct visitor: static_visitor<> + { + Geometry2 const& m_geometry2; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + : m_geometry2(geometry2) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1) const + { + union_ + < + Geometry1, + Geometry2 + >::apply(geometry1, m_geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry2, + output_collection, + strategy), + geometry1); + } +}; + + +template +struct union_ > +{ + template + struct visitor: static_visitor<> + { + Geometry1 const& m_geometry1; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, + Collection& output_collection, + Strategy const& strategy) + : m_geometry1(geometry1) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry2 const& geometry2) const + { + union_ + < + Geometry1, + Geometry2 + >::apply(m_geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(Geometry1 const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry1, + output_collection, + strategy), + geometry2); + } +}; + + +template +struct union_, variant > +{ + template + struct visitor: static_visitor<> + { + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Collection& output_collection, Strategy const& strategy) + : m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + union_ + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(output_collection, + strategy), + geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief Combines two geometries which each other +\ingroup union +\details \details_calc2{union, spatial set theoretic union}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Collection output collection, either a multi-geometry, + or a std::vector / std::deque etc +\tparam Strategy \tparam_strategy{Union_} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param output_collection the output collection +\param strategy \param_strategy{union_} +\note Called union_ because union is a reserved word. + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/union.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename Collection, + typename Strategy +> +inline void union_(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) +{ + resolve_variant::union_ + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, output_collection, strategy); +} /*! @@ -258,17 +520,14 @@ template typename Collection > inline void union_(Geometry1 const& geometry1, - Geometry2 const& geometry2, - Collection& output_collection) + Geometry2 const& geometry2, + Collection& output_collection) { - concepts::check(); - concepts::check(); - - typedef typename boost::range_value::type geometry_out; - concepts::check(); - - detail::union_::union_insert(geometry1, geometry2, - range::back_inserter(output_collection)); + resolve_variant::union_ + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, output_collection, default_strategy()); } diff --git a/include/boost/geometry/arithmetic/normalize.hpp b/include/boost/geometry/arithmetic/normalize.hpp new file mode 100644 index 000000000..7dfdbd2b0 --- /dev/null +++ b/include/boost/geometry/arithmetic/normalize.hpp @@ -0,0 +1,71 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2016, 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_ARITHMETIC_NORMALIZE_HPP +#define BOOST_GEOMETRY_ARITHMETIC_NORMALIZE_HPP + + +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +inline typename coordinate_type::type vec_length_sqr(Point const& pt) +{ + return dot_product(pt, pt); +} + +template +inline typename coordinate_type::type vec_length(Point const& pt) +{ + // NOTE: hypot() could be used instead of sqrt() + return math::sqrt(dot_product(pt, pt)); +} + +template +inline bool vec_normalize(Point & pt, typename coordinate_type::type & len) +{ + typedef typename coordinate_type::type coord_t; + + coord_t const c0 = 0; + len = vec_length(pt); + + if (math::equals(len, c0)) + { + return false; + } + + divide_value(pt, len); + return true; +} + +template +inline bool vec_normalize(Point & pt) +{ + typedef typename coordinate_type::type coord_t; + coord_t len; + return vec_normalize(pt, len); +} + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ARITHMETIC_NORMALIZE_HPP diff --git a/include/boost/geometry/core/srs.hpp b/include/boost/geometry/core/srs.hpp index bf1b4e28a..cd637c1e9 100644 --- a/include/boost/geometry/core/srs.hpp +++ b/include/boost/geometry/core/srs.hpp @@ -4,8 +4,8 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2016. +// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -51,7 +51,7 @@ public: spheroid() : m_a(RadiusType(6378137.0)) - , m_b(RadiusType(6356752.314245)) + , m_b(RadiusType(6356752.3142451793)) {} template diff --git a/include/boost/geometry/extensions/gis/io/wkb/detail/ogc.hpp b/include/boost/geometry/extensions/gis/io/wkb/detail/ogc.hpp index b85447347..523461009 100644 --- a/include/boost/geometry/extensions/gis/io/wkb/detail/ogc.hpp +++ b/include/boost/geometry/extensions/gis/io/wkb/detail/ogc.hpp @@ -68,12 +68,10 @@ struct geometry_type_ogc { point = 1, linestring = 2, - polygon = 3 - - // TODO: Not implemented - //multipoint = 4, - //multilinestring = 5, - //multipolygon = 6, + polygon = 3, + multipoint = 4, + multilinestring = 5, + multipolygon = 6, //collection = 7 }; }; @@ -169,6 +167,21 @@ struct geometry_type : geometry_type_impl {}; +template +struct geometry_type + : geometry_type_impl +{}; + +template +struct geometry_type + : geometry_type_impl +{}; + +template +struct geometry_type + : geometry_type_impl +{}; + }} // namespace detail::wkb #endif // DOXYGEN_NO_IMPL diff --git a/include/boost/geometry/extensions/gis/io/wkb/detail/parser.hpp b/include/boost/geometry/extensions/gis/io/wkb/detail/parser.hpp index 8c649d659..0e6758e03 100644 --- a/include/boost/geometry/extensions/gis/io/wkb/detail/parser.hpp +++ b/include/boost/geometry/extensions/gis/io/wkb/detail/parser.hpp @@ -15,6 +15,8 @@ #include #include +#include + #include #include #include @@ -33,6 +35,23 @@ namespace boost { namespace geometry { +/*! +\brief Read WKB Exception +\ingroup core +\details The read_wkb_exception is thrown when there is an error in wkb parsing + */ +class read_wkb_exception : public geometry::exception +{ +public: + + inline read_wkb_exception() {} + + virtual char const* what() const throw() + { + return "Boost.Geometry Read WKB exception"; + } +}; + #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace wkb { @@ -191,9 +210,12 @@ struct point_container_parser { return false; } - + typedef typename std::iterator_traits::difference_type size_type; - BOOST_GEOMETRY_ASSERT(num_points <= boost::uint32_t( (std::numeric_limits::max)() ) ); + if(num_points > (std::numeric_limits::max)() ) + { + throw boost::geometry::read_wkb_exception(); + } size_type const container_size = static_cast(num_points); size_type const point_size = dimension::value * sizeof(double); @@ -237,7 +259,11 @@ struct linestring_parser return false; } - BOOST_GEOMETRY_ASSERT(it != end); + if(it == end) + { + throw boost::geometry::read_wkb_exception(); + } + return point_container_parser::parse(it, end, linestring, order); } }; @@ -259,15 +285,15 @@ struct polygon_parser { return false; } - - typedef typename ring_type::type ring_type; + + typedef typename boost::geometry::ring_return_type::type ring_type; std::size_t rings_parsed = 0; while (rings_parsed < num_rings && it != end) { if (0 == rings_parsed) { - ring_type& ring0 = exterior_ring(polygon); + ring_type ring0 = exterior_ring(polygon); if (!point_container_parser::parse(it, end, ring0, order)) { return false; @@ -275,19 +301,16 @@ struct polygon_parser } else { - interior_rings(polygon).resize(rings_parsed); - ring_type& ringN = interior_rings(polygon).back(); + boost::geometry::range::resize(interior_rings(polygon), rings_parsed); + ring_type ringN = boost::geometry::range::back(interior_rings(polygon)); + if (!point_container_parser::parse(it, end, ringN, order)) { return false; } } - ++rings_parsed; - } - if (num_rings != rings_parsed) - { - return false; + ++rings_parsed; } return true; diff --git a/include/boost/geometry/extensions/gis/io/wkb/read_wkb.hpp b/include/boost/geometry/extensions/gis/io/wkb/read_wkb.hpp index 69662b584..73b4cd220 100644 --- a/include/boost/geometry/extensions/gis/io/wkb/read_wkb.hpp +++ b/include/boost/geometry/extensions/gis/io/wkb/read_wkb.hpp @@ -27,38 +27,38 @@ namespace dispatch template struct read_wkb {}; -template -struct read_wkb +template +struct read_wkb { template - static inline bool parse(Iterator& it, Iterator end, G& geometry, + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, detail::wkb::byte_order_type::enum_t order) { - return detail::wkb::point_parser::parse(it, end, geometry, order); + return detail::wkb::point_parser::parse(it, end, geometry, order); } }; -template -struct read_wkb +template +struct read_wkb { template - static inline bool parse(Iterator& it, Iterator end, G& geometry, + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, detail::wkb::byte_order_type::enum_t order) { geometry::clear(geometry); - return detail::wkb::linestring_parser::parse(it, end, geometry, order); + return detail::wkb::linestring_parser::parse(it, end, geometry, order); } }; -template -struct read_wkb +template +struct read_wkb { template - static inline bool parse(Iterator& it, Iterator end, G& geometry, + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, detail::wkb::byte_order_type::enum_t order) { geometry::clear(geometry); - return detail::wkb::polygon_parser::parse(it, end, geometry, order); + return detail::wkb::polygon_parser::parse(it, end, geometry, order); } }; @@ -66,8 +66,8 @@ struct read_wkb #endif // DOXYGEN_NO_DISPATCH -template -inline bool read_wkb(Iterator begin, Iterator end, G& geometry) +template +inline bool read_wkb(Iterator begin, Iterator end, Geometry& geometry) { // Stream of bytes can only be parsed using random access iterator. BOOST_STATIC_ASSERT(( @@ -82,16 +82,16 @@ inline bool read_wkb(Iterator begin, Iterator end, G& geometry) { return dispatch::read_wkb < - typename tag::type, - G + typename tag::type, + Geometry >::parse(begin, end, geometry, byte_order); } return false; } -template -inline bool read_wkb(ByteType const* bytes, std::size_t length, G& geometry) +template +inline bool read_wkb(ByteType const* bytes, std::size_t length, Geometry& geometry) { BOOST_STATIC_ASSERT((boost::is_integral::value)); BOOST_STATIC_ASSERT((sizeof(boost::uint8_t) == sizeof(ByteType))); diff --git a/include/boost/geometry/extensions/multi/gis/io/wkb/detail/parser.hpp b/include/boost/geometry/extensions/multi/gis/io/wkb/detail/parser.hpp new file mode 100644 index 000000000..30009af4c --- /dev/null +++ b/include/boost/geometry/extensions/multi/gis/io/wkb/detail/parser.hpp @@ -0,0 +1,248 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// 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_MULTI_IO_WKB_DETAIL_PARSER_HPP +#define BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_PARSER_HPP + +#include +#include +#include + +#include + +#include +#include +#include + + +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace wkb +{ + +template +struct multipoint_parser +{ + template + static bool parse(Iterator& it, Iterator end, MultiPoint& multipoint, byte_order_type::enum_t order) + { + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + boost::uint32_t num_points(0); + if (!value_parser::parse(it, end, num_points, order)) + { + return false; + } + + // Check that the number of double values in the stream is equal + // or greater than the number of expected values in the multipoint + + typedef typename std::iterator_traits::difference_type size_type; + + typedef typename point_type::type point_type; + + size_type const container_byte_size = dimension::value * num_points * sizeof(double) + + num_points * sizeof(boost::uint8_t) + + num_points * sizeof(boost::uint32_t); + + size_type const stream_byte_size = std::distance(it,end); + + if(stream_byte_size < container_byte_size) + { + throw boost::geometry::read_wkb_exception(); + } + + point_type point_buffer; + std::back_insert_iterator output(std::back_inserter(multipoint)); + + typedef typename std::iterator_traits::difference_type size_type; + if(num_points > (std::numeric_limits::max)() ) + { + throw boost::geometry::read_wkb_exception(); + } + + size_type points_parsed = 0; + while (points_parsed < num_points && it != end) + { + detail::wkb::byte_order_type::enum_t point_byte_order; + if (!detail::wkb::byte_order_parser::parse(it, end, point_byte_order)) + { + return false; + } + + if (!geometry_type_parser::parse(it, end, point_byte_order)) + { + return false; + } + + parsing_assigner::value>::run(it, end, point_buffer, point_byte_order); + + output = point_buffer; + + ++output; + ++points_parsed; + } + return true; + } +}; + +template +struct multilinestring_parser +{ + template + static bool parse(Iterator& it, Iterator end, MultiLinestring& multilinestring, byte_order_type::enum_t order) + { + typedef typename MultiLinestring::value_type linestring_type; + typedef typename point_type::type point_type; + + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + boost::uint32_t num_linestrings(0); + if (!value_parser::parse(it, end, num_linestrings, order)) + { + return false; + } + + std::back_insert_iterator output(std::back_inserter(multilinestring)); + + typedef typename std::iterator_traits::difference_type size_type; + if(num_linestrings > (std::numeric_limits::max)() ) + { + throw boost::geometry::read_wkb_exception(); + } + + size_type linestrings_parsed = 0; + while (linestrings_parsed < num_linestrings && it != end) + { + linestring_type linestring_buffer; + + detail::wkb::byte_order_type::enum_t linestring_byte_order; + if (!detail::wkb::byte_order_parser::parse(it, end, linestring_byte_order)) + { + return false; + } + + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + if(!point_container_parser::parse(it, end, linestring_buffer, linestring_byte_order)) + { + return false; + } + + output = linestring_buffer; + + ++output; + ++linestrings_parsed; + } + return true; + } +}; + +template +struct multipolygon_parser +{ + template + static bool parse(Iterator& it, Iterator end, MultiPolygon& multipolygon, byte_order_type::enum_t order) + { + typedef typename boost::range_value::type polygon_type; + + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + boost::uint32_t num_polygons(0); + if (!value_parser::parse(it, end, num_polygons, order)) + { + return false; + } + + std::back_insert_iterator output(std::back_inserter(multipolygon)); + + std::size_t polygons_parsed = 0; + while(polygons_parsed < num_polygons && it != end) + { + polygon_type polygon_buffer; + + detail::wkb::byte_order_type::enum_t polygon_byte_order; + if (!detail::wkb::byte_order_parser::parse(it, end, polygon_byte_order)) + { + return false; + } + + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + boost::uint32_t num_rings(0); + if (!value_parser::parse(it, end, num_rings, polygon_byte_order)) + { + return false; + } + + std::size_t rings_parsed = 0; + + while (rings_parsed < num_rings && it != end) + { + typedef typename boost::geometry::ring_return_type::type ring_type; + + if (0 == rings_parsed) + { + ring_type ring0 = exterior_ring(polygon_buffer); + + if (!point_container_parser::parse(it, end, ring0, polygon_byte_order)) + { + return false; + } + } + else + { + boost::geometry::range::resize(interior_rings(polygon_buffer), rings_parsed); + ring_type ringN = boost::geometry::range::back(interior_rings(polygon_buffer)); + + if (!point_container_parser::parse(it, end, ringN, polygon_byte_order)) + { + return false; + } + } + ++rings_parsed; + } + + output = polygon_buffer; + ++output; + } + + return true; + } +}; + +}} // namespace detail::wkb +#endif // DOXYGEN_NO_IMPL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_PARSER_HPP diff --git a/include/boost/geometry/extensions/multi/gis/io/wkb/detail/writer.hpp b/include/boost/geometry/extensions/multi/gis/io/wkb/detail/writer.hpp new file mode 100644 index 000000000..5b826ce1a --- /dev/null +++ b/include/boost/geometry/extensions/multi/gis/io/wkb/detail/writer.hpp @@ -0,0 +1,145 @@ +// Boost.Geometry +// +// Copyright (c) 2015 Mats Taraldsvik. +// +// 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_MULTI_IO_WKB_DETAIL_WRITER_HPP +#define BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_WRITER_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace wkb +{ + template + struct multipoint_writer + { + template + static bool write(MultiPoint const& multipoint, + OutputIterator& iter, + byte_order_type::enum_t byte_order) + { + // write endian type + value_writer::write(byte_order, iter, byte_order); + + // write geometry type + uint32_t type = geometry_type::get(); + value_writer::write(type, iter, byte_order); + + // write num points + uint32_t num_points = boost::size(multipoint); + value_writer::write(num_points, iter, byte_order); + + typedef typename point_type::type point_type; + + for(typename boost::range_iterator::type + point_iter = boost::begin(multipoint); + point_iter != boost::end(multipoint); + ++point_iter) + { + detail::wkb::point_writer::write(*point_iter, iter, byte_order); + } + + return true; + } + }; + + template + struct multilinestring_writer + { + template + static bool write(MultiLinestring const& multilinestring, + OutputIterator& iter, + byte_order_type::enum_t byte_order) + { + // write endian type + value_writer::write(byte_order, iter, byte_order); + + // write geometry type + uint32_t type = geometry_type::get(); + value_writer::write(type, iter, byte_order); + + // write num linestrings + uint32_t num_linestrings = boost::size(multilinestring); + value_writer::write(num_linestrings, iter, byte_order); + + typedef typename boost::range_value::type linestring_type; + + for(typename boost::range_iterator::type + linestring_iter = boost::begin(multilinestring); + linestring_iter != boost::end(multilinestring); + ++linestring_iter) + { + detail::wkb::linestring_writer::write(*linestring_iter, iter, byte_order); + } + + return true; + } + }; + + template + struct multipolygon_writer + { + template + static bool write(MultiPolygon const& multipolygon, + OutputIterator& iter, + byte_order_type::enum_t byte_order) + { + // write endian type + value_writer::write(byte_order, iter, byte_order); + + // write geometry type + uint32_t type = geometry_type::get(); + value_writer::write(type, iter, byte_order); + + // write num polygons + uint32_t num_polygons = boost::size(multipolygon); + value_writer::write(num_polygons, iter, byte_order); + + typedef typename boost::range_value::type polygon_type; + + for(typename boost::range_iterator::type + polygon_iter = boost::begin(multipolygon); + polygon_iter != boost::end(multipolygon); + ++polygon_iter) + { + detail::wkb::polygon_writer::write(*polygon_iter, iter, byte_order); + } + + return true; + } + }; + +}} // namespace detail::wkb +#endif // DOXYGEN_NO_IMPL + +}} // namespace boost::geometry +#endif // BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_WRITER_HPP diff --git a/include/boost/geometry/extensions/multi/gis/io/wkb/read_wkb.hpp b/include/boost/geometry/extensions/multi/gis/io/wkb/read_wkb.hpp new file mode 100644 index 000000000..b8e3e65bd --- /dev/null +++ b/include/boost/geometry/extensions/multi/gis/io/wkb/read_wkb.hpp @@ -0,0 +1,62 @@ +// Boost.Geometry + +// Copyright (c) 2015 Mats Taraldsvik + +// 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_MULTI_IO_WKB_READ_WKB_HPP +#define BOOST_GEOMETRY_MULTI_IO_WKB_READ_WKB_HPP + +#include + +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct read_wkb +{ + template + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, + detail::wkb::byte_order_type::enum_t order) + { + return detail::wkb::multipoint_parser::parse(it, end, geometry, order); + } +}; + +template +struct read_wkb +{ + template + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, + detail::wkb::byte_order_type::enum_t order) + { + return detail::wkb::multilinestring_parser::parse(it, end, geometry, order); + } +}; + +template +struct read_wkb +{ + template + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, + detail::wkb::byte_order_type::enum_t order) + { + return detail::wkb::multipolygon_parser::parse(it, end, geometry, order); + } +}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_MULTI_IO_WKB_READ_WKB_HPP diff --git a/include/boost/geometry/extensions/multi/gis/io/wkb/write_wkb.hpp b/include/boost/geometry/extensions/multi/gis/io/wkb/write_wkb.hpp new file mode 100644 index 000000000..1deed3ed3 --- /dev/null +++ b/include/boost/geometry/extensions/multi/gis/io/wkb/write_wkb.hpp @@ -0,0 +1,66 @@ +// Boost.Geometry +// +// Copyright (c) 2015 Mats Taraldsvik. +// +// 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_MULTI_IO_WKB_WRITE_WKB_HPP +#define BOOST_GEOMETRY_MULTI_IO_WKB_WRITE_WKB_HPP + +#include + +#include +#include + +#include + +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct write_wkb +{ + template + static inline bool write(const Geometry& geometry, OutputIterator iter, + detail::wkb::byte_order_type::enum_t byte_order) + { + return detail::wkb::multipoint_writer::write(geometry, iter, byte_order); + } +}; + +template +struct write_wkb +{ + template + static inline bool write(const Geometry& geometry, OutputIterator iter, + detail::wkb::byte_order_type::enum_t byte_order) + { + return detail::wkb::multilinestring_writer::write(geometry, iter, byte_order); + } +}; + +template +struct write_wkb +{ + template + static inline bool write(const Geometry& geometry, OutputIterator iter, + detail::wkb::byte_order_type::enum_t byte_order) + { + return detail::wkb::multipolygon_writer::write(geometry, iter, byte_order); + } +}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +}} // namespace boost::geometry +#endif // BOOST_GEOMETRY_MULTI_IO_WKB_WRITE_WKB_HPP diff --git a/include/boost/geometry/formulas/andoyer_inverse.hpp b/include/boost/geometry/formulas/andoyer_inverse.hpp index 04a43995b..902fd7d8f 100644 --- a/include/boost/geometry/formulas/andoyer_inverse.hpp +++ b/include/boost/geometry/formulas/andoyer_inverse.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2015-2016 Oracle and/or its affiliates. +// Copyright (c) 2015-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -20,9 +20,8 @@ #include #include -#include - #include +#include #include @@ -75,7 +74,7 @@ public: CT const c0 = CT(0); CT const c1 = CT(1); CT const pi = math::pi(); - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const dlon = lon2 - lon1; CT const sin_dlon = sin(dlon); @@ -138,7 +137,14 @@ public: CT A = c0; CT U = c0; - if ( ! math::equals(cos_lat2, c0) ) + if (math::equals(cos_lat2, c0)) + { + if (sin_lat2 < c0) + { + A = pi; + } + } + else { CT const tan_lat2 = sin_lat2/cos_lat2; CT const M = cos_lat1*tan_lat2-sin_lat1*cos_dlon; @@ -149,7 +155,14 @@ public: CT B = c0; CT V = c0; - if ( ! math::equals(cos_lat1, c0) ) + if (math::equals(cos_lat1, c0)) + { + if (sin_lat1 < c0) + { + B = pi; + } + } + else { CT const tan_lat1 = sin_lat1/cos_lat1; CT const N = cos_lat2*tan_lat1-sin_lat2*cos_dlon; diff --git a/include/boost/geometry/formulas/area_formulas.hpp b/include/boost/geometry/formulas/area_formulas.hpp index a9c503b65..6a0b525e2 100644 --- a/include/boost/geometry/formulas/area_formulas.hpp +++ b/include/boost/geometry/formulas/area_formulas.hpp @@ -11,7 +11,7 @@ #ifndef BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP #define BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP -#include +#include #include namespace boost { namespace geometry { namespace formula @@ -429,7 +429,7 @@ public: // Constants CT const ep = spheroid_const.m_ep; - CT const f = geometry::detail::flattening(spheroid_const.m_spheroid); + CT const f = formula::flattening(spheroid_const.m_spheroid); CT const one_minus_f = CT(1) - f; std::size_t const series_order_plus_one = SeriesOrder + 1; std::size_t const series_order_plus_two = SeriesOrder + 2; diff --git a/include/boost/geometry/formulas/differential_quantities.hpp b/include/boost/geometry/formulas/differential_quantities.hpp index 9a92f14e1..ff2ec539d 100644 --- a/include/boost/geometry/formulas/differential_quantities.hpp +++ b/include/boost/geometry/formulas/differential_quantities.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 @@ -64,8 +64,8 @@ public: CT const c1 = 1; CT const one_minus_f = c1 - f; - CT const sin_bet1 = one_minus_f * sin_lat1; - CT const sin_bet2 = one_minus_f * sin_lat2; + CT sin_bet1 = one_minus_f * sin_lat1; + CT sin_bet2 = one_minus_f * sin_lat2; // equator if (math::equals(sin_bet1, c0) && math::equals(sin_bet2, c0)) @@ -89,14 +89,17 @@ public: CT const e2 = f * (c2 - f); CT const ep2 = e2 / math::sqr(one_minus_f); - CT const cos_bet1 = cos_lat1; - CT const cos_bet2 = cos_lat2; - CT const sin_alp1 = sin(azimuth); CT const cos_alp1 = cos(azimuth); //CT const sin_alp2 = sin(reverse_azimuth); CT const cos_alp2 = cos(reverse_azimuth); + CT cos_bet1 = cos_lat1; + CT cos_bet2 = cos_lat2; + + normalize(sin_bet1, cos_bet1); + normalize(sin_bet2, cos_bet2); + CT sin_sig1 = sin_bet1; CT cos_sig1 = cos_alp1 * cos_bet1; CT sin_sig2 = sin_bet2; @@ -112,8 +115,8 @@ public: J12_f(sin_sig1, cos_sig1, sin_sig2, cos_sig2, cos_alp0_sqr, f) : J12_ep_sqr(sin_sig1, cos_sig1, sin_sig2, cos_sig2, cos_alp0_sqr, ep2) ; - CT const dn1 = math::sqrt(c1 + e2 * math::sqr(sin_lat1)); - CT const dn2 = math::sqrt(c1 + e2 * math::sqr(sin_lat2)); + CT const dn1 = math::sqrt(c1 + ep2 * math::sqr(sin_bet1)); + CT const dn2 = math::sqrt(c1 + ep2 * math::sqr(sin_bet2)); if (BOOST_GEOMETRY_CONDITION(EnableReducedLength)) { diff --git a/include/boost/geometry/formulas/eccentricity_sqr.hpp b/include/boost/geometry/formulas/eccentricity_sqr.hpp new file mode 100644 index 000000000..01a9beacb --- /dev/null +++ b/include/boost/geometry/formulas/eccentricity_sqr.hpp @@ -0,0 +1,70 @@ +// Boost.Geometry + +// Copyright (c) 2016 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_ECCENCRICITY_SQR_HPP +#define BOOST_GEOMETRY_FORMULAS_ECCENCRICITY_SQR_HPP + +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace formula_dispatch +{ + +template ::type> +struct eccentricity_sqr + : not_implemented +{}; + +template +struct eccentricity_sqr +{ + static inline ResultType apply(Geometry const& /*geometry*/) + { + return ResultType(0); + } +}; + +template +struct eccentricity_sqr +{ + static inline ResultType apply(Geometry const& geometry) + { + // 1 - (b / a)^2 + return ResultType(1) - math::sqr(ResultType(get_radius<2>(geometry)) + / ResultType(get_radius<0>(geometry))); + } +}; + +} // namespace formula_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template +ResultType eccentricity_sqr(Geometry const& geometry) +{ + return formula_dispatch::eccentricity_sqr::apply(geometry); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_ECCENCRICITY_SQR_HPP diff --git a/include/boost/geometry/algorithms/detail/flattening.hpp b/include/boost/geometry/formulas/flattening.hpp similarity index 78% rename from include/boost/geometry/algorithms/detail/flattening.hpp rename to include/boost/geometry/formulas/flattening.hpp index 8ed5fd9a8..f94ead65b 100644 --- a/include/boost/geometry/algorithms/detail/flattening.hpp +++ b/include/boost/geometry/formulas/flattening.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2014 Oracle and/or its affiliates. +// Copyright (c) 2014-2016 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -8,8 +8,8 @@ // 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_FLATTENING_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_FLATTENING_HPP +#ifndef BOOST_GEOMETRY_FORMULAS_FLATTENING_HPP +#define BOOST_GEOMETRY_FORMULAS_FLATTENING_HPP #include #include @@ -21,7 +21,7 @@ namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DISPATCH -namespace detail_dispatch +namespace formula_dispatch { template ::type> @@ -43,27 +43,28 @@ struct flattening { static inline ResultType apply(Geometry const& geometry) { + // (a - b) / a return ResultType(get_radius<0>(geometry) - get_radius<2>(geometry)) / ResultType(get_radius<0>(geometry)); } }; -} // namespace detail_dispatch +} // namespace formula_dispatch #endif // DOXYGEN_NO_DISPATCH #ifndef DOXYGEN_NO_DETAIL -namespace detail +namespace formula { template ResultType flattening(Geometry const& geometry) { - return detail_dispatch::flattening::apply(geometry); + return formula_dispatch::flattening::apply(geometry); } -} // namespace detail +} // namespace formula #endif // DOXYGEN_NO_DETAIL }} // namespace boost::geometry -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_FLATTENING_HPP +#endif // BOOST_GEOMETRY_FORMULAS_FLATTENING_HPP diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp new file mode 100644 index 000000000..f6feb6663 --- /dev/null +++ b/include/boost/geometry/formulas/geographic.hpp @@ -0,0 +1,457 @@ +// Boost.Geometry + +// Copyright (c) 2016, 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_GEOGRAPHIC_HPP +#define BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace boost { namespace geometry { + +namespace formula { + +template +inline Point3d geo_to_cart3d(PointGeo const& point_geo, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type calc_t; + + calc_t const c1 = 1; + calc_t const e_sqr = eccentricity_sqr(spheroid); + + calc_t const lon = get_as_radian<0>(point_geo); + calc_t const lat = get_as_radian<1>(point_geo); + + Point3d res; + + calc_t const sin_lat = sin(lat); + + // "unit" spheroid, a = 1 + calc_t const N = c1 / math::sqrt(c1 - e_sqr * math::sqr(sin_lat)); + calc_t const N_cos_lat = N * cos(lat); + + set<0>(res, N_cos_lat * cos(lon)); + set<1>(res, N_cos_lat * sin(lon)); + set<2>(res, N * (c1 - e_sqr) * sin_lat); + + return res; +} + +template +inline void geo_to_cart3d(PointGeo const& point_geo, Point3d & result, Point3d & north, Point3d & east, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type calc_t; + + calc_t const c1 = 1; + calc_t const e_sqr = eccentricity_sqr(spheroid); + + calc_t const lon = get_as_radian<0>(point_geo); + calc_t const lat = get_as_radian<1>(point_geo); + + calc_t const sin_lon = sin(lon); + calc_t const cos_lon = cos(lon); + calc_t const sin_lat = sin(lat); + calc_t const cos_lat = cos(lat); + + // "unit" spheroid, a = 1 + calc_t const N = c1 / math::sqrt(c1 - e_sqr * math::sqr(sin_lat)); + calc_t const N_cos_lat = N * cos_lat; + + set<0>(result, N_cos_lat * cos_lon); + set<1>(result, N_cos_lat * sin_lon); + set<2>(result, N * (c1 - e_sqr) * sin_lat); + + set<0>(east, -sin_lon); + set<1>(east, cos_lon); + set<2>(east, 0); + + set<0>(north, -sin_lat * cos_lon); + set<1>(north, -sin_lat * sin_lon); + set<2>(north, cos_lat); +} + +template +inline PointGeo cart3d_to_geo(Point3d const& point_3d, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + typedef typename coordinate_type::type calc_t; + + calc_t const c1 = 1; + //calc_t const c2 = 2; + calc_t const e_sqr = eccentricity_sqr(spheroid); + + calc_t const x = get<0>(point_3d); + calc_t const y = get<1>(point_3d); + calc_t const z = get<2>(point_3d); + calc_t const xy_l = math::sqrt(math::sqr(x) + math::sqr(y)); + + calc_t const lonr = atan2(y, x); + + // NOTE: Alternative version + // http://www.iag-aig.org/attach/989c8e501d9c5b5e2736955baf2632f5/V60N2_5FT.pdf + // calc_t const lonr = c2 * atan2(y, x + xy_l); + + calc_t const latr = atan2(z, (c1 - e_sqr) * xy_l); + + // NOTE: If h is equal to 0 then there is no need to improve value of latitude + // because then N_i / (N_i + h_i) = 1 + // http://www.navipedia.net/index.php/Ellipsoidal_and_Cartesian_Coordinates_Conversion + + PointGeo res; + + set_from_radian<0>(res, lonr); + set_from_radian<1>(res, latr); + + coord_t lon = get<0>(res); + coord_t lat = get<1>(res); + + math::normalize_spheroidal_coordinates + < + typename coordinate_system::type::units, + coord_t + >(lon, lat); + + set<0>(res, lon); + set<1>(res, lat); + + return res; +} + +template +inline Point3d projected_to_xy(Point3d const& point_3d, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + // len_xy = sqrt(x^2 + y^2) + // r = len_xy - |z / tan(lat)| + // assuming h = 0 + // lat = atan2(z, (1 - e^2) * len_xy); + // |z / tan(lat)| = (1 - e^2) * len_xy + // r = e^2 * len_xy + // x_res = r * cos(lon) = e^2 * len_xy * x / len_xy = e^2 * x + // y_res = r * sin(lon) = e^2 * len_xy * y / len_xy = e^2 * y + + coord_t const c0 = 0; + coord_t const e_sqr = formula::eccentricity_sqr(spheroid); + + Point3d res; + + set<0>(res, e_sqr * get<0>(point_3d)); + set<1>(res, e_sqr * get<1>(point_3d)); + set<2>(res, c0); + + return res; +} + +template +inline Point3d projected_to_surface(Point3d const& direction, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + //coord_t const c0 = 0; + coord_t const c2 = 2; + coord_t const c4 = 4; + + // calculate the point of intersection of a ray and spheroid's surface + // the origin is the origin of the coordinate system + //(x*x+y*y)/(a*a) + z*z/(b*b) = 1 + // x = d.x * t + // y = d.y * t + // z = d.z * t + coord_t const dx = get<0>(direction); + coord_t const dy = get<1>(direction); + coord_t const dz = get<2>(direction); + + //coord_t const a_sqr = math::sqr(get_radius<0>(spheroid)); + //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 param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; + coord_t const delta = c4 * param_a; + // delta >= 0 + coord_t const t = math::sqrt(delta) / (c2 * param_a); + + // result = direction * t + Point3d result = direction; + multiply_value(result, t); + + return result; +} + +template +inline bool projected_to_surface(Point3d const& origin, Point3d const& direction, Point3d & result1, Point3d & result2, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + coord_t const c0 = 0; + coord_t const c1 = 1; + coord_t const c2 = 2; + coord_t const c4 = 4; + + // calculate the point of intersection of a ray and spheroid's surface + //(x*x+y*y)/(a*a) + z*z/(b*b) = 1 + // x = o.x + d.x * t + // y = o.y + d.y * t + // z = o.z + d.z * t + coord_t const ox = get<0>(origin); + coord_t const oy = get<1>(origin); + coord_t const oz = get<2>(origin); + coord_t const dx = get<0>(direction); + coord_t const dy = get<1>(direction); + coord_t const dz = get<2>(direction); + + //coord_t const a_sqr = math::sqr(get_radius<0>(spheroid)); + //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 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); + coord_t const param_c = (ox*ox + oy*oy) / a_sqr + oz*oz / b_sqr - c1; + + coord_t const delta = math::sqr(param_b) - c4 * param_a*param_c; + + // equals() ? + if (delta < c0 || param_a == 0) + { + return false; + } + + // result = origin + direction * t + + coord_t const sqrt_delta = math::sqrt(delta); + coord_t const two_a = c2 * param_a; + + coord_t const t1 = (-param_b + sqrt_delta) / two_a; + result1 = direction; + multiply_value(result1, t1); + add_point(result1, origin); + + coord_t const t2 = (-param_b - sqrt_delta) / two_a; + result2 = direction; + multiply_value(result2, t2); + add_point(result2, origin); + + return true; +} + +template +inline bool great_elliptic_intersection(Point3d const& a1, Point3d const& a2, + Point3d const& b1, Point3d const& b2, + Point3d & result, + Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + coord_t c0 = 0; + coord_t c1 = 1; + + Point3d n1 = cross_product(a1, a2); + Point3d n2 = cross_product(b1, b2); + + // intersection direction + Point3d id = cross_product(n1, n2); + coord_t id_len_sqr = dot_product(id, id); + + if (math::equals(id_len_sqr, c0)) + { + return false; + } + + // no need to normalize a1 and a2 because the intersection point on + // the opposite side of the globe is at the same distance from the origin + coord_t cos_a1i = dot_product(a1, id); + coord_t cos_a2i = dot_product(a2, id); + coord_t gri = math::detail::greatest(cos_a1i, cos_a2i); + Point3d neg_id = id; + multiply_value(neg_id, -c1); + coord_t cos_a1ni = dot_product(a1, neg_id); + coord_t cos_a2ni = dot_product(a2, neg_id); + coord_t grni = math::detail::greatest(cos_a1ni, cos_a2ni); + + if (gri >= grni) + { + result = projected_to_surface(id, spheroid); + } + else + { + result = projected_to_surface(neg_id, spheroid); + } + + return true; +} + +template +static inline int elliptic_side_value(Point3d1 const& origin, Point3d1 const& norm, Point3d2 const& pt) +{ + typedef typename coordinate_type::type calc_t; + calc_t c0 = 0; + + // vector oposite to pt - origin + // only for the purpose of assigning origin + Point3d1 vec = origin; + subtract_point(vec, pt); + + calc_t d = dot_product(norm, vec); + + // since the vector is opposite the signs are opposite + return math::equals(d, c0) ? 0 + : d < c0 ? 1 + : -1; // d > 0 +} + +template +inline bool planes_spheroid_intersection(Point3d const& o1, Point3d const& n1, + Point3d const& o2, Point3d const& n2, + Point3d & ip1, Point3d & ip2, + Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + coord_t c0 = 0; + coord_t c1 = 1; + + // Below + // n . (p - o) = 0 + // n . p - n . o = 0 + // n . p + d = 0 + // n . p = h + + // intersection direction + Point3d id = cross_product(n1, n2); + + if (math::equals(dot_product(id, id), c0)) + { + return false; + } + + coord_t dot_n1_n2 = dot_product(n1, n2); + coord_t dot_n1_n2_sqr = math::sqr(dot_n1_n2); + + coord_t h1 = dot_product(n1, o1); + coord_t h2 = dot_product(n2, o2); + + coord_t denom = c1 - dot_n1_n2_sqr; + coord_t C1 = (h1 - h2 * dot_n1_n2) / denom; + coord_t C2 = (h2 - h1 * dot_n1_n2) / denom; + + // C1 * n1 + C2 * n2 + Point3d C1_n1 = n1; + multiply_value(C1_n1, C1); + Point3d C2_n2 = n2; + multiply_value(C2_n2, C2); + Point3d io = C1_n1; + add_point(io, C2_n2); + + if (! projected_to_surface(io, id, ip1, ip2, spheroid)) + { + return false; + } + + return true; +} + + +template +inline void experimental_elliptic_plane(Point3d const& p1, Point3d const& p2, + Point3d & v1, Point3d & v2, + Point3d & origin, Point3d & normal, + Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + Point3d xy1 = projected_to_xy(p1, spheroid); + Point3d xy2 = projected_to_xy(p2, spheroid); + + // origin = (xy1 + xy2) / 2 + origin = xy1; + add_point(origin, xy2); + multiply_value(origin, coord_t(0.5)); + + // v1 = p1 - origin + v1 = p1; + subtract_point(v1, origin); + // v2 = p2 - origin + v2 = p2; + subtract_point(v2, origin); + + normal = cross_product(v1, v2); +} + +template +inline void experimental_elliptic_plane(Point3d const& p1, Point3d const& p2, + Point3d & origin, Point3d & normal, + Spheroid const& spheroid) +{ + Point3d v1, v2; + experimental_elliptic_plane(p1, p2, v1, v2, origin, normal, spheroid); +} + +template +inline bool experimental_elliptic_intersection(Point3d const& a1, Point3d const& a2, + Point3d const& b1, Point3d const& b2, + Point3d & result, + Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + coord_t c0 = 0; + coord_t c1 = 1; + + Point3d a1v, a2v, o1, n1; + experimental_elliptic_plane(a1, a2, a1v, a2v, o1, n1, spheroid); + Point3d b1v, b2v, o2, n2; + experimental_elliptic_plane(b1, b2, b1v, b2v, o2, n2, spheroid); + + if (! detail::vec_normalize(n1) || ! detail::vec_normalize(n2)) + { + return false; + } + + Point3d ip1_s, ip2_s; + if (! planes_spheroid_intersection(o1, n1, o2, n2, ip1_s, ip2_s, spheroid)) + { + return false; + } + + // NOTE: simplified test, may not work in all cases + coord_t dot_a1i1 = dot_product(a1, ip1_s); + coord_t dot_a2i1 = dot_product(a2, ip1_s); + coord_t gri1 = math::detail::greatest(dot_a1i1, dot_a2i1); + coord_t dot_a1i2 = dot_product(a1, ip2_s); + coord_t dot_a2i2 = dot_product(a2, ip2_s); + coord_t gri2 = math::detail::greatest(dot_a1i2, dot_a2i2); + + result = gri1 >= gri2 ? ip1_s : ip2_s; + + return true; +} + +} // namespace formula + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP diff --git a/include/boost/geometry/formulas/gnomonic_spheroid.hpp b/include/boost/geometry/formulas/gnomonic_spheroid.hpp index 3457397b0..4710c6c06 100644 --- a/include/boost/geometry/formulas/gnomonic_spheroid.hpp +++ b/include/boost/geometry/formulas/gnomonic_spheroid.hpp @@ -14,12 +14,11 @@ #include -#include - #include #include #include +#include #include #include #include diff --git a/include/boost/geometry/formulas/sjoberg_intersection.hpp b/include/boost/geometry/formulas/sjoberg_intersection.hpp index 03bd4bc97..cf9e8c97b 100644 --- a/include/boost/geometry/formulas/sjoberg_intersection.hpp +++ b/include/boost/geometry/formulas/sjoberg_intersection.hpp @@ -20,19 +20,606 @@ #include #include -#include +#include +#include namespace boost { namespace geometry { namespace formula { +/*! +\brief The intersection of two great circles as proposed by Sjoberg. +\see See + - [Sjoberg02] Lars E. Sjoberg, Intersections on the sphere and ellipsoid, 2002 + http://link.springer.com/article/10.1007/s00190-001-0230-9 +*/ +template +struct sjoberg_intersection_spherical_02 +{ + // TODO: if it will be used as standalone formula + // support segments on equator and endpoints on poles + + static inline bool apply(CT const& lon1, CT const& lat1, CT const& lon_a2, CT const& lat_a2, + CT const& lon2, CT const& lat2, CT const& lon_b2, CT const& lat_b2, + CT & lon, CT & lat) + { + CT tan_lat = 0; + bool res = apply_alt(lon1, lat1, lon_a2, lat_a2, + lon2, lat2, lon_b2, lat_b2, + lon, tan_lat); + + if (res) + { + lat = atan(tan_lat); + } + + return res; + } + + static inline bool apply_alt(CT const& lon1, CT const& lat1, CT const& lon_a2, CT const& lat_a2, + CT const& lon2, CT const& lat2, CT const& lon_b2, CT const& lat_b2, + CT & lon, CT & tan_lat) + { + CT const cos_lon1 = cos(lon1); + CT const sin_lon1 = sin(lon1); + CT const cos_lon2 = cos(lon2); + CT const sin_lon2 = sin(lon2); + CT const sin_lat1 = sin(lat1); + CT const sin_lat2 = sin(lat2); + CT const cos_lat1 = cos(lat1); + CT const cos_lat2 = cos(lat2); + + CT const tan_lat_a2 = tan(lat_a2); + CT const tan_lat_b2 = tan(lat_b2); + + return apply(lon1, lon_a2, lon2, lon_b2, + sin_lon1, cos_lon1, sin_lat1, cos_lat1, + sin_lon2, cos_lon2, sin_lat2, cos_lat2, + tan_lat_a2, tan_lat_b2, + lon, tan_lat); + } + +private: + static inline bool apply(CT const& lon1, CT const& lon_a2, CT const& lon2, CT const& lon_b2, + CT const& sin_lon1, CT const& cos_lon1, CT const& sin_lat1, CT const& cos_lat1, + CT const& sin_lon2, CT const& cos_lon2, CT const& sin_lat2, CT const& cos_lat2, + CT const& tan_lat_a2, CT const& tan_lat_b2, + CT & lon, CT & tan_lat) + { + // NOTE: + // cos_lat_ = 0 <=> segment on equator + // tan_alpha_ = 0 <=> segment vertical + + CT const tan_lat1 = sin_lat1 / cos_lat1; //tan(lat1); + CT const tan_lat2 = sin_lat2 / cos_lat2; //tan(lat2); + + CT const dlon1 = lon_a2 - lon1; + CT const sin_dlon1 = sin(dlon1); + CT const dlon2 = lon_b2 - lon2; + CT const sin_dlon2 = sin(dlon2); + + CT const c0 = 0; + bool const is_vertical1 = math::equals(sin_dlon1, c0); + bool const is_vertical2 = math::equals(sin_dlon2, c0); + + CT tan_alpha1 = 0; + CT tan_alpha2 = 0; + + if (is_vertical1 && is_vertical2) + { + // circles intersect at one of the poles or are collinear + return false; + } + else if (is_vertical1) + { + CT const cos_dlon2 = cos(dlon2); + CT const tan_alpha2_x = cos_lat2 * tan_lat_b2 - sin_lat2 * cos_dlon2; + tan_alpha2 = sin_dlon2 / tan_alpha2_x; + + lon = lon1; + } + else if (is_vertical2) + { + CT const cos_dlon1 = cos(dlon1); + CT const tan_alpha1_x = cos_lat1 * tan_lat_a2 - sin_lat1 * cos_dlon1; + tan_alpha1 = sin_dlon1 / tan_alpha1_x; + + lon = lon2; + } + else + { + CT const cos_dlon1 = cos(dlon1); + CT const cos_dlon2 = cos(dlon2); + + CT const tan_alpha1_x = cos_lat1 * tan_lat_a2 - sin_lat1 * cos_dlon1; + CT const tan_alpha2_x = cos_lat2 * tan_lat_b2 - sin_lat2 * cos_dlon2; + tan_alpha1 = sin_dlon1 / tan_alpha1_x; + tan_alpha2 = sin_dlon2 / tan_alpha2_x; + + CT const T1 = tan_alpha1 * cos_lat1; + CT const T2 = tan_alpha2 * cos_lat2; + CT const T1T2 = T1*T2; + CT const tan_lon_y = T1 * sin_lon2 - T2 * sin_lon1 + T1T2 * (tan_lat1 * cos_lon1 - tan_lat2 * cos_lon2); + CT const tan_lon_x = T1 * cos_lon2 - T2 * cos_lon1 - T1T2 * (tan_lat1 * sin_lon1 - tan_lat2 * sin_lon2); + + lon = atan2(tan_lon_y, tan_lon_x); + } + + // choose closer result + CT const pi = math::pi(); + CT const lon_2 = lon > c0 ? lon - pi : lon + pi; + CT const lon_dist1 = (std::max)((std::min)(math::longitude_difference(lon1, lon), + math::longitude_difference(lon_a2, lon)), + (std::min)(math::longitude_difference(lon2, lon), + math::longitude_difference(lon_b2, lon))); + CT const lon_dist2 = (std::max)((std::min)(math::longitude_difference(lon1, lon_2), + math::longitude_difference(lon_a2, lon_2)), + (std::min)(math::longitude_difference(lon2, lon_2), + math::longitude_difference(lon_b2, lon_2))); + if (lon_dist2 < lon_dist1) + { + lon = lon_2; + } + + CT const sin_lon = sin(lon); + CT const cos_lon = cos(lon); + + if (math::abs(tan_alpha1) >= math::abs(tan_alpha2)) // pick less vertical segment + { + CT const sin_dlon_1 = sin_lon * cos_lon1 - cos_lon * sin_lon1; + CT const cos_dlon_1 = cos_lon * cos_lon1 + sin_lon * sin_lon1; + CT const lat_y_1 = sin_dlon_1 + tan_alpha1 * sin_lat1 * cos_dlon_1; + CT const lat_x_1 = tan_alpha1 * cos_lat1; + tan_lat = lat_y_1 / lat_x_1; + } + else + { + CT const sin_dlon_2 = sin_lon * cos_lon2 - cos_lon * sin_lon2; + CT const cos_dlon_2 = cos_lon * cos_lon2 + sin_lon * sin_lon2; + CT const lat_y_2 = sin_dlon_2 + tan_alpha2 * sin_lat2 * cos_dlon_2; + CT const lat_x_2 = tan_alpha2 * cos_lat2; + tan_lat = lat_y_2 / lat_x_2; + } + + return true; + } +}; + + +/*! Approximation of dLambda_j [Sjoberg07], expanded into taylor series in e^2 + Maxima script: + dLI_j(c_j, sinB_j, sinB) := integrate(1 / (sqrt(1 - c_j ^ 2 - x ^ 2)*(1 + sqrt(1 - e2*(1 - x ^ 2)))), x, sinB_j, sinB); + dL_j(c_j, B_j, B) := -e2 * c_j * dLI_j(c_j, B_j, B); + S: taylor(dLI_j(c_j, sinB_j, sinB), e2, 0, 3); + assume(c_j < 1); + assume(c_j > 0); + L1: factor(integrate(sqrt(-x ^ 2 - c_j ^ 2 + 1) / (x ^ 2 + c_j ^ 2 - 1), x)); + L2: factor(integrate(((x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); + L3: factor(integrate(((x ^ 4 - 2 * x ^ 2 + 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); + L4: factor(integrate(((x ^ 6 - 3 * x ^ 4 + 3 * x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); + +\see See + - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007 + http://link.springer.com/article/10.1007/s00190-007-0204-7 +*/ +template +inline CT sjoberg_d_lambda_e_sqr(CT const& sin_betaj, CT const& sin_beta, + CT const& Cj, CT const& sqrt_1_Cj_sqr, + CT const& e_sqr) +{ + using math::detail::bounded; + + if (Order == 0) + { + return 0; + } + + CT const c1 = 1; + CT const c2 = 2; + + CT const asin_B = asin(bounded(sin_beta / sqrt_1_Cj_sqr, -c1, c1)); + CT const asin_Bj = asin(sin_betaj / sqrt_1_Cj_sqr); + CT const L0 = (asin_B - asin_Bj) / c2; + + if (Order == 1) + { + return -Cj * e_sqr * L0; + } + + CT const c0 = 0; + CT const c16 = 16; + + CT const X = sin_beta; + CT const Xj = sin_betaj; + CT const X_sqr = math::sqr(X); + CT const Xj_sqr = math::sqr(Xj); + CT const Cj_sqr = math::sqr(Cj); + CT const Cj_sqr_plus_one = Cj_sqr + c1; + CT const one_minus_Cj_sqr = c1 - Cj_sqr; + CT const sqrt_Y = math::sqrt(bounded(-X_sqr + one_minus_Cj_sqr, c0)); + CT const sqrt_Yj = math::sqrt(-Xj_sqr + one_minus_Cj_sqr); + CT const L1 = (Cj_sqr_plus_one * (asin_B - asin_Bj) + X * sqrt_Y - Xj * sqrt_Yj) / c16; + + if (Order == 2) + { + return -Cj * e_sqr * (L0 + e_sqr * L1); + } + + CT const c3 = 3; + CT const c5 = 5; + CT const c128 = 128; + + CT const E = Cj_sqr * (c3 * Cj_sqr + c2) + c3; + CT const F = X * (-c2 * X_sqr + c3 * Cj_sqr + c5); + CT const Fj = Xj * (-c2 * Xj_sqr + c3 * Cj_sqr + c5); + CT const L2 = (E * (asin_B - asin_Bj) + F * sqrt_Y - Fj * sqrt_Yj) / c128; + + if (Order == 3) + { + return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * L2)); + } + + CT const c8 = 8; + CT const c9 = 9; + CT const c10 = 10; + CT const c15 = 15; + CT const c24 = 24; + CT const c26 = 26; + CT const c33 = 33; + CT const c6144 = 6144; + + CT const G = Cj_sqr * (Cj_sqr * (Cj_sqr * c15 + c9) + c9) + c15; + CT const H = -c10 * Cj_sqr - c26; + CT const I = Cj_sqr * (Cj_sqr * c15 + c24) + c33; + CT const J = X_sqr * (X * (c8 * X_sqr + H)) + X * I; + CT const Jj = Xj_sqr * (Xj * (c8 * Xj_sqr + H)) + Xj * I; + CT const L3 = (G * (asin_B - asin_Bj) + J * sqrt_Y - Jj * sqrt_Yj) / c6144; + + // Order 4 and higher + return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * (L2 + e_sqr * L3))); +} + +/*! +\brief The representation of geodesic as proposed by Sjoberg. +\see See + - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007 + http://link.springer.com/article/10.1007/s00190-007-0204-7 + - [Sjoberg12] Lars E. Sjoberg, Solutions to the ellipsoidal Clairaut constant + and the inverse geodetic problem by numerical integration, 2012 + https://www.degruyter.com/view/j/jogs.2012.2.issue-3/v10156-011-0037-4/v10156-011-0037-4.xml +*/ +template +class sjoberg_geodesic +{ + sjoberg_geodesic() {} + + static int sign_C(CT const& alphaj) + { + CT const c0 = 0; + CT const c2 = 2; + CT const pi = math::pi(); + CT const pi_half = pi / c2; + + return (pi_half < alphaj && alphaj < pi) || (-pi_half < alphaj && alphaj < c0) ? -1 : 1; + } + +public: + sjoberg_geodesic(CT const& lon, CT const& lat, CT const& alpha, CT const& f) + : lonj(lon) + , latj(lat) + , alphaj(alpha) + { + CT const c0 = 0; + CT const c1 = 1; + CT const c2 = 2; + //CT const pi = math::pi(); + //CT const pi_half = pi / c2; + + one_minus_f = c1 - f; + e_sqr = f * (c2 - f); + + tan_latj = tan(lat); + tan_betaj = one_minus_f * tan_latj; + betaj = atan(tan_betaj); + sin_betaj = sin(betaj); + + cos_betaj = cos(betaj); + sin_alphaj = sin(alphaj); + // Clairaut constant (lower-case in the paper) + Cj = sign_C(alphaj) * cos_betaj * sin_alphaj; + Cj_sqr = math::sqr(Cj); + sqrt_1_Cj_sqr = math::sqrt(c1 - Cj_sqr); + + sign_lon_diff = alphaj >= 0 ? 1 : -1; // || alphaj == -pi ? + //sign_lon_diff = 1; + + is_on_equator = math::equals(sqrt_1_Cj_sqr, c0); + is_Cj_zero = math::equals(Cj, c0); + + t0j = c0; + asin_tj_t0j = c0; + + if (! is_Cj_zero) + { + t0j = sqrt_1_Cj_sqr / Cj; + } + + if (! is_on_equator) + { + //asin_tj_t0j = asin(tan_betaj / t0j); + asin_tj_t0j = asin(tan_betaj * Cj / sqrt_1_Cj_sqr); + } + } + + struct vertex_data + { + //CT beta0j; + CT sin_beta0j; + CT dL0j; + CT lon0j; + }; + + vertex_data get_vertex_data() const + { + CT const c2 = 2; + CT const pi = math::pi(); + CT const pi_half = pi / c2; + + vertex_data res; + + if (! is_Cj_zero) + { + //res.beta0j = atan(t0j); + //res.sin_beta0j = sin(res.beta0j); + res.sin_beta0j = math::sign(t0j) * sqrt_1_Cj_sqr; + res.dL0j = d_lambda(res.sin_beta0j); + res.lon0j = lonj + sign_lon_diff * (pi_half - asin_tj_t0j + res.dL0j); + } + else + { + //res.beta0j = pi_half; + //res.sin_beta0j = betaj >= 0 ? 1 : -1; + res.sin_beta0j = 1; + res.dL0j = 0; + res.lon0j = lonj; + } + + return res; + } + + bool is_sin_beta_ok(CT const& sin_beta) const + { + CT const c1 = 1; + return math::abs(sin_beta / sqrt_1_Cj_sqr) <= c1; + } + + bool k_diff(CT const& sin_beta, + CT & delta_k) const + { + if (is_Cj_zero) + { + delta_k = 0; + return true; + } + + // beta out of bounds and not close + if (! (is_sin_beta_ok(sin_beta) + || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) ) + { + return false; + } + + // NOTE: beta may be slightly out of bounds here but d_lambda handles that + CT const dLj = d_lambda(sin_beta); + delta_k = sign_lon_diff * (/*asin_t_t0j*/ - asin_tj_t0j + dLj); + + return true; + } + + bool lon_diff(CT const& sin_beta, CT const& t, + CT & delta_lon) const + { + using math::detail::bounded; + CT const c1 = 1; + + if (is_Cj_zero) + { + delta_lon = 0; + return true; + } + + CT delta_k = 0; + if (! k_diff(sin_beta, delta_k)) + { + return false; + } + + CT const t_t0j = t / t0j; + // NOTE: t may be slightly out of bounds here + CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1)); + delta_lon = sign_lon_diff * asin_t_t0j + delta_k; + + return true; + } + + bool k_diffs(CT const& sin_beta, vertex_data const& vd, + CT & delta_k_before, CT & delta_k_behind, + bool check_sin_beta = true) const + { + CT const pi = math::pi(); + + if (is_Cj_zero) + { + delta_k_before = 0; + delta_k_behind = sign_lon_diff * pi; + return true; + } + + // beta out of bounds and not close + if (check_sin_beta + && ! (is_sin_beta_ok(sin_beta) + || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) ) + { + return false; + } + + // NOTE: beta may be slightly out of bounds here but d_lambda handles that + CT const dLj = d_lambda(sin_beta); + delta_k_before = sign_lon_diff * (/*asin_t_t0j*/ - asin_tj_t0j + dLj); + + // This version require no additional dLj calculation + delta_k_behind = sign_lon_diff * (pi /*- asin_t_t0j*/ - asin_tj_t0j + vd.dL0j + (vd.dL0j - dLj)); + + // [Sjoberg12] + //CT const dL101 = d_lambda(sin_betaj, vd.sin_beta0j); + // WARNING: the following call might not work if beta was OoB because only the second argument is bounded + //CT const dL_01 = d_lambda(sin_beta, vd.sin_beta0j); + //delta_k_behind = sign_lon_diff * (pi /*- asin_t_t0j*/ - asin_tj_t0j + dL101 + dL_01); + + return true; + } + + bool lon_diffs(CT const& sin_beta, CT const& t, vertex_data const& vd, + CT & delta_lon_before, CT & delta_lon_behind) const + { + using math::detail::bounded; + CT const c1 = 1; + CT const pi = math::pi(); + + if (is_Cj_zero) + { + delta_lon_before = 0; + delta_lon_behind = sign_lon_diff * pi; + return true; + } + + CT delta_k_before = 0, delta_k_behind = 0; + if (! k_diffs(sin_beta, vd, delta_k_before, delta_k_behind)) + { + return false; + } + + CT const t_t0j = t / t0j; + // NOTE: t may be slightly out of bounds here + CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1)); + CT const sign_asin_t_t0j = sign_lon_diff * asin_t_t0j; + delta_lon_before = sign_asin_t_t0j + delta_k_before; + delta_lon_behind = -sign_asin_t_t0j + delta_k_behind; + + return true; + } + + bool lon(CT const& sin_beta, CT const& t, vertex_data const& vd, + CT & lon_before, CT & lon_behind) const + { + using math::detail::bounded; + CT const c1 = 1; + CT const pi = math::pi(); + + if (is_Cj_zero) + { + lon_before = lonj; + lon_behind = lonj + sign_lon_diff * pi; + return true; + } + + if (! (is_sin_beta_ok(sin_beta) + || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) ) + { + return false; + } + + CT const t_t0j = t / t0j; + CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1)); + CT const dLj = d_lambda(sin_beta); + lon_before = lonj + sign_lon_diff * (asin_t_t0j - asin_tj_t0j + dLj); + lon_behind = vd.lon0j + (vd.lon0j - lon_before); + + return true; + } + + + CT lon(CT const& delta_lon) const + { + return lonj + delta_lon; + } + + CT lat(CT const& t) const + { + // t = tan(beta) = (1-f)tan(lat) + return atan(t / one_minus_f); + } + + void vertex(CT & lon, CT & lat) const + { + lon = get_vertex_data().lon0j; + if (! is_Cj_zero) + { + lat = sjoberg_geodesic::lat(t0j); + } + else + { + CT const c2 = 2; + lat = math::pi() / c2; + } + } + + CT lon_of_equator_intersection() const + { + CT const c0 = 0; + CT const dLj = d_lambda(c0); + CT const asin_tj_t0j = asin(Cj * tan_betaj / sqrt_1_Cj_sqr); + return lonj - asin_tj_t0j + dLj; + } + + CT d_lambda(CT const& sin_beta) const + { + return sjoberg_d_lambda_e_sqr(sin_betaj, sin_beta, Cj, sqrt_1_Cj_sqr, e_sqr); + } + + // [Sjoberg12] + /*CT d_lambda(CT const& sin_beta1, CT const& sin_beta2) const + { + return sjoberg_d_lambda_e_sqr(sin_beta1, sin_beta2, Cj, sqrt_1_Cj_sqr, e_sqr); + }*/ + + CT lonj; + CT latj; + CT alphaj; + + CT one_minus_f; + CT e_sqr; + + CT tan_latj; + CT tan_betaj; + CT betaj; + CT sin_betaj; + CT cos_betaj; + CT sin_alphaj; + CT Cj; + CT Cj_sqr; + CT sqrt_1_Cj_sqr; + + int sign_lon_diff; + + bool is_on_equator; + bool is_Cj_zero; + + CT t0j; + CT asin_tj_t0j; +}; + + /*! \brief The intersection of two geodesics as proposed by Sjoberg. -\author See +\see See - [Sjoberg02] Lars E. Sjoberg, Intersections on the sphere and ellipsoid, 2002 http://link.springer.com/article/10.1007/s00190-001-0230-9 - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007 http://link.springer.com/article/10.1007/s00190-007-0204-7 + - [Sjoberg12] Lars E. Sjoberg, Solutions to the ellipsoidal Clairaut constant + and the inverse geodetic problem by numerical integration, 2012 + https://www.degruyter.com/view/j/jogs.2012.2.issue-3/v10156-011-0037-4/v10156-011-0037-4.xml */ template < @@ -42,9 +629,14 @@ template > class sjoberg_intersection { + typedef sjoberg_geodesic geodesic_type; typedef Inverse inverse_type; typedef typename inverse_type::result_type inverse_result; + static bool const enable_02 = true; + static int const max_iterations_02 = 10; + static int const max_iterations_07 = 20; + public: template static inline bool apply(T1 const& lona1, T1 const& lata1, @@ -63,285 +655,515 @@ public: CT const lon_b2 = lonb2; CT const lat_b2 = latb2; - CT const alpha1 = inverse_type::apply(lon_a1, lat_a1, lon_a2, lat_a2, spheroid).azimuth; - CT const alpha2 = inverse_type::apply(lon_b1, lat_b1, lon_b2, lat_b2, spheroid).azimuth; + inverse_result const res1 = inverse_type::apply(lon_a1, lat_a1, lon_a2, lat_a2, spheroid); + inverse_result const res2 = inverse_type::apply(lon_b1, lat_b1, lon_b2, lat_b2, spheroid); - return apply(lon_a1, lat_a1, alpha1, lon_b1, lat_b1, alpha2, lon, lat, spheroid); + return apply(lon_a1, lat_a1, lon_a2, lat_a2, res1.azimuth, + lon_b1, lat_b1, lon_b2, lat_b2, res2.azimuth, + lon, lat, spheroid); } - + template - static inline bool apply(CT const& lon1, CT const& lat1, CT const& alpha1, - CT const& lon2, CT const& lat2, CT const& alpha2, + static inline bool apply(CT const& lon_a1, CT const& lat_a1, CT const& lon_a2, CT const& lat_a2, CT const& alpha_a1, + CT const& lon_b1, CT const& lat_b1, CT const& lon_b2, CT const& lat_b2, CT const& alpha_b1, CT & lon, CT & lat, Spheroid const& spheroid) { // coordinates in radians - // TODO - handle special cases like degenerated segments, equator, poles, etc. - CT const c0 = 0; CT const c1 = 1; - CT const c2 = 2; - CT const pi = math::pi(); - CT const pi_half = pi / c2; - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const one_minus_f = c1 - f; - CT const e_sqr = f * (c2 - f); - - CT const sin_alpha1 = sin(alpha1); - CT const sin_alpha2 = sin(alpha2); - CT const tan_beta1 = one_minus_f * tan(lat1); - CT const tan_beta2 = one_minus_f * tan(lat2); - CT const beta1 = atan(tan_beta1); - CT const beta2 = atan(tan_beta2); - CT const cos_beta1 = cos(beta1); - CT const cos_beta2 = cos(beta2); - CT const sin_beta1 = sin(beta1); - CT const sin_beta2 = sin(beta2); + geodesic_type geod1(lon_a1, lat_a1, alpha_a1, f); + geodesic_type geod2(lon_b1, lat_b1, alpha_b1, f); - // Clairaut constants (lower-case in the paper) - int const sign_C1 = math::abs(alpha1) <= pi_half ? 1 : -1; - int const sign_C2 = math::abs(alpha2) <= pi_half ? 1 : -1; - // Cj = 1 if on equator - CT const C1 = sign_C1 * cos_beta1 * sin_alpha1; - CT const C2 = sign_C2 * cos_beta2 * sin_alpha2; + // Cj = 1 if on equator <=> sqrt_1_Cj_sqr = 0 + // Cj = 0 if vertical <=> sqrt_1_Cj_sqr = 1 - CT const sqrt_1_C1_sqr = math::sqrt(c1 - math::sqr(C1)); - CT const sqrt_1_C2_sqr = math::sqrt(c1 - math::sqr(C2)); - - // handle special case: segments on the equator - bool const on_equator1 = math::equals(sqrt_1_C1_sqr, c0); - bool const on_equator2 = math::equals(sqrt_1_C2_sqr, c0); - if (on_equator1 && on_equator2) + if (geod1.is_on_equator && geod2.is_on_equator) { return false; } - else if (on_equator1) + else if (geod1.is_on_equator) { - CT const dL2 = d_lambda_e_sqr(sin_beta2, c0, C2, sqrt_1_C2_sqr, e_sqr); - CT const asin_t2_t02 = asin(C2 * tan_beta2 / sqrt_1_C2_sqr); + lon = geod2.lon_of_equator_intersection(); lat = c0; - lon = lon2 - asin_t2_t02 + dL2; return true; } - else if (on_equator2) + else if (geod2.is_on_equator) { - CT const dL1 = d_lambda_e_sqr(sin_beta1, c0, C1, sqrt_1_C1_sqr, e_sqr); - CT const asin_t1_t01 = asin(C1 * tan_beta1 / sqrt_1_C1_sqr); + lon = geod1.lon_of_equator_intersection(); lat = c0; - lon = lon1 - asin_t1_t01 + dL1; return true; } - CT const t01 = sqrt_1_C1_sqr / C1; - CT const t02 = sqrt_1_C2_sqr / C2; - - CT const asin_t1_t01 = asin(tan_beta1 / t01); - CT const asin_t2_t02 = asin(tan_beta2 / t02); - CT const t01_t02 = t01 * t02; - CT const t01_t02_2 = c2 * t01_t02; - CT const sqr_t01_sqr_t02 = math::sqr(t01) + math::sqr(t02); - - CT t = tan_beta1; - int t_id = 0; - - // find the initial t using simplified spherical solution - // though not entirely since the reduced latitudes and azimuths are spheroidal - // [Sjoberg07] - CT const k_base = lon1 - lon2 + asin_t2_t02 - asin_t1_t01; - + // vertical segments + if (geod1.is_Cj_zero && geod2.is_Cj_zero) { - CT const K = sin(k_base); - CT const d1 = sqr_t01_sqr_t02; - //CT const d2 = t01_t02_2 * math::sqrt(c1 - math::sqr(K)); - CT const d2 = t01_t02_2 * cos(k_base); - CT const D1 = math::sqrt(d1 - d2); - CT const D2 = math::sqrt(d1 + d2); - CT const K_t01_t02 = K * t01_t02; - - CT const T1 = K_t01_t02 / D1; - CT const T2 = K_t01_t02 / D2; - CT asin_T1_t01 = 0; - CT asin_T1_t02 = 0; - CT asin_T2_t01 = 0; - CT asin_T2_t02 = 0; - - // test 4 possible results - CT l1 = 0, l2 = 0, dl = 0; - bool found = check_t<0>( T1, - lon1, asin_T1_t01 = asin(T1 / t01), asin_t1_t01, - lon2, asin_T1_t02 = asin(T1 / t02), asin_t2_t02, - t, l1, l2, dl, t_id) - || check_t<1>(-T1, - lon1, -asin_T1_t01 , asin_t1_t01, - lon2, -asin_T1_t02 , asin_t2_t02, - t, l1, l2, dl, t_id) - || check_t<2>( T2, - lon1, asin_T2_t01 = asin(T2 / t01), asin_t1_t01, - lon2, asin_T2_t02 = asin(T2 / t02), asin_t2_t02, - t, l1, l2, dl, t_id) - || check_t<3>(-T2, - lon1, -asin_T2_t01 , asin_t1_t01, - lon2, -asin_T2_t02 , asin_t2_t02, - t, l1, l2, dl, t_id); - - boost::ignore_unused(found); + //TODO: the geodesics may be parallel or intersect at one of the poles + return false; } - // [Sjoberg07] - //int const d2_sign = t_id < 2 ? -1 : 1; - int const t_sign = (t_id % 2) ? -1 : 1; - // [Sjoberg02] - CT const C1_sqr = math::sqr(C1); - CT const C2_sqr = math::sqr(C2); - - CT beta = atan(t); - CT dL1 = 0, dL2 = 0; - CT asin_t_t01 = 0; - CT asin_t_t02 = 0; + // (lon1 - lon2) normalized to (-180, 180] + CT const lon1_minus_lon2 = math::longitude_distance_signed(geod2.lonj, geod1.lonj); - for (int i = 0; i < 10; ++i) + CT lon_sph = lon; + + // Starting tan(beta) + CT t = 0; + + /*if (geod1.is_Cj_zero) + { + CT const k_base = lon1_minus_lon2 + geod2.sign_lon_diff * geod2.asin_tj_t0j; + t = sin(k_base) * geod2.t0j; + lon_sph = vertical_intersection_longitude(geod1.lonj, lon_b1, lon_b2); + } + else if (geod2.is_Cj_zero) + { + CT const k_base = lon1_minus_lon2 - geod1.sign_lon_diff * geod1.asin_tj_t0j; + t = sin(-k_base) * geod1.t0j; + lon_sph = vertical_intersection_longitude(geod2.lonj, lon_a1, lon_a2); + } + else*/ + { + // TODO: Consider using betas instead of latitudes. + // Some function calls might be saved this way. + CT tan_lat_sph = 0; + sjoberg_intersection_spherical_02::apply_alt(lon_a1, lat_a1, lon_a2, lat_a2, + lon_b1, lat_b1, lon_b2, lat_b2, + lon_sph, tan_lat_sph); + t = one_minus_f * tan_lat_sph; // tan(beta) + } + + // TODO: no need to calculate atan here if reduced latitudes were used + // instead of latitudes above, in sjoberg_intersection_spherical_02 + CT const beta = atan(t); + + if (enable_02 && newton_method(geod1, geod2, beta, t, lon1_minus_lon2, lon, lat)) + { + return true; + } + + return converge_07(geod1, geod2, beta, t, lon1_minus_lon2, lon_sph, lon, lat); + } + +private: + static inline bool newton_method(geodesic_type const& geod1, geodesic_type const& geod2, // in + CT beta, CT t, CT const& lon1_minus_lon2, // in + CT & lon, CT & lat) // out + { + CT const c0 = 0; + CT const c1 = 1; + + CT const e_sqr = geod1.e_sqr; + + CT lon1_diff = 0; + CT lon2_diff = 0; + + CT abs_dbeta_last = 0; + + // [Sjoberg02] converges faster than solution in [Sjoberg07] + // Newton-Raphson method + for (int i = 0; i < max_iterations_02; ++i) { CT const sin_beta = sin(beta); - - // integrals approximation - dL1 = d_lambda_e_sqr(sin_beta1, sin_beta, C1, sqrt_1_C1_sqr, e_sqr); - dL2 = d_lambda_e_sqr(sin_beta2, sin_beta, C2, sqrt_1_C2_sqr, e_sqr); - - // [Sjoberg07] - /*CT const k = k_base + dL1 - dL2; - CT const K = sin(k); - CT const d1 = sqr_t01_sqr_t02; - //CT const d2 = t01_t02_2 * math::sqrt(c1 - math::sqr(K)); - CT const d2 = t01_t02_2 * cos(k); - CT const D = math::sqrt(d1 + d2_sign * d2); - CT const t_new = t_sign * K * t01_t02 / D; - CT const dt = math::abs(t_new - t); - t = t_new; - CT const new_beta = atan(t); - CT const dbeta = math::abs(new_beta - beta); - beta = new_beta;*/ - - // [Sjoberg02] - it converges faster - // Newton–Raphson method - asin_t_t01 = asin(t / t01); - asin_t_t02 = asin(t / t02); - CT const R1 = asin_t_t01 + dL1; - CT const R2 = asin_t_t02 + dL2; CT const cos_beta = cos(beta); CT const cos_beta_sqr = math::sqr(cos_beta); CT const G = c1 - e_sqr * cos_beta_sqr; - CT const f1 = C1 / cos_beta * math::sqrt(G / (cos_beta_sqr - C1_sqr)); - CT const f2 = C2 / cos_beta * math::sqrt(G / (cos_beta_sqr - C2_sqr)); - CT const abs_f1 = math::abs(f1); - CT const abs_f2 = math::abs(f2); - CT const dbeta = t_sign * (k_base - R2 + R1) / (abs_f1 + abs_f2); - - if (math::equals(dbeta, CT(0))) + + CT f1 = 0; + CT f2 = 0; + + if (!geod1.is_Cj_zero) { + bool is_beta_ok = geod1.lon_diff(sin_beta, t, lon1_diff); + + if (is_beta_ok) + { + CT const H = cos_beta_sqr - geod1.Cj_sqr; + f1 = geod1.Cj / cos_beta * math::sqrt(G / H); + } + else + { + return false; + } + } + + if (!geod2.is_Cj_zero) + { + bool is_beta_ok = geod2.lon_diff(sin_beta, t, lon2_diff); + + if (is_beta_ok) + { + CT const H = cos_beta_sqr - geod2.Cj_sqr; + f2 = geod2.Cj / cos_beta * math::sqrt(G / H); + } + else + { + return false; + } + } + + // NOTE: Things may go wrong if the IP is near the vertex + // 1. May converge into the wrong direction (from the other way around). + // This happens when the starting point is on the other side than the vertex + // 2. During converging may "jump" into the other side of the vertex. + // In this case sin_beta/sqrt_1_Cj_sqr and t/t0j is not in [-1, 1] + // 3. f1-f2 may be 0 which means that the intermediate point is on the vertex + // In this case it's not possible to check if this is the correct result + + CT const dbeta_denom = f1 - f2; + //CT const dbeta_denom = math::abs(f1) + math::abs(f2); + + if (math::equals(dbeta_denom, c0)) + { + return false; + } + + // The sign of dbeta is changed WRT [Sjoberg02] + CT const dbeta = (lon1_minus_lon2 + lon1_diff - lon2_diff) / dbeta_denom; + + CT const abs_dbeta = math::abs(dbeta); + if (i > 0 && abs_dbeta > abs_dbeta_last) + { + // The algorithm is not converging + // The intersection may be on the other side of the vertex + return false; + } + abs_dbeta_last = abs_dbeta; + + if (math::equals(dbeta, c0)) + { + // Result found break; } + // Because the sign of dbeta is changed WRT [Sjoberg02] dbeta is subtracted here beta = beta - dbeta; + t = tan(beta); } - - // t = tan(beta) = (1-f)tan(lat) - lat = atan(t / one_minus_f); - CT const l1 = lon1 + asin_t_t01 - asin_t1_t01 + dL1; - //CT const l2 = lon2 + asin_t_t02 - asin_t2_t02 + dL2; - lon = l1; + lat = geod1.lat(t); + // NOTE: if Cj is 0 then the result is lonj or lonj+180 + lon = ! geod1.is_Cj_zero + ? geod1.lon(lon1_diff) + : geod2.lon(lon2_diff); return true; } -private: - /*! Approximation of dLambda_j [Sjoberg07], expanded into taylor series in e^2 - Maxima script: - dLI_j(c_j, sinB_j, sinB) := integrate(1 / (sqrt(1 - c_j ^ 2 - x ^ 2)*(1 + sqrt(1 - e2*(1 - x ^ 2)))), x, sinB_j, sinB); - dL_j(c_j, B_j, B) := -e2 * c_j * dLI_j(c_j, B_j, B); - S: taylor(dLI_j(c_j, sinB_j, sinB), e2, 0, 3); - assume(c_j < 1); - assume(c_j > 0); - L1: factor(integrate(sqrt(-x ^ 2 - c_j ^ 2 + 1) / (x ^ 2 + c_j ^ 2 - 1), x)); - L2: factor(integrate(((x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); - L3: factor(integrate(((x ^ 4 - 2 * x ^ 2 + 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); - L4: factor(integrate(((x ^ 6 - 3 * x ^ 4 + 3 * x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); - */ - static inline CT d_lambda_e_sqr(CT const& sin_betaj, CT const& sin_beta, - CT const& Cj, CT const& sqrt_1_Cj_sqr, - CT const& e_sqr) + struct geodesics_type { - if (Order == 0) + geodesics_type(geodesic_type const& g1, geodesic_type const& g2) + : geod1(g1) + , geod2(g2) + , vertex1(geod1.get_vertex_data()) + , vertex2(geod2.get_vertex_data()) + {} + + geodesic_type const& geod1; + geodesic_type const& geod2; + typename geodesic_type::vertex_data vertex1; + typename geodesic_type::vertex_data vertex2; + }; + + struct converge_07_result + { + converge_07_result() + : lon1(0), lon2(0), k1_diff(0), k2_diff(0), t1(0), t2(0) + {} + + CT lon1, lon2; + CT k1_diff, k2_diff; + CT t1, t2; + }; + + static inline bool converge_07(geodesic_type const& geod1, geodesic_type const& geod2, + CT beta, CT t, + CT const& lon1_minus_lon2, CT const& lon_sph, + CT & lon, CT & lat) + { + //CT const c0 = 0; + //CT const c1 = 1; + //CT const c2 = 2; + //CT const pi = math::pi(); + + geodesics_type geodesics(geod1, geod2); + converge_07_result result; + + // calculate first pair of longitudes + if (!converge_07_step_one(CT(sin(beta)), t, lon1_minus_lon2, geodesics, lon_sph, result, false)) { - return 0; + return false; } - CT const c2 = 2; - - CT const asin_B = asin(sin_beta / sqrt_1_Cj_sqr); - CT const asin_Bj = asin(sin_betaj / sqrt_1_Cj_sqr); - CT const L0 = (asin_B - asin_Bj) / c2; + int t_direction = 0; - if (Order == 1) + CT lon_diff_prev = math::longitude_difference(result.lon1, result.lon2); + + // [Sjoberg07] + for (int i = 2; i < max_iterations_07; ++i) { - return -Cj * e_sqr * L0; + // pick t candidates from previous result based on dir + CT t_cand1 = result.t1; + CT t_cand2 = result.t2; + // if direction is 0 the closer one is the first + if (t_direction < 0) + { + t_cand1 = (std::min)(result.t1, result.t2); + t_cand2 = (std::max)(result.t1, result.t2); + } + else if (t_direction > 0) + { + t_cand1 = (std::max)(result.t1, result.t2); + t_cand2 = (std::min)(result.t1, result.t2); + } + else + { + t_direction = t_cand1 < t_cand2 ? -1 : 1; + } + + CT t1 = t; + CT beta1 = beta; + // check if the further calculation is needed + if (converge_07_update(t1, beta1, t_cand1)) + { + break; + } + + bool try_t2 = false; + converge_07_result result_curr; + if (converge_07_step_one(CT(sin(beta1)), t1, lon1_minus_lon2, geodesics, lon_sph, result_curr)) + { + CT const lon_diff1 = math::longitude_difference(result_curr.lon1, result_curr.lon2); + if (lon_diff_prev > lon_diff1) + { + t = t1; + beta = beta1; + lon_diff_prev = lon_diff1; + result = result_curr; + } + else if (t_cand1 != t_cand2) + { + try_t2 = true; + } + else + { + // the result is not fully correct but it won't be more accurate + break; + } + } + // ! converge_07_step_one + else + { + if (t_cand1 != t_cand2) + { + try_t2 = true; + } + else + { + return false; + } + } + + + if (try_t2) + { + CT t2 = t; + CT beta2 = beta; + // check if the further calculation is needed + if (converge_07_update(t2, beta2, t_cand2)) + { + break; + } + + if (! converge_07_step_one(CT(sin(beta2)), t2, lon1_minus_lon2, geodesics, lon_sph, result_curr)) + { + return false; + } + + CT const lon_diff2 = math::longitude_difference(result_curr.lon1, result_curr.lon2); + if (lon_diff_prev > lon_diff2) + { + t_direction *= -1; + t = t2; + beta = beta2; + lon_diff_prev = lon_diff2; + result = result_curr; + } + else + { + // the result is not fully correct but it won't be more accurate + break; + } + } } + lat = geod1.lat(t); + lon = ! geod1.is_Cj_zero ? result.lon1 : result.lon2; + math::normalize_longitude(lon); + + return true; + } + + static inline bool converge_07_update(CT & t, CT & beta, CT const& t_new) + { + CT const c0 = 0; + + CT const beta_new = atan(t_new); + CT const dbeta = beta_new - beta; + beta = beta_new; + t = t_new; + + return math::equals(dbeta, c0); + } + + static inline CT const& pick_t(CT const& t1, CT const& t2, int direction) + { + return direction < 0 ? (std::min)(t1, t2) : (std::max)(t1, t2); + } + + static inline bool converge_07_step_one(CT const& sin_beta, + CT const& t, + CT const& lon1_minus_lon2, + geodesics_type const& geodesics, + CT const& lon_sph, + converge_07_result & result, + bool check_sin_beta = true) + { + bool ok = converge_07_one_geod(sin_beta, t, geodesics.geod1, geodesics.vertex1, lon_sph, + result.lon1, result.k1_diff, check_sin_beta) + && converge_07_one_geod(sin_beta, t, geodesics.geod2, geodesics.vertex2, lon_sph, + result.lon2, result.k2_diff, check_sin_beta); + + if (!ok) + { + return false; + } + + CT const k = lon1_minus_lon2 + result.k1_diff - result.k2_diff; + + // get 2 possible ts one lesser and one greater than t + // t1 is the closer one + calc_ts(t, k, geodesics.geod1, geodesics.geod2, result.t1, result.t2); + + return true; + } + + static inline bool converge_07_one_geod(CT const& sin_beta, CT const& t, + geodesic_type const& geod, + typename geodesic_type::vertex_data const& vertex, + CT const& lon_sph, + CT & lon, CT & k_diff, + bool check_sin_beta) + { + using math::detail::bounded; CT const c1 = 1; - CT const c16 = 16; + + CT k_diff_before = 0; + CT k_diff_behind = 0; - CT const X = sin_beta; - CT const Xj = sin_betaj; - CT const Cj_sqr = math::sqr(Cj); - CT const Cj_sqr_plus_one = Cj_sqr + c1; - CT const one_minus_Cj_sqr = c1 - Cj_sqr; - CT const sqrt_Y = math::sqrt(-math::sqr(X) + one_minus_Cj_sqr); - CT const sqrt_Yj = math::sqrt(-math::sqr(Xj) + one_minus_Cj_sqr); - CT const L1 = (Cj_sqr_plus_one * (asin_B - asin_Bj) + X * sqrt_Y - Xj * sqrt_Yj) / c16; + bool is_beta_ok = geod.k_diffs(sin_beta, vertex, k_diff_before, k_diff_behind, check_sin_beta); - if (Order == 2) + if (! is_beta_ok) { - return -Cj * e_sqr * (L0 + e_sqr * L1); + return false; } - CT const c3 = 3; - CT const c5 = 5; - CT const c128 = 128; + CT const asin_t_t0j = ! geod.is_Cj_zero ? asin(bounded(t / geod.t0j, -c1, c1)) : 0; + CT const sign_asin_t_t0j = geod.sign_lon_diff * asin_t_t0j; - CT const E = Cj_sqr * (c3 * Cj_sqr + c2) + c3; - CT const X_sqr = math::sqr(X); - CT const Xj_sqr = math::sqr(Xj); - CT const F = X * (-c2 * X_sqr + c3 * Cj_sqr + c5); - CT const Fj = Xj * (-c2 * Xj_sqr + c3 * Cj_sqr + c5); - CT const L2 = (E * (asin_B - asin_Bj) + F * sqrt_Y - Fj * sqrt_Yj) / c128; + CT const lon_before = geod.lonj + sign_asin_t_t0j + k_diff_before; + CT const lon_behind = geod.lonj - sign_asin_t_t0j + k_diff_behind; - if (Order == 3) + CT const lon_dist_before = math::longitude_distance_signed(lon_before, lon_sph); + CT const lon_dist_behind = math::longitude_distance_signed(lon_behind, lon_sph); + if (math::abs(lon_dist_before) <= math::abs(lon_dist_behind)) { - return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * L2)); + k_diff = k_diff_before; + lon = lon_before; + } + else + { + k_diff = k_diff_behind; + lon = lon_behind; } - CT const c8 = 8; - CT const c9 = 9; - CT const c10 = 10; - CT const c15 = 15; - CT const c24 = 24; - CT const c26 = 26; - CT const c33 = 33; - CT const c6144 = 6144; + return true; + } - CT const G = Cj_sqr * (Cj_sqr * (Cj_sqr * c15 + c9) + c9) + c15; - CT const H = -c10 * Cj_sqr - c26; - CT const I = Cj_sqr * (Cj_sqr * c15 + c24) + c33; - CT const J = X_sqr * (X * (c8 * X_sqr + H)) + X * I; - CT const Jj = Xj_sqr * (Xj * (c8 * Xj_sqr + H)) + Xj * I; - CT const L3 = (G * (asin_B - asin_Bj) + J * sqrt_Y - Jj * sqrt_Yj) / c6144; + static inline void calc_ts(CT const& t, CT const& k, + geodesic_type const& geod1, geodesic_type const& geod2, + CT & t1, CT& t2) + { + CT const c1 = 1; + CT const c2 = 2; - // Order 4 and higher - return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * (L2 + e_sqr * L3))); + CT const K = sin(k); + + BOOST_GEOMETRY_ASSERT(!geod1.is_Cj_zero || !geod2.is_Cj_zero); + if (geod1.is_Cj_zero) + { + t1 = K * geod2.t0j; + t2 = -t1; + } + else if (geod2.is_Cj_zero) + { + t1 = -K * geod1.t0j; + t2 = -t1; + } + else + { + CT const A = math::sqr(geod1.t0j) + math::sqr(geod2.t0j); + CT const B = c2 * geod1.t0j * geod2.t0j * math::sqrt(c1 - math::sqr(K)); + + CT const K_t01_t02 = K * geod1.t0j * geod2.t0j; + CT const D1 = math::sqrt(A + B); + CT const D2 = math::sqrt(A - B); + CT const t_new1 = K_t01_t02 / D1; + CT const t_new2 = K_t01_t02 / D2; + CT const t_new3 = -t_new1; + CT const t_new4 = -t_new2; + + // Pick 2 nearest t_new, one greater and one lesser than current t + CT const abs_t_new1 = math::abs(t_new1); + CT const abs_t_new2 = math::abs(t_new2); + CT const abs_t_max = (std::max)(abs_t_new1, abs_t_new2); + t1 = -abs_t_max; // lesser + t2 = abs_t_max; // greater + if (t1 < t) + { + if (t_new1 < t && t_new1 > t1) + t1 = t_new1; + if (t_new2 < t && t_new2 > t1) + t1 = t_new2; + if (t_new3 < t && t_new3 > t1) + t1 = t_new3; + if (t_new4 < t && t_new4 > t1) + t1 = t_new4; + } + if (t2 > t) + { + if (t_new1 > t && t_new1 < t2) + t2 = t_new1; + if (t_new2 > t && t_new2 < t2) + t2 = t_new2; + if (t_new3 > t && t_new3 < t2) + t2 = t_new3; + if (t_new4 > t && t_new4 < t2) + t2 = t_new4; + } + } + + // the first one is the closer one + if (math::abs(t - t2) < math::abs(t - t1)) + { + std::swap(t2, t1); + } } static inline CT fj(CT const& cos_beta, CT const& cos2_beta, CT const& Cj, CT const& e_sqr) @@ -351,32 +1173,18 @@ private: return Cj / cos_beta * math::sqrt((c1 - e_sqr * cos2_beta) / (cos2_beta - Cj_sqr)); } - template - static inline bool check_t(CT const& t, - CT const& lon_a1, CT const& asin_t_t01, CT const& asin_t1_t01, - CT const& lon_b1, CT const& asin_t_t02, CT const& asin_t2_t02, - CT & current_t, CT & current_lon1, CT & current_lon2, CT & current_dlon, - int & t_id) + /*static inline CT vertical_intersection_longitude(CT const& ip_lon, CT const& seg_lon1, CT const& seg_lon2) { - CT const lon1 = lon_a1 + asin_t_t01 - asin_t1_t01; - CT const lon2 = lon_b1 + asin_t_t02 - asin_t2_t02; + CT const c0 = 0; + CT const lon_2 = ip_lon > c0 ? ip_lon - pi : ip_lon + pi; - // TODO - true angle difference - CT const dlon = math::abs(lon2 - lon1); - - bool are_equal = math::equals(dlon, CT(0)); - - if ((TId == 0) || are_equal || dlon < current_dlon) - { - current_t = t; - current_lon1 = lon1; - current_lon2 = lon2; - current_dlon = dlon; - t_id = TId; - } - - return are_equal; - } + return (std::min)(math::longitude_difference(ip_lon, seg_lon1), + math::longitude_difference(ip_lon, seg_lon2)) + <= + (std::min)(math::longitude_difference(lon_2, seg_lon1), + math::longitude_difference(lon_2, seg_lon2)) + ? ip_lon : lon_2; + }*/ }; }}} // namespace boost::geometry::formula diff --git a/include/boost/geometry/formulas/spherical.hpp b/include/boost/geometry/formulas/spherical.hpp index 23269d644..ff24c51a8 100644 --- a/include/boost/geometry/formulas/spherical.hpp +++ b/include/boost/geometry/formulas/spherical.hpp @@ -40,38 +40,55 @@ struct result_spherical T reverse_azimuth; }; +template +static inline void sph_to_cart3d(T const& lon, T const& lat, T & x, T & y, T & z) +{ + T const cos_lat = cos(lat); + x = cos_lat * cos(lon); + y = cos_lat * sin(lon); + z = sin(lat); +} + template static inline Point3d sph_to_cart3d(PointSph const& point_sph) { typedef typename coordinate_type::type calc_t; + calc_t const lon = get_as_radian<0>(point_sph); + calc_t const lat = get_as_radian<1>(point_sph); + calc_t x, y, z; + sph_to_cart3d(lon, lat, x, y, z); + Point3d res; - - calc_t lon = get_as_radian<0>(point_sph); - calc_t lat = get_as_radian<1>(point_sph); - - calc_t const cos_lat = cos(lat); - set<0>(res, cos_lat * cos(lon)); - set<1>(res, cos_lat * sin(lon)); - set<2>(res, sin(lat)); + set<0>(res, x); + set<1>(res, y); + set<2>(res, z); return res; } +template +static inline void cart3d_to_sph(T const& x, T const& y, T const& z, T & lon, T & lat) +{ + lon = atan2(y, x); + lat = asin(z); +} + template static inline PointSph cart3d_to_sph(Point3d const& point_3d) { typedef typename coordinate_type::type coord_t; typedef typename coordinate_type::type calc_t; - PointSph res; - calc_t const x = get<0>(point_3d); calc_t const y = get<1>(point_3d); calc_t const z = get<2>(point_3d); + calc_t lonr, latr; + cart3d_to_sph(x, y, z, lonr, latr); - set_from_radian<0>(res, atan2(y, x)); - set_from_radian<1>(res, asin(z)); + PointSph res; + set_from_radian<0>(res, lonr); + set_from_radian<1>(res, latr); coord_t lon = get<0>(res); coord_t lat = get<1>(res); @@ -104,9 +121,9 @@ static inline int sph_side_value(Point3d1 const& norm, Point3d2 const& pt) template static inline result_spherical spherical_azimuth(T1 const& lon1, - T1 const& lat1, - T2 const& lon2, - T2 const& lat2) + T1 const& lat1, + T2 const& lon2, + T2 const& lat2) { typedef result_spherical result_type; result_type result; @@ -114,12 +131,6 @@ static inline result_spherical spherical_azimuth(T1 const& lon1, // http://williams.best.vwh.net/avform.htm#Crs // https://en.wikipedia.org/wiki/Great-circle_navigation CT dlon = lon2 - lon1; - CT cos_dlon = cos(dlon); - CT sin_dlon = sin(dlon); - CT cos_lat1 = cos(lat1); - CT cos_lat2 = cos(lat2); - CT sin_lat1 = sin(lat1); - CT sin_lat2 = sin(lat2); // An optimization which should kick in often for Boxes //if ( math::equals(dlon, ReturnType(0)) ) @@ -128,21 +139,84 @@ static inline result_spherical spherical_azimuth(T1 const& lon1, // return - sin(get_as_radian<1>(p1)) * cos_p2lat); //} - // "An alternative formula, not requiring the pre-computation of d" - // In the formula below dlon is used as "d" - result.azimuth = atan2(sin_dlon * cos_lat2, - cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_dlon); + CT const cos_dlon = cos(dlon); + CT const sin_dlon = sin(dlon); + CT const cos_lat1 = cos(lat1); + CT const cos_lat2 = cos(lat2); + CT const sin_lat1 = sin(lat1); + CT const sin_lat2 = sin(lat2); + + { + // "An alternative formula, not requiring the pre-computation of d" + // In the formula below dlon is used as "d" + CT const y = sin_dlon * cos_lat2; + CT const x = cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_dlon; + result.azimuth = atan2(y, x); + } if (ReverseAzimuth) { - result.reverse_azimuth = - atan2(sin_dlon * cos_lat1, - sin_lat2 * cos_lat1 * cos_dlon - cos_lat2 * sin_lat1); + CT const y = sin_dlon * cos_lat1; + CT const x = sin_lat2 * cos_lat1 * cos_dlon - cos_lat2 * sin_lat1; + result.reverse_azimuth = atan2(y, x); } return result; } +template +inline ReturnType spherical_azimuth(T1 const& lon1, T1 const& lat1, + T2 const& lon2, T2 const& lat2) +{ + return spherical_azimuth(lon1, lat1, lon2, lat2).azimuth; +} + +template +inline T spherical_azimuth(T const& lon1, T const& lat1, T const& lon2, T const& lat2) +{ + return spherical_azimuth(lon1, lat1, lon2, lat2).azimuth; +} + +template +inline int azimuth_side_value(T const& azi_a1_p, T const& azi_a1_a2) +{ + T const pi = math::pi(); + T const two_pi = math::two_pi(); + + // instead of the formula from XTD + //calc_t a_diff = asin(sin(azi_a1_p - azi_a1_a2)); + + T a_diff = azi_a1_p - azi_a1_a2; + // normalize, angle in [-pi, pi] + while (a_diff > pi) + a_diff -= two_pi; + while (a_diff < -pi) + a_diff += two_pi; + + // NOTE: in general it shouldn't be required to support the pi/-pi case + // because in non-cartesian systems it makes sense to check the side + // only "between" the endpoints. + // However currently the winding strategy calls the side strategy + // for vertical segments to check if the point is "between the endpoints. + // This could be avoided since the side strategy is not required for that + // because meridian is the shortest path. So a difference of + // longitudes would be sufficient (of course normalized to [-pi, pi]). + + // NOTE: with the above said, the pi/-pi check is temporary + // however in case if this was required + // the geodesics on ellipsoid aren't "symmetrical" + // therefore instead of comparing a_diff to pi and -pi + // one should probably use inverse azimuths and compare + // the difference to 0 as well + + // positive azimuth is on the right side + return math::equals(a_diff, 0) + || math::equals(a_diff, pi) + || math::equals(a_diff, -pi) ? 0 + : a_diff > 0 ? -1 // right + : 1; // left +} + } // namespace formula }} // namespace boost::geometry diff --git a/include/boost/geometry/formulas/thomas_direct.hpp b/include/boost/geometry/formulas/thomas_direct.hpp index f8a7f8394..f208167cf 100644 --- a/include/boost/geometry/formulas/thomas_direct.hpp +++ b/include/boost/geometry/formulas/thomas_direct.hpp @@ -20,9 +20,8 @@ #include #include -#include - #include +#include #include @@ -82,7 +81,7 @@ public: CT const a = CT(get_radius<0>(spheroid)); CT const b = CT(get_radius<2>(spheroid)); - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const one_minus_f = c1 - f; CT const pi = math::pi(); diff --git a/include/boost/geometry/formulas/thomas_inverse.hpp b/include/boost/geometry/formulas/thomas_inverse.hpp index d68c9de05..0853a3698 100644 --- a/include/boost/geometry/formulas/thomas_inverse.hpp +++ b/include/boost/geometry/formulas/thomas_inverse.hpp @@ -20,9 +20,8 @@ #include #include -#include - #include +#include #include @@ -78,7 +77,7 @@ public: CT const c4 = 4; CT const pi_half = math::pi() / c2; - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const one_minus_f = c1 - f; // CT const tan_theta1 = one_minus_f * tan(lat1); diff --git a/include/boost/geometry/formulas/vertex_latitude.hpp b/include/boost/geometry/formulas/vertex_latitude.hpp index 1d4c24a82..755067b08 100644 --- a/include/boost/geometry/formulas/vertex_latitude.hpp +++ b/include/boost/geometry/formulas/vertex_latitude.hpp @@ -12,9 +12,9 @@ #ifndef BOOST_GEOMETRY_FORMULAS_MAXIMUM_LATITUDE_HPP #define BOOST_GEOMETRY_FORMULAS_MAXIMUM_LATITUDE_HPP -#include -#include #include +#include +#include #include namespace boost { namespace geometry { namespace formula @@ -55,7 +55,7 @@ public: T2 const& alp1, Spheroid const& spheroid) { - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const e2 = f * (CT(2) - f); CT const sin_alp1 = sin(alp1); @@ -76,7 +76,7 @@ public: T2 const& alp1, Spheroid const& spheroid) { - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const one_minus_f = (CT(1) - f); diff --git a/include/boost/geometry/formulas/vincenty_direct.hpp b/include/boost/geometry/formulas/vincenty_direct.hpp index f3647ff4e..1697e5fb6 100644 --- a/include/boost/geometry/formulas/vincenty_direct.hpp +++ b/include/boost/geometry/formulas/vincenty_direct.hpp @@ -23,9 +23,8 @@ #include #include -#include - #include +#include #include @@ -85,7 +84,7 @@ public: CT const radius_a = CT(get_radius<0>(spheroid)); CT const radius_b = CT(get_radius<2>(spheroid)); - CT const flattening = geometry::detail::flattening(spheroid); + CT const flattening = formula::flattening(spheroid); CT const sin_azimuth12 = sin(azimuth12); CT const cos_azimuth12 = cos(azimuth12); diff --git a/include/boost/geometry/formulas/vincenty_inverse.hpp b/include/boost/geometry/formulas/vincenty_inverse.hpp index bbda00036..032e16e29 100644 --- a/include/boost/geometry/formulas/vincenty_inverse.hpp +++ b/include/boost/geometry/formulas/vincenty_inverse.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2016. -// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2016, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -23,9 +23,8 @@ #include #include -#include - #include +#include #include @@ -41,7 +40,7 @@ namespace boost { namespace geometry { namespace formula \brief The solution of the inverse problem of geodesics on latlong coordinates, after Vincenty, 1975 \author See - http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf - - http://www.icsm.gov.au/gda/gdav2.3.pdf + - http://www.icsm.gov.au/gda/gda-v_2.4.pdf \author Adapted from various implementations to get it close to the original document - http://www.movable-type.co.uk/scripts/LatLongVincenty.html - http://exogen.case.edu/projects/geopy/source/geopy.distance.html @@ -99,10 +98,10 @@ public: CT const radius_a = CT(get_radius<0>(spheroid)); CT const radius_b = CT(get_radius<2>(spheroid)); - CT const flattening = geometry::detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); // U: reduced latitude, defined by tan U = (1-f) tan phi - CT const one_min_f = c1 - flattening; + CT const one_min_f = c1 - f; CT const tan_U1 = one_min_f * tan(lat1); // above (1) CT const tan_U2 = one_min_f * tan(lat2); // above (1) @@ -113,8 +112,9 @@ public: CT const cos_U1 = c1 / temp_den_U1; CT const cos_U2 = c1 / temp_den_U2; // sin = tan / sqrt(1 + tan^2) - CT const sin_U1 = tan_U1 / temp_den_U1; - CT const sin_U2 = tan_U2 / temp_den_U2; + // sin = tan * cos + CT const sin_U1 = tan_U1 * cos_U1; + CT const sin_U2 = tan_U2 * cos_U2; // calculate sin U and cos U directly //CT const U1 = atan(tan_U1); @@ -130,7 +130,8 @@ public: CT sin_sigma; CT sin_alpha; CT cos2_alpha; - CT cos2_sigma_m; + CT cos_2sigma_m; + CT cos2_2sigma_m; CT sigma; int counter = 0; // robustness @@ -144,12 +145,13 @@ public: CT cos_sigma = sin_U1 * sin_U2 + cos_U1 * cos_U2 * cos_lambda; // (15) sin_alpha = cos_U1 * cos_U2 * sin_lambda / sin_sigma; // (17) cos2_alpha = c1 - math::sqr(sin_alpha); - cos2_sigma_m = math::equals(cos2_alpha, 0) ? 0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18) + cos_2sigma_m = math::equals(cos2_alpha, 0) ? 0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18) + cos2_2sigma_m = math::sqr(cos_2sigma_m); - CT C = flattening/c16 * cos2_alpha * (c4 + flattening * (c4 - c3 * cos2_alpha)); // (10) + CT C = f/c16 * cos2_alpha * (c4 + f * (c4 - c3 * cos2_alpha)); // (10) sigma = atan2(sin_sigma, cos_sigma); // (16) - lambda = L + (c1 - C) * flattening * sin_alpha * - (sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * (-c1 + c2 * math::sqr(cos2_sigma_m)))); // (11) + lambda = L + (c1 - C) * f * sin_alpha * + (sigma + C * sin_sigma * (cos_2sigma_m + C * cos_sigma * (-c1 + c2 * cos2_2sigma_m))); // (11) ++counter; // robustness @@ -182,8 +184,10 @@ public: CT A = c1 + sqr_u/c16384 * (c4096 + sqr_u * (-c768 + sqr_u * (c320 - c175 * sqr_u))); // (3) CT B = sqr_u/c1024 * (c256 + sqr_u * ( -c128 + sqr_u * (c74 - c47 * sqr_u))); // (4) - CT delta_sigma = B * sin_sigma * ( cos2_sigma_m + (B/c4) * (cos(sigma)* (-c1 + c2 * cos2_sigma_m) - - (B/c6) * cos2_sigma_m * (-c3 + c4 * math::sqr(sin_sigma)) * (-c3 + c4 * cos2_sigma_m))); // (6) + CT const cos_sigma = cos(sigma); + CT const sin2_sigma = math::sqr(sin_sigma); + CT delta_sigma = B * sin_sigma * (cos_2sigma_m + (B/c4) * (cos_sigma* (-c1 + c2 * cos2_2sigma_m) + - (B/c6) * cos_2sigma_m * (-c3 + c4 * sin2_sigma) * (-c3 + c4 * cos2_2sigma_m))); // (6) result.distance = radius_b * A * (sigma - delta_sigma); // (19) } @@ -206,7 +210,7 @@ public: typedef differential_quantities quantities; quantities::apply(lon1, lat1, lon2, lat2, result.azimuth, result.reverse_azimuth, - radius_b, flattening, + radius_b, f, result.reduced_length, result.geodesic_scale); } diff --git a/include/boost/geometry/index/detail/algorithms/nth_element.hpp b/include/boost/geometry/index/detail/algorithms/nth_element.hpp new file mode 100644 index 000000000..201180ae3 --- /dev/null +++ b/include/boost/geometry/index/detail/algorithms/nth_element.hpp @@ -0,0 +1,62 @@ +// Boost.Geometry Index +// +// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_INDEX_DETAIL_ALGORITHMS_NTH_ELEMENT_HPP +#define BOOST_GEOMETRY_INDEX_DETAIL_ALGORITHMS_NTH_ELEMENT_HPP + +#include + +namespace boost { namespace geometry { namespace index { namespace detail { + +// See https://svn.boost.org/trac/boost/ticket/12861 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58800 +// https://gcc.gnu.org/develop.html#timeline +// 20120920 4.7.2 - no bug +// 20130322 4.8.0 - no bug +// 20130411 4.7.3 - no bug +// 20130531 4.8.1 - no bug +// 20131016 4.8.2 - bug +// 20140422 4.9.0 - fixed +// 20140522 4.8.3 - fixed +// 20140612 4.7.4 - fixed +// 20140716 4.9.1 - fixed +#if defined(__GLIBCXX__) && (__GLIBCXX__ == 20131016) + +#warning "std::nth_element replaced with std::sort, libstdc++ bug workaround."; + +template +void nth_element(RandomIt first, RandomIt , RandomIt last) +{ + std::sort(first, last); +} + +template +void nth_element(RandomIt first, RandomIt , RandomIt last, Compare comp) +{ + std::sort(first, last, comp); +} + +#else + +template +void nth_element(RandomIt first, RandomIt nth, RandomIt last) +{ + std::nth_element(first, nth, last); +} + +template +void nth_element(RandomIt first, RandomIt nth, RandomIt last, Compare comp) +{ + std::nth_element(first, nth, last, comp); +} + +#endif + +}}}} // namespace boost::geometry::index::detail + +#endif // BOOST_GEOMETRY_INDEX_DETAIL_ALGORITHMS_NTH_ELEMENT_HPP diff --git a/include/boost/geometry/index/detail/rtree/pack_create.hpp b/include/boost/geometry/index/detail/rtree/pack_create.hpp index d1491b8d4..2d3903a7b 100644 --- a/include/boost/geometry/index/detail/rtree/pack_create.hpp +++ b/include/boost/geometry/index/detail/rtree/pack_create.hpp @@ -2,7 +2,7 @@ // // R-tree initial packing // -// Copyright (c) 2011-2015 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -13,6 +13,7 @@ #include #include +#include #include @@ -67,7 +68,7 @@ struct nth_element_and_half_boxes { if ( I == dim_index ) { - std::nth_element(first, median, last, point_entries_comparer()); + index::detail::nth_element(first, median, last, point_entries_comparer()); geometry::convert(box, left); geometry::convert(box, right); diff --git a/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp b/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp index 7a96986a2..89697b594 100644 --- a/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp +++ b/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp @@ -2,7 +2,7 @@ // // R-tree R*-tree next node choosing algorithm implementation // -// Copyright (c) 2011-2014 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -112,7 +113,7 @@ private: first_n_children_count = overlap_cost_threshold; // rearrange by content_diff // in order to calculate nearly minimum overlap cost - std::nth_element(children_contents.begin(), children_contents.begin() + first_n_children_count, children_contents.end(), content_diff_less); + index::detail::nth_element(children_contents.begin(), children_contents.begin() + first_n_children_count, children_contents.end(), content_diff_less); } // calculate minimum or nearly minimum overlap cost diff --git a/include/boost/geometry/index/detail/rtree/rstar/redistribute_elements.hpp b/include/boost/geometry/index/detail/rtree/rstar/redistribute_elements.hpp index 8f270537f..187d37fac 100644 --- a/include/boost/geometry/index/detail/rtree/rstar/redistribute_elements.hpp +++ b/include/boost/geometry/index/detail/rtree/rstar/redistribute_elements.hpp @@ -2,7 +2,7 @@ // // R-tree R*-tree split algorithm implementation // -// Copyright (c) 2011-2014 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -12,8 +12,9 @@ #define BOOST_GEOMETRY_INDEX_DETAIL_RTREE_RSTAR_REDISTRIBUTE_ELEMENTS_HPP #include -#include #include +#include +#include #include @@ -122,8 +123,9 @@ struct choose_split_axis_and_index_for_corner // { // typename Elements::iterator f = elements_copy.begin() + index_first; // typename Elements::iterator l = elements_copy.begin() + index_last; -// std::nth_element(elements_copy.begin(), f, elements_copy.end(), elements_less); // MAY THROW, BASIC (copy) -// std::nth_element(f, l, elements_copy.end(), elements_less); // MAY THROW, BASIC (copy) +// // NOTE: for stdlibc++ shipped with gcc 4.8.2 std::nth_element is replaced with std::sort anyway +// index::detail::nth_element(elements_copy.begin(), f, elements_copy.end(), elements_less); // MAY THROW, BASIC (copy) +// index::detail::nth_element(f, l, elements_copy.end(), elements_less); // MAY THROW, BASIC (copy) // std::sort(f, l, elements_less); // MAY THROW, BASIC (copy) // } @@ -349,7 +351,7 @@ struct nth_element typedef typename tag::type indexable_tag; element_axis_corner_less less(tr); - std::nth_element(elements.begin(), elements.begin() + index, elements.end(), less); // MAY THROW, BASIC (copy) + index::detail::nth_element(elements.begin(), elements.begin() + index, elements.end(), less); // MAY THROW, BASIC (copy) } } }; diff --git a/include/boost/geometry/index/parameters.hpp b/include/boost/geometry/index/parameters.hpp index 6a3a67c6c..1a9469c10 100644 --- a/include/boost/geometry/index/parameters.hpp +++ b/include/boost/geometry/index/parameters.hpp @@ -2,7 +2,7 @@ // // R-tree algorithms parameters // -// Copyright (c) 2011-2013 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -162,8 +162,8 @@ public: \param max_elements Maximum number of elements in nodes. \param min_elements Minimum number of elements in nodes. Default: 0.3*Max. */ - dynamic_linear(size_t max_elements, - size_t min_elements = detail::default_min_elements_d()) + explicit dynamic_linear(size_t max_elements, + size_t min_elements = detail::default_min_elements_d()) : m_max_elements(max_elements) , m_min_elements(detail::default_min_elements_d_calc(max_elements, min_elements)) { @@ -191,8 +191,8 @@ public: \param max_elements Maximum number of elements in nodes. \param min_elements Minimum number of elements in nodes. Default: 0.3*Max. */ - dynamic_quadratic(size_t max_elements, - size_t min_elements = detail::default_min_elements_d()) + explicit dynamic_quadratic(size_t max_elements, + size_t min_elements = detail::default_min_elements_d()) : m_max_elements(max_elements) , m_min_elements(detail::default_min_elements_d_calc(max_elements, min_elements)) { @@ -228,10 +228,10 @@ public: nearly minimum overlap cost, otherwise all leafs are analyzed and true minimum overlap cost is calculated. Default: 32. */ - dynamic_rstar(size_t max_elements, - size_t min_elements = detail::default_min_elements_d(), - size_t reinserted_elements = detail::default_rstar_reinserted_elements_d(), - size_t overlap_cost_threshold = 32) + explicit dynamic_rstar(size_t max_elements, + size_t min_elements = detail::default_min_elements_d(), + size_t reinserted_elements = detail::default_rstar_reinserted_elements_d(), + size_t overlap_cost_threshold = 32) : m_max_elements(max_elements) , m_min_elements(detail::default_min_elements_d_calc(max_elements, min_elements)) , m_reinserted_elements(detail::default_rstar_reinserted_elements_d_calc(max_elements, reinserted_elements)) diff --git a/include/boost/geometry/strategies/cartesian/area_surveyor.hpp b/include/boost/geometry/strategies/cartesian/area_surveyor.hpp index ba76f6794..b3f19b1b1 100644 --- a/include/boost/geometry/strategies/cartesian/area_surveyor.hpp +++ b/include/boost/geometry/strategies/cartesian/area_surveyor.hpp @@ -4,8 +4,8 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 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 @@ -25,6 +25,7 @@ //#include #include #include +#include #include diff --git a/include/boost/geometry/strategies/cartesian/cart_intersect.hpp b/include/boost/geometry/strategies/cartesian/cart_intersect.hpp index 1f6f56d66..e4c2baa99 100644 --- a/include/boost/geometry/strategies/cartesian/cart_intersect.hpp +++ b/include/boost/geometry/strategies/cartesian/cart_intersect.hpp @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -95,6 +97,39 @@ struct relate_cartesian_segments return strategy_type(); } + template + struct area_strategy + { + typedef area::surveyor + < + typename point_type::type, + CalculationType + > type; + }; + + template + static inline typename area_strategy::type get_area_strategy() + { + typedef typename area_strategy::type strategy_type; + return strategy_type(); + } + + template + struct distance_strategy + { + typedef distance::pythagoras + < + CalculationType + > type; + }; + + template + static inline typename distance_strategy::type get_distance_strategy() + { + typedef typename distance_strategy::type strategy_type; + return strategy_type(); + } + template struct segment_intersection_info { diff --git a/include/boost/geometry/strategies/geographic/area_geographic.hpp b/include/boost/geometry/strategies/geographic/area_geographic.hpp index efa79df46..d5cd240a7 100644 --- a/include/boost/geometry/strategies/geographic/area_geographic.hpp +++ b/include/boost/geometry/strategies/geographic/area_geographic.hpp @@ -1,7 +1,8 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2016 Oracle and/or its affiliates. +// Copyright (c) 2016-2017 Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fisikopoulos, 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 @@ -11,6 +12,7 @@ #define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_AREA_HPP #include +#include #include #include @@ -21,12 +23,26 @@ namespace strategy { namespace area { /*! -\brief Geographic area calculation by trapezoidal rule plus integral -approximation that gives the ellipsoidal correction - +\brief Geographic area calculation +\ingroup strategies +\details Geographic area calculation by trapezoidal rule plus integral + approximation that gives the ellipsoidal correction +\tparam PointOfSegment \tparam_segment_point +\tparam Inverse Formula used to calculate azimuths +\tparam SeriesOrder The order of approximation of the geodesic integral +\tparam ExpandEpsN Switch between two kinds of approximation (series in eps and n v.s. series in k^2 and e'^2) +\tparam LongSegment Enables special handling of long segments +\tparam Spheroid The spheroid model +\tparam CalculationType \tparam_calculation +\author See +- Danielsen JS, The area under the geodesic. Surv Rev 30(232): 61–66, 1989 +- Charles F.F Karney, Algorithms for geodesics, 2011 https://arxiv.org/pdf/1109.4448.pdf +\qbk{ +[heading See also] +[link geometry.reference.algorithms.area.area_2_with_strategy area (with strategy)] +} */ - template < typename PointOfSegment, @@ -87,8 +103,8 @@ protected : inline spheroid_constants(SpheroidType spheroid) : m_spheroid(spheroid) , m_a2(math::sqr(get_radius<0>(spheroid))) - , m_e2(detail::flattening(spheroid) - * (CT(2.0) - CT(detail::flattening(spheroid)))) + , m_e2(formula::flattening(spheroid) + * (CT(2.0) - CT(formula::flattening(spheroid)))) , m_ep2(m_e2 / (CT(1.0) - m_e2)) , m_ep(math::sqrt(m_ep2)) , m_c2((m_a2 / CT(2.0)) + diff --git a/include/boost/geometry/strategies/geographic/distance.hpp b/include/boost/geometry/strategies/geographic/distance.hpp new file mode 100644 index 000000000..9be21d49c --- /dev/null +++ b/include/boost/geometry/strategies/geographic/distance.hpp @@ -0,0 +1,194 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2016 Barend Gehrels, Amsterdam, the Netherlands. + +// 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 + +// 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_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_HPP + + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace distance +{ + +template +< + template class InverseFormula = formula::andoyer_inverse, + typename Spheroid = srs::spheroid, + typename CalculationType = void +> +class geographic +{ +public : + template + struct calculation_type + : promote_floating_point + < + typename select_calculation_type + < + Point1, + Point2, + CalculationType + >::type + > + {}; + + typedef Spheroid model_type; + + inline geographic() + : m_spheroid() + {} + + explicit inline geographic(Spheroid const& spheroid) + : m_spheroid(spheroid) + {} + + template + inline typename calculation_type::type + apply(Point1 const& point1, Point2 const& point2) const + { + return InverseFormula + < + typename calculation_type::type, + true, false, false, false, false + >::apply(get_as_radian<0>(point1), get_as_radian<1>(point1), + get_as_radian<0>(point2), get_as_radian<1>(point2), + m_spheroid).distance; + } + + inline Spheroid const& model() const + { + return m_spheroid; + } + +private : + Spheroid m_spheroid; +}; + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace services +{ + +template +< + template class Formula, + typename Spheroid, + typename CalculationType +> +struct tag > +{ + typedef strategy_tag_distance_point_point type; +}; + + +template +< + template class Formula, + typename Spheroid, + typename CalculationType, + typename P1, + typename P2 +> +struct return_type, P1, P2> + : geographic::template calculation_type +{}; + + +template +< + template class Formula, + typename Spheroid, + typename CalculationType +> +struct comparable_type > +{ + typedef geographic type; +}; + + +template +< + template class Formula, + typename Spheroid, + typename CalculationType +> +struct get_comparable > +{ + static inline geographic + apply(geographic const& input) + { + return input; + } +}; + +template +< + template class Formula, + typename Spheroid, + typename CalculationType, + typename P1, + typename P2 +> +struct result_from_distance, P1, P2> +{ + template + static inline typename return_type, P1, P2>::type + apply(geographic const& , T const& value) + { + return value; + } +}; + + +template +struct default_strategy +{ + typedef strategy::distance::geographic + < + formula::andoyer_inverse, + srs::spheroid + < + typename select_coordinate_type::type + > + > type; +}; + + +} // namespace services +#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + + +}} // namespace strategy::distance + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_HPP diff --git a/include/boost/geometry/strategies/geographic/distance_andoyer.hpp b/include/boost/geometry/strategies/geographic/distance_andoyer.hpp index 1946cd109..5ff54d53c 100644 --- a/include/boost/geometry/strategies/geographic/distance_andoyer.hpp +++ b/include/boost/geometry/strategies/geographic/distance_andoyer.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2016 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2016. -// Modifications copyright (c) 2014-2016 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 @@ -11,24 +11,13 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_ANDOYER_HPP -#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_ANDOYER_HPP +#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_DETAIL_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_DETAIL_HPP -#include -#include -#include -#include - -#include - #include -#include - -#include -#include -#include +#include namespace boost { namespace geometry @@ -61,51 +50,24 @@ template typename CalculationType = void > class andoyer + : public strategy::distance::geographic + < + formula::andoyer_inverse, Spheroid, CalculationType + > { + typedef strategy::distance::geographic + < + formula::andoyer_inverse, Spheroid, CalculationType + > base_type; + public : - template - struct calculation_type - : promote_floating_point - < - typename select_calculation_type - < - Point1, - Point2, - CalculationType - >::type - > - {}; - - typedef Spheroid model_type; - inline andoyer() - : m_spheroid() + : base_type() {} explicit inline andoyer(Spheroid const& spheroid) - : m_spheroid(spheroid) + : base_type(spheroid) {} - - template - inline typename calculation_type::type - apply(Point1 const& point1, Point2 const& point2) const - { - return geometry::formula::andoyer_inverse - < - typename calculation_type::type, - true, false - >::apply(get_as_radian<0>(point1), get_as_radian<1>(point1), - get_as_radian<0>(point2), get_as_radian<1>(point2), - m_spheroid).distance; - } - - inline Spheroid const& model() const - { - return m_spheroid; - } - -private : - Spheroid m_spheroid; }; @@ -154,19 +116,6 @@ struct result_from_distance, P1, P2> }; -template -struct default_strategy -{ - typedef strategy::distance::andoyer - < - srs::spheroid - < - typename select_coordinate_type::type - > - > type; -}; - - } // namespace services #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS @@ -177,4 +126,4 @@ struct default_strategy -#include - -#include - -#include -#include - #include +#include + + namespace boost { namespace geometry { @@ -49,53 +44,24 @@ template typename CalculationType = void > class thomas + : public strategy::distance::geographic + < + formula::thomas_inverse, Spheroid, CalculationType + > { + typedef strategy::distance::geographic + < + formula::thomas_inverse, Spheroid, CalculationType + > base_type; + public : - template - struct calculation_type - : promote_floating_point - < - typename select_calculation_type - < - Point1, - Point2, - CalculationType - >::type - > - {}; - - typedef Spheroid model_type; - inline thomas() - : m_spheroid() + : base_type() {} explicit inline thomas(Spheroid const& spheroid) - : m_spheroid(spheroid) + : base_type(spheroid) {} - - template - inline typename calculation_type::type - apply(Point1 const& point1, Point2 const& point2) const - { - return geometry::formula::thomas_inverse - < - typename calculation_type::type, - true, false - >::apply(get_as_radian<0>(point1), - get_as_radian<1>(point1), - get_as_radian<0>(point2), - get_as_radian<1>(point2), - m_spheroid).distance; - } - - inline Spheroid const& model() const - { - return m_spheroid; - } - -private : - Spheroid m_spheroid; }; #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS diff --git a/include/boost/geometry/strategies/geographic/distance_vincenty.hpp b/include/boost/geometry/strategies/geographic/distance_vincenty.hpp index e79e9aeb4..d3f3663ad 100644 --- a/include/boost/geometry/strategies/geographic/distance_vincenty.hpp +++ b/include/boost/geometry/strategies/geographic/distance_vincenty.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2016. -// Modifications copyright (c) 2014-2016 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 @@ -15,16 +15,11 @@ #define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_VINCENTY_HPP -#include -#include - -#include - -#include -#include - #include +#include + + namespace boost { namespace geometry { @@ -51,53 +46,24 @@ template typename CalculationType = void > class vincenty + : public strategy::distance::geographic + < + formula::vincenty_inverse, Spheroid, CalculationType + > { -public : - template - struct calculation_type - : promote_floating_point - < - typename select_calculation_type - < - Point1, - Point2, - CalculationType - >::type - > - {}; - - typedef Spheroid model_type; + typedef strategy::distance::geographic + < + formula::vincenty_inverse, Spheroid, CalculationType + > base_type; +public: inline vincenty() - : m_spheroid() + : base_type() {} explicit inline vincenty(Spheroid const& spheroid) - : m_spheroid(spheroid) + : base_type(spheroid) {} - - template - inline typename calculation_type::type - apply(Point1 const& point1, Point2 const& point2) const - { - return geometry::formula::vincenty_inverse - < - typename calculation_type::type, - true, false - >::apply(get_as_radian<0>(point1), - get_as_radian<1>(point1), - get_as_radian<0>(point2), - get_as_radian<1>(point2), - m_spheroid).distance; - } - - inline Spheroid const& model() const - { - return m_spheroid; - } - -private : - Spheroid m_spheroid; }; #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS diff --git a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp new file mode 100644 index 000000000..e2dc96e39 --- /dev/null +++ b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp @@ -0,0 +1,893 @@ +// Boost.Geometry + +// 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, +// 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_GEODESIC_INTERSECTION_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_GEODESIC_INTERSECTION_HPP + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace intersection +{ + +// CONSIDER: Improvement of the robustness/accuracy/repeatability by +// moving all segments to 0 longitude +// picking latitudes closer to 0 +// etc. + +template +< + typename Spheroid = srs::spheroid, + template class Inverse = formula::andoyer_inverse, + unsigned int Order = 2, + typename CalculationType = void +> +struct relate_geodesic_segments +{ + typedef side::geographic + < + Inverse, Spheroid, CalculationType + > side_strategy_type; + + inline side_strategy_type get_side_strategy() + { + return side_strategy_type(m_spheroid); + } + + template + struct point_in_geometry_strategy + { + typedef strategy::within::winding + < + typename point_type::type, + typename point_type::type, + side_strategy_type, + CalculationType + > type; + }; + + template + inline typename point_in_geometry_strategy::type + get_point_in_geometry_strategy() + { + typedef typename point_in_geometry_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + return strategy_type(get_side_strategy()); + } + + template + struct area_strategy + { + typedef area::geographic + < + typename point_type::type, + Inverse, + Order, + true, + false, + Spheroid, + CalculationType + > type; + }; + + template + inline typename area_strategy::type get_area_strategy() + { + typedef typename area_strategy::type strategy_type; + return strategy_type(m_spheroid); + } + + template + struct distance_strategy + { + typedef distance::geographic type; + }; + + template + inline typename distance_strategy::type get_distance_strategy() + { + typedef typename distance_strategy::type strategy_type; + return strategy_type(m_spheroid); + } + + enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 }; + + template + struct segment_intersection_info + { + typedef typename select_most_precise + < + CoordinateType, double + >::type promoted_type; + + promoted_type comparable_length_a() const + { + return robust_ra.denominator(); + } + + promoted_type comparable_length_b() const + { + return robust_rb.denominator(); + } + + template + void assign_a(Point& point, Segment1 const& a, Segment2 const& b) const + { + assign(point, a, b); + } + template + void assign_b(Point& point, Segment1 const& a, Segment2 const& b) const + { + assign(point, a, b); + } + + template + void assign(Point& point, Segment1 const& a, Segment2 const& b) const + { + if (ip_flag == ipi_inters) + { + // TODO: assign the rest of coordinates + set_from_radian<0>(point, lon); + set_from_radian<1>(point, lat); + } + else if (ip_flag == ipi_at_a1) + { + detail::assign_point_from_index<0>(a, point); + } + else if (ip_flag == ipi_at_a2) + { + detail::assign_point_from_index<1>(a, point); + } + else if (ip_flag == ipi_at_b1) + { + detail::assign_point_from_index<0>(b, point); + } + else // ip_flag == ipi_at_b2 + { + detail::assign_point_from_index<1>(b, point); + } + } + + CoordinateType lon; + CoordinateType lat; + SegmentRatio robust_ra; + SegmentRatio robust_rb; + intersection_point_flag ip_flag; + }; + + relate_geodesic_segments(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) + {} + + // Relate segments a and b + template + < + typename Segment1, + typename Segment2, + typename Policy, + typename RobustPolicy + > + inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b, + Policy const& policy, + RobustPolicy const& robust_policy) const + { + typedef typename point_type::type point1_t; + typedef typename point_type::type point2_t; + point1_t a1, a2; + point2_t b1, b2; + + detail::assign_point_from_index<0>(a, a1); + detail::assign_point_from_index<1>(a, a2); + detail::assign_point_from_index<0>(b, b1); + detail::assign_point_from_index<1>(b, b2); + + return apply(a, b, policy, robust_policy, a1, a2, b1, b2); + } + + // Relate segments a and b + template + < + typename Segment1, + typename Segment2, + typename Policy, + typename RobustPolicy, + typename Point1, + typename Point2 + > + inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b, + Policy const&, RobustPolicy const&, + Point1 a1, Point1 a2, Point2 b1, Point2 b2) const + { + bool is_a_reversed = get<1>(a1) > get<1>(a2); + bool is_b_reversed = get<1>(b1) > get<1>(b2); + + if (is_a_reversed) + { + std::swap(a1, a2); + } + + if (is_b_reversed) + { + std::swap(b1, b2); + } + + return apply(a, b, a1, a2, b1, b2, is_a_reversed, is_b_reversed); + } + +private: + // Relate segments a and b + template + < + typename Policy, + typename Segment1, + typename Segment2, + typename Point1, + typename Point2 + > + inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b, + Point1 const& a1, Point1 const& a2, + Point2 const& b1, Point2 const& b2, + bool is_a_reversed, bool is_b_reversed) const + { + BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); + BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); + + typedef typename select_calculation_type + ::type calc_t; + + // normalized spheroid + srs::spheroid spheroid = normalized_spheroid(m_spheroid); + + // TODO: check only 2 first coordinates here? + using geometry::detail::equals::equals_point_point; + bool a_is_point = equals_point_point(a1, a2); + bool b_is_point = equals_point_point(b1, b2); + + if(a_is_point && b_is_point) + { + return equals_point_point(a1, b2) + ? Policy::degenerate(a, true) + : Policy::disjoint() + ; + } + + calc_t const a1_lon = get_as_radian<0>(a1); + calc_t const a1_lat = get_as_radian<1>(a1); + calc_t const a2_lon = get_as_radian<0>(a2); + calc_t const a2_lat = get_as_radian<1>(a2); + calc_t const b1_lon = get_as_radian<0>(b1); + calc_t const b1_lat = get_as_radian<1>(b1); + calc_t const b2_lon = get_as_radian<0>(b2); + calc_t const b2_lat = get_as_radian<1>(b2); + + side_info sides; + + // NOTE: potential optimization, don't calculate distance at this point + // this would require to reimplement inverse strategy to allow + // calculation of distance if needed, probably also storing intermediate + // results somehow inside an object. + typedef Inverse inverse_dist_azi; + typedef typename inverse_dist_azi::result_type inverse_result; + + // 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); + + 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>()) + { + // 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); + + 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>()) + { + // Both points are at the same side of other segment, we can leave + return Policy::disjoint(); + } + + // NOTE: at this point the segments may still be disjoint + // NOTE: at this point one of the segments may be degenerated + + bool collinear = sides.collinear(); + + if (! collinear) + { + // WARNING: the side strategy doesn't have the info about the other + // segment so it may return results inconsistent with this intersection + // strategy, as it checks both segments for consistency + + if (sides.get<0, 0>() == 0 && sides.get<0, 1>() == 0) + { + collinear = true; + sides.set<1>(0, 0); + } + else if (sides.get<1, 0>() == 0 && sides.get<1, 1>() == 0) + { + collinear = true; + sides.set<0>(0, 0); + } + } + + if (collinear) + { + if (a_is_point) + { + return collinear_one_degenerated(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1, is_b_reversed); + } + else if (b_is_point) + { + return collinear_one_degenerated(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1, is_a_reversed); + } + else + { + calc_t dist_a1_a2, dist_a1_b1, dist_a1_b2; + calc_t dist_b1_b2, dist_b1_a1, dist_b1_a2; + // 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); + 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); + dist_a1_a2 = dist_b1_a2 - dist_b1_a1; + dist_a1_b1 = -dist_b1_a1; + dist_a1_b2 = dist_b1_b2 - dist_b1_a1; + } + + // NOTE: this is probably not needed + calc_t const c0 = 0; + int a1_on_b = position_value(c0, dist_a1_b1, dist_a1_b2); + int a2_on_b = position_value(dist_a1_a2, dist_a1_b1, dist_a1_b2); + int b1_on_a = position_value(c0, dist_b1_a1, dist_b1_a2); + int b2_on_a = position_value(dist_b1_b2, dist_b1_a1, dist_b1_a2); + + if ((a1_on_b < 1 && a2_on_b < 1) || (a1_on_b > 3 && a2_on_b > 3)) + { + return Policy::disjoint(); + } + + if (a1_on_b == 1) + { + dist_b1_a1 = 0; + dist_a1_b1 = 0; + } + else if (a1_on_b == 3) + { + dist_b1_a1 = dist_b1_b2; + dist_a1_b2 = 0; + } + + if (a2_on_b == 1) + { + dist_b1_a2 = 0; + dist_a1_b1 = dist_a1_a2; + } + else if (a2_on_b == 3) + { + dist_b1_a2 = dist_b1_b2; + dist_a1_b2 = dist_a1_a2; + } + + bool opposite = ! same_direction(res_a1_a2.azimuth, res_b1_b2.azimuth); + + // NOTE: If segment was reversed opposite, positions and segment ratios has to be altered + if (is_a_reversed) + { + // opposite + opposite = ! opposite; + // positions + std::swap(a1_on_b, a2_on_b); + b1_on_a = 4 - b1_on_a; + b2_on_a = 4 - b2_on_a; + // distances for ratios + std::swap(dist_b1_a1, dist_b1_a2); + dist_a1_b1 = dist_a1_a2 - dist_a1_b1; + dist_a1_b2 = dist_a1_a2 - dist_a1_b2; + } + if (is_b_reversed) + { + // opposite + opposite = ! opposite; + // positions + a1_on_b = 4 - a1_on_b; + a2_on_b = 4 - a2_on_b; + std::swap(b1_on_a, b2_on_a); + // distances for ratios + dist_b1_a1 = dist_b1_b2 - dist_b1_a1; + dist_b1_a2 = dist_b1_b2 - dist_b1_a2; + std::swap(dist_a1_b1, dist_a1_b2); + } + + segment_ratio ra_from(dist_b1_a1, dist_b1_b2); + segment_ratio ra_to(dist_b1_a2, dist_b1_b2); + segment_ratio rb_from(dist_a1_b1, dist_a1_a2); + segment_ratio rb_to(dist_a1_b2, dist_a1_a2); + + return Policy::segments_collinear(a, b, opposite, + a1_on_b, a2_on_b, b1_on_a, b2_on_a, + ra_from, ra_to, rb_from, rb_to); + } + } + else // crossing or touching + { + if (a_is_point || b_is_point) + { + return Policy::disjoint(); + } + + calc_t lon = 0, lat = 0; + intersection_point_flag ip_flag; + calc_t dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1; + if (calculate_ip_data(a1, a2, b1, b2, + a1_lon, a1_lat, a2_lon, a2_lat, + b1_lon, b1_lat, b2_lon, b2_lat, + res_a1_a2, res_a1_b1, res_a1_b2, + res_b1_b2, res_b1_a1, res_b1_a2, + sides, spheroid, + lon, lat, + dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1, + ip_flag)) + { + // NOTE: If segment was reversed sides and segment ratios has to be altered + if (is_a_reversed) + { + // sides + sides_reverse_segment<0>(sides); + // distance for ratio + dist_a1_i1 = dist_a1_a2 - dist_a1_i1; + // ip flag + ip_flag_reverse_segment(ip_flag, ipi_at_a1, ipi_at_a2); + } + if (is_b_reversed) + { + // sides + sides_reverse_segment<1>(sides); + // distance for ratio + dist_b1_i1 = dist_b1_b2 - dist_b1_i1; + // ip flag + ip_flag_reverse_segment(ip_flag, ipi_at_b1, ipi_at_b2); + } + + // intersects + segment_intersection_info + < + calc_t, + segment_ratio + > sinfo; + + sinfo.lon = lon; + sinfo.lat = lat; + sinfo.robust_ra.assign(dist_a1_i1, dist_a1_a2); + sinfo.robust_rb.assign(dist_b1_i1, dist_b1_b2); + sinfo.ip_flag = ip_flag; + + return Policy::segments_crosses(sides, sinfo, a, b); + } + else + { + return Policy::disjoint(); + } + } + } + + template + static inline typename Policy::return_type + collinear_one_degenerated(Segment const& segment, bool degenerated_a, + 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) + { + 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)) + { + return Policy::disjoint(); + } + + // NOTE: If segment was reversed segment ratio has to be altered + if (is_other_reversed) + { + // distance for ratio + dist_1_o = dist_1_2 - dist_1_o; + } + + return Policy::one_degenerate(segment, segment_ratio(dist_1_o, dist_1_2), degenerated_a); + } + + // TODO: instead of checks below test bi against a1 and a2 here? + // in order to make this independent from is_near() + template + 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 + { + 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 = -dist_a1_bi; + } + + // if i1 is close to a1 and b1 or b2 is equal to a1 + if (is_endpoint_equal(dist_a1_bi, a1, b1, b2)) + { + dist_a1_bi = 0; + return true; + } + // or i1 is close to a2 and b1 or b2 is equal to a2 + else if (is_endpoint_equal(dist_a1_a2 - dist_a1_bi, a2, b1, b2)) + { + dist_a1_bi = dist_a1_a2; + return true; + } + + // or i1 is on b + return segment_ratio(dist_a1_bi, dist_a1_a2).on_segment(); + } + + template + static inline bool calculate_ip_data(Point1 const& a1, Point1 const& a2, // in + Point2 const& b1, Point2 const& b2, // in + CalcT const& a1_lon, CalcT const& a1_lat, // in + CalcT const& a2_lon, CalcT const& a2_lat, // in + CalcT const& b1_lon, CalcT const& b1_lat, // in + CalcT const& b2_lon, CalcT const& b2_lat, // in + ResultInverse const& res_a1_a2, // in + ResultInverse const& res_a1_b1, // in + ResultInverse const& res_a1_b2, // in + ResultInverse const& res_b1_b2, // in + ResultInverse const& res_b1_a1, // in + ResultInverse const& res_b1_a2, // in + side_info const& sides, // in + Spheroid_ const& spheroid, // in + CalcT & lon, CalcT & lat, // 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 + { + dist_a1_a2 = res_a1_a2.distance; + dist_b1_b2 = res_b1_b2.distance; + + // assign the IP if some endpoints overlap + using geometry::detail::equals::equals_point_point; + if (equals_point_point(a1, b1)) + { + lon = a1_lon; + lat = a1_lat; + dist_a1_ip = 0; + dist_b1_ip = 0; + ip_flag = ipi_at_a1; + return true; + } + else if (equals_point_point(a1, b2)) + { + lon = a1_lon; + lat = a1_lat; + dist_a1_ip = 0; + dist_b1_ip = dist_b1_b2; + ip_flag = ipi_at_a1; + return true; + } + else if (equals_point_point(a2, b1)) + { + lon = a2_lon; + lat = a2_lat; + dist_a1_ip = dist_a1_a2; + dist_b1_ip = 0; + ip_flag = ipi_at_a2; + return true; + } + else if (equals_point_point(a2, b2)) + { + lon = a2_lon; + lat = a2_lat; + dist_a1_ip = dist_a1_a2; + dist_b1_ip = dist_b1_b2; + ip_flag = ipi_at_a2; + return true; + } + + // at this point we know that the endpoints doesn't overlap + // check cases when an endpoint lies on the other geodesic + if (sides.template get<0, 0>() == 0) // a1 wrt b + { + if (res_b1_a1.distance <= res_b1_b2.distance + && same_direction(res_b1_a1.azimuth, res_b1_b2.azimuth)) + { + lon = a1_lon; + lat = a1_lat; + dist_a1_ip = 0; + dist_b1_ip = res_b1_a1.distance; + ip_flag = ipi_at_a1; + return true; + } + else + { + return false; + } + } + else if (sides.template get<0, 1>() == 0) // a2 wrt b + { + if (res_b1_a2.distance <= res_b1_b2.distance + && same_direction(res_b1_a2.azimuth, res_b1_b2.azimuth)) + { + lon = a2_lon; + lat = a2_lat; + dist_a1_ip = res_a1_a2.distance; + dist_b1_ip = res_b1_a2.distance; + ip_flag = ipi_at_a2; + return true; + } + else + { + return false; + } + } + else if (sides.template get<1, 0>() == 0) // b1 wrt a + { + if (res_a1_b1.distance <= res_a1_a2.distance + && same_direction(res_a1_b1.azimuth, res_a1_a2.azimuth)) + { + lon = b1_lon; + lat = b1_lat; + dist_a1_ip = res_a1_b1.distance; + dist_b1_ip = 0; + ip_flag = ipi_at_b1; + return true; + } + else + { + return false; + } + } + else if (sides.template get<1, 1>() == 0) // b2 wrt a + { + if (res_a1_b2.distance <= res_a1_a2.distance + && same_direction(res_a1_b2.azimuth, res_a1_a2.azimuth)) + { + lon = b2_lon; + lat = b2_lat; + dist_a1_ip = res_a1_b2.distance; + dist_b1_ip = res_b1_b2.distance; + ip_flag = ipi_at_b2; + return true; + } + else + { + return false; + } + } + + // At this point neither the endpoints overlaps + // nor any andpoint lies on the other geodesic + // So the endpoints should lie on the opposite sides of both geodesics + + bool const ok = formula::sjoberg_intersection::apply( + a1_lon, a1_lat, a2_lon, a2_lat, res_a1_a2.azimuth, + b1_lon, b1_lat, b2_lon, b2_lat, res_b1_b2.azimuth, + lon, lat, spheroid); + + if (! ok) + { + return false; + } + + typedef Inverse inverse_dist_azi; + typedef typename inverse_dist_azi::result_type inverse_result; + + inverse_result const res_a1_ip = inverse_dist_azi::apply(a1_lon, a1_lat, lon, lat, spheroid); + dist_a1_ip = res_a1_ip.distance; + if (! same_direction(res_a1_ip.azimuth, res_a1_a2.azimuth)) + { + dist_a1_ip = -dist_a1_ip; + } + + bool is_on_a = segment_ratio(dist_a1_ip, dist_a1_a2).on_segment(); + // NOTE: not fully consistent with equals_point_point() since radians are always used. + bool is_on_a1 = math::equals(lon, a1_lon) && math::equals(lat, a1_lat); + bool is_on_a2 = math::equals(lon, a2_lon) && math::equals(lat, a2_lat); + + if (! (is_on_a || is_on_a1 || is_on_a2)) + { + return false; + } + + inverse_result const res_b1_ip = inverse_dist_azi::apply(b1_lon, b1_lat, lon, lat, spheroid); + dist_b1_ip = res_b1_ip.distance; + if (! same_direction(res_b1_ip.azimuth, res_b1_b2.azimuth)) + { + dist_b1_ip = -dist_b1_ip; + } + + bool is_on_b = segment_ratio(dist_b1_ip, dist_b1_b2).on_segment(); + // NOTE: not fully consistent with equals_point_point() since radians are always used. + bool is_on_b1 = math::equals(lon, b1_lon) && math::equals(lat, b1_lat); + bool is_on_b2 = math::equals(lon, b2_lon) && math::equals(lat, b2_lat); + + if (! (is_on_b || is_on_b1 || is_on_b2)) + { + return false; + } + + ip_flag = ipi_inters; + + if (is_on_b1) + { + lon = b1_lon; + lat = b1_lat; + dist_b1_ip = 0; + ip_flag = ipi_at_b1; + } + else if (is_on_b2) + { + lon = b2_lon; + lat = b2_lat; + dist_b1_ip = res_b1_b2.distance; + ip_flag = ipi_at_b2; + } + + if (is_on_a1) + { + lon = a1_lon; + lat = a1_lat; + dist_a1_ip = 0; + ip_flag = ipi_at_a1; + } + else if (is_on_a2) + { + lon = a2_lon; + lat = a2_lat; + dist_a1_ip = res_a1_a2.distance; + ip_flag = ipi_at_a2; + } + + return true; + } + + template + static inline bool is_endpoint_equal(CalcT const& dist, + P1 const& ai, P2 const& b1, P2 const& b2) + { + using geometry::detail::equals::equals_point_point; + return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2)); + } + + template + static inline bool is_near(CalcT const& dist) + { + // NOTE: This strongly depends on the Inverse method + CalcT const small_number = CalcT(boost::is_same::value ? 0.0001 : 0.00000001); + return math::abs(dist) <= small_number; + } + + template + static inline int position_value(ProjCoord1 const& ca1, + ProjCoord2 const& cb1, + ProjCoord2 const& cb2) + { + // S1x 0 1 2 3 4 + // S2 |----------> + return math::equals(ca1, cb1) ? 1 + : math::equals(ca1, cb2) ? 3 + : cb1 < cb2 ? + ( ca1 < cb1 ? 0 + : ca1 > cb2 ? 4 + : 2 ) + : ( ca1 > cb1 ? 0 + : ca1 < cb2 ? 4 + : 2 ); + } + + template + static inline bool same_direction(CalcT const& azimuth1, CalcT const& azimuth2) + { + // distance between two angles normalized to (-180, 180] + CalcT const angle_diff = math::longitude_distance_signed(azimuth1, azimuth2); + return math::abs(angle_diff) <= math::half_pi(); + } + + template + static inline void sides_reverse_segment(side_info & sides) + { + // names assuming segment A is reversed (Which == 0) + int a1_wrt_b = sides.template get(); + int a2_wrt_b = sides.template get(); + std::swap(a1_wrt_b, a2_wrt_b); + sides.template set(a1_wrt_b, a2_wrt_b); + int b1_wrt_a = sides.template get<1 - Which, 0>(); + int b2_wrt_a = sides.template get<1 - Which, 1>(); + sides.template set<1 - Which>(-b1_wrt_a, -b2_wrt_a); + } + + static inline void ip_flag_reverse_segment(intersection_point_flag & ip_flag, + intersection_point_flag const& ipi_at_p1, + intersection_point_flag const& ipi_at_p2) + { + ip_flag = ip_flag == ipi_at_p1 ? ipi_at_p2 : + ip_flag == ipi_at_p2 ? ipi_at_p1 : + 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; +}; + + +}} // namespace strategy::intersection + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_GEODESIC_INTERSECTION_HPP diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp new file mode 100644 index 000000000..38f92985a --- /dev/null +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -0,0 +1,235 @@ +// Boost.Geometry + +// 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, +// 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_INTERSECTION_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_INTERSECTION_HPP + + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace intersection +{ + +template +struct relate_great_elliptic_segments_calc_policy + : relate_spherical_segments_calc_policy +{ + explicit relate_great_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) + {} + + template + Point from_cart3d(Point3d const& point_3d) const + { + return formula::cart3d_to_geo(point_3d, m_spheroid); + } + + template + Point3d to_cart3d(Point const& point) const + { + return formula::geo_to_cart3d(point, m_spheroid); + } + + // relate_xxx_calc_policy must live londer than plane because it contains + // Spheroid object and plane keeps the reference to that object. + template + struct plane + { + typedef typename coordinate_type::type coord_t; + + // not normalized + plane(Point3d const& p1, Point3d const& p2) + : normal(cross_product(p1, p2)) + {} + + int side_value(Point3d const& pt) const + { + return formula::sph_side_value(normal, pt); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const + { + Point3d v1 = p1; + detail::vec_normalize(v1); + Point3d v2 = p2; + detail::vec_normalize(v2); + + return dot_product(v1, v2); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const + { + coord_t const c0 = 0; + + Point3d v1 = p1; + detail::vec_normalize(v1); + Point3d v2 = p2; + detail::vec_normalize(v2); + + is_forward = dot_product(normal, cross_product(v1, v2)) >= c0; + return dot_product(v1, v2); + } + + Point3d normal; + }; + + template + plane get_plane(Point3d const& p1, Point3d const& p2) const + { + return plane(p1, p2); + } + + template + bool intersection_points(plane const& plane1, + plane const& plane2, + Point3d & ip1, Point3d & ip2) const + { + typedef typename coordinate_type::type coord_t; + + Point3d id = cross_product(plane1.normal, plane2.normal); + // NOTE: the length should be greater than 0 at this point + // NOTE: no need to normalize in this case + + ip1 = formula::projected_to_surface(id, m_spheroid); + + ip2 = ip1; + multiply_value(ip2, coord_t(-1)); + + return true; + } + +private: + Spheroid m_spheroid; +}; + +template +struct relate_experimental_elliptic_segments_calc_policy +{ + explicit relate_experimental_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) + {} + + template + Point from_cart3d(Point3d const& point_3d) const + { + return formula::cart3d_to_geo(point_3d, m_spheroid); + } + + template + Point3d to_cart3d(Point const& point) const + { + return formula::geo_to_cart3d(point, m_spheroid); + } + + // relate_xxx_calc_policy must live londer than plane because it contains + // Spheroid object and plane keeps the reference to that object. + template + struct plane + { + typedef typename coordinate_type::type coord_t; + + // not normalized + plane(Point3d const& p1, Point3d const& p2, Spheroid const& spheroid) + : m_spheroid(spheroid) + { + formula::experimental_elliptic_plane(p1, p2, origin, normal, m_spheroid); + } + + int side_value(Point3d const& pt) const + { + return formula::elliptic_side_value(origin, normal, pt); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const + { + Point3d const v1 = normalized_vec(p1); + Point3d const v2 = normalized_vec(p2); + return dot_product(v1, v2); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const + { + coord_t const c0 = 0; + + Point3d const v1 = normalized_vec(p1); + Point3d const v2 = normalized_vec(p2); + + is_forward = dot_product(normal, cross_product(v1, v2)) >= c0; + return dot_product(v1, v2); + } + + Point3d origin; + Point3d normal; + + private: + Point3d normalized_vec(Point3d const& p) const + { + Point3d v = p; + subtract_point(v, origin); + detail::vec_normalize(v); + return v; + } + + Spheroid const& m_spheroid; + }; + + template + plane get_plane(Point3d const& p1, Point3d const& p2) const + { + return plane(p1, p2, m_spheroid); + } + + template + bool intersection_points(plane const& plane1, + plane const& plane2, + Point3d & ip1, Point3d & ip2) const + { + return formula::planes_spheroid_intersection(plane1.origin, plane1.normal, + plane2.origin, plane2.normal, + ip1, ip2, m_spheroid); + } + +private: + Spheroid m_spheroid; +}; + + +template , typename CalculationType = void> +struct relate_great_elliptic_segments + : relate_ecef_segments + < + relate_great_elliptic_segments_calc_policy, + CalculationType + > +{}; + +template , typename CalculationType = void> +struct relate_experimental_elliptic_segments + : relate_ecef_segments + < + relate_experimental_elliptic_segments_calc_policy, + CalculationType + > +{}; + + +}} // namespace strategy::intersection + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_INTERSECTION_HPP diff --git a/include/boost/geometry/strategies/geographic/side_detail.hpp b/include/boost/geometry/strategies/geographic/side.hpp similarity index 58% rename from include/boost/geometry/strategies/geographic/side_detail.hpp rename to include/boost/geometry/strategies/geographic/side.hpp index ce1b47c88..1c4843efc 100644 --- a/include/boost/geometry/strategies/geographic/side_detail.hpp +++ b/include/boost/geometry/strategies/geographic/side.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2015, 2016. -// Modifications copyright (c) 2014-2016 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 @@ -11,14 +11,16 @@ // 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_SIDE_DETAIL_HPP -#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_DETAIL_HPP +#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_HPP #include #include #include #include +#include + #include #include #include @@ -34,9 +36,6 @@ namespace boost { namespace geometry namespace strategy { namespace side { -#ifndef DOXYGEN_NO_DETAIL -namespace detail -{ /*! \brief Check at which side of a segment a point lies @@ -49,10 +48,13 @@ namespace detail template class InverseFormula, typename Model, typename CalculationType = void> -class by_azimuth +class geographic { public: - by_azimuth(Model const& model = Model()) + geographic() + {} + + explicit geographic(Model const& model) : m_model(model) {} @@ -73,40 +75,7 @@ public: calc_t a1p = azimuth(p1, p, m_model); calc_t a12 = azimuth(p1, p2, m_model); - calc_t const pi = math::pi(); - - // instead of the formula from XTD - //calc_t a_diff = asin(sin(a1p - a12)); - - calc_t a_diff = a1p - a12; - // normalize, angle in [-pi, pi] - while ( a_diff > pi ) - a_diff -= calc_t(2) * pi; - while ( a_diff < -pi ) - a_diff += calc_t(2) * pi; - - // NOTE: in general it shouldn't be required to support the pi/-pi case - // because in non-cartesian systems it makes sense to check the side - // only "between" the endpoints. - // However currently the winding strategy calls the side strategy - // for vertical segments to check if the point is "between the endpoints. - // This could be avoided since the side strategy is not required for that - // because meridian is the shortest path. So a difference of - // longitudes would be sufficient (of course normalized to [-pi, pi]). - - // NOTE: with the above said, the pi/-pi check is temporary - // however in case if this was required - // the geodesics on ellipsoid aren't "symmetrical" - // therefore instead of comparing a_diff to pi and -pi - // one should probably use inverse azimuths and compare - // the difference to 0 as well - - // positive azimuth is on the right side - return math::equals(a_diff, 0) - || math::equals(a_diff, pi) - || math::equals(a_diff, -pi) ? 0 - : a_diff > 0 ? -1 // right - : 1; // left + return formula::azimuth_side_value(a1p, a12); } private: @@ -127,8 +96,6 @@ private: Model m_model; }; -} // detail -#endif // DOXYGEN_NO_DETAIL }} // namespace strategy::side @@ -136,4 +103,4 @@ private: }} // namespace boost::geometry -#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_DETAIL_HPP +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_HPP diff --git a/include/boost/geometry/strategies/geographic/side_andoyer.hpp b/include/boost/geometry/strategies/geographic/side_andoyer.hpp index c3e71cd1c..127ba484c 100644 --- a/include/boost/geometry/strategies/geographic/side_andoyer.hpp +++ b/include/boost/geometry/strategies/geographic/side_andoyer.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2015, 2016. -// Modifications copyright (c) 2014-2016 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 @@ -17,7 +17,7 @@ #include -#include +#include namespace boost { namespace geometry @@ -36,12 +36,15 @@ namespace strategy { namespace side */ template class andoyer - : public detail::by_azimuth + : public side::geographic { - typedef detail::by_azimuth base_t; + typedef side::geographic base_t; public: - andoyer(Model const& model = Model()) + andoyer() + {} + + explicit andoyer(Model const& model) : base_t(model) {} }; diff --git a/include/boost/geometry/strategies/geographic/side_thomas.hpp b/include/boost/geometry/strategies/geographic/side_thomas.hpp index 96b032330..79f0bc988 100644 --- a/include/boost/geometry/strategies/geographic/side_thomas.hpp +++ b/include/boost/geometry/strategies/geographic/side_thomas.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2015, 2016. -// Modifications copyright (c) 2014-2016 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 @@ -17,7 +17,7 @@ #include -#include +#include namespace boost { namespace geometry @@ -36,12 +36,15 @@ namespace strategy { namespace side */ template class thomas - : public detail::by_azimuth + : public side::geographic { - typedef detail::by_azimuth base_t; + typedef side::geographic base_t; public: - thomas(Model const& model = Model()) + thomas() + {} + + explicit thomas(Model const& model) : base_t(model) {} }; diff --git a/include/boost/geometry/strategies/geographic/side_vincenty.hpp b/include/boost/geometry/strategies/geographic/side_vincenty.hpp index 103277a8b..02c9ca4e0 100644 --- a/include/boost/geometry/strategies/geographic/side_vincenty.hpp +++ b/include/boost/geometry/strategies/geographic/side_vincenty.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2015, 2016. -// Modifications copyright (c) 2014-2016 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 @@ -17,7 +17,7 @@ #include -#include +#include namespace boost { namespace geometry @@ -36,12 +36,15 @@ namespace strategy { namespace side */ template class vincenty - : public detail::by_azimuth + : public side::geographic { - typedef detail::by_azimuth base_t; + typedef side::geographic base_t; public: - vincenty(Model const& model = Model()) + vincenty() + {} + + explicit vincenty(Model const& model) : base_t(model) {} }; diff --git a/include/boost/geometry/strategies/spherical/area_spherical.hpp b/include/boost/geometry/strategies/spherical/area_spherical.hpp index 920e7a460..c03d70f64 100644 --- a/include/boost/geometry/strategies/spherical/area_spherical.hpp +++ b/include/boost/geometry/strategies/spherical/area_spherical.hpp @@ -1,7 +1,8 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2016 Oracle and/or its affiliates. +// Copyright (c) 2016-2017 Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -10,9 +11,12 @@ #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_SPHERICAL_HPP #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_SPHERICAL_HPP + #include #include #include +#include + namespace boost { namespace geometry { @@ -21,10 +25,17 @@ namespace strategy { namespace area { /*! -\brief Spherical area calculation by trapezoidal rule +\brief Spherical area calculation +\ingroup strategies +\details Calculates area on the surface of a sphere using the trapezoidal rule +\tparam PointOfSegment \tparam_segment_point +\tparam LongSegment Enables special handling of long segments +\tparam CalculationType \tparam_calculation +\qbk{ +[heading See also] +[link geometry.reference.algorithms.area.area_2_with_strategy area (with strategy)] } - */ template < @@ -102,7 +113,7 @@ public : inline spherical(CT radius) //backward compatibility : m_sphere() { - m_sphere.set_radius<0>(radius); + geometry::set_radius<0>(m_sphere, radius); } inline void apply(PointOfSegment const& p1, diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index b8b8bf93e..0f25ffbdd 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -73,8 +76,8 @@ namespace strategy { namespace intersection // For now, intersection points near the endpoints are checked explicitly if needed (if the IP is near the endpoint) // to generate precise result for them. Only the crossing (i) case may suffer from lower precision. -template -struct relate_spherical_segments +template +struct relate_ecef_segments { typedef side::spherical_side_formula side_strategy_type; @@ -106,8 +109,44 @@ struct relate_spherical_segments return strategy_type(); } + template + struct area_strategy + { + typedef area::spherical + < + typename point_type::type, + false, + CalculationType + > type; + }; + + template + static inline typename area_strategy::type get_area_strategy() + { + typedef typename area_strategy::type strategy_type; + return strategy_type(); + } + + template + struct distance_strategy + { + typedef distance::haversine + < + typename coordinate_type::type, + CalculationType + > type; + }; + + template + static inline typename distance_strategy::type get_distance_strategy() + { + typedef typename distance_strategy::type strategy_type; + return strategy_type(); + } + enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 }; + // segment_intersection_info cannot outlive relate_ecef_segments template struct segment_intersection_info { @@ -116,6 +155,10 @@ struct relate_spherical_segments CoordinateType, double >::type promoted_type; + segment_intersection_info(CalcPolicy const& calc) + : calc_policy(calc) + {} + promoted_type comparable_length_a() const { return robust_ra.denominator(); @@ -143,7 +186,7 @@ struct relate_spherical_segments if (ip_flag == ipi_inters) { // TODO: assign the rest of coordinates - point = formula::cart3d_to_sph(intersection_point); + point = calc_policy.template from_cart3d(intersection_point); } else if (ip_flag == ipi_at_a1) { @@ -167,6 +210,8 @@ struct relate_spherical_segments SegmentRatio robust_ra; SegmentRatio robust_rb; intersection_point_flag ip_flag; + + CalcPolicy const& calc_policy; }; // Relate segments a and b @@ -210,6 +255,12 @@ struct relate_spherical_segments Policy const&, RobustPolicy const&, Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2) { + // For now create it using default constructor. In the future it could + // be stored in strategy. However then apply() wouldn't be static and + // all relops and setops would have to take the strategy or model. + // Initialize explicitly to prevent compiler errors in case of PoD type + CalcPolicy const calc_policy = CalcPolicy(); + BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); @@ -234,25 +285,29 @@ struct relate_spherical_segments typedef model::point vec3d_t; - using namespace formula; - vec3d_t const a1v = sph_to_cart3d(a1); - vec3d_t const a2v = sph_to_cart3d(a2); - vec3d_t const b1v = sph_to_cart3d(b1); - vec3d_t const b2v = sph_to_cart3d(b2); + vec3d_t const a1v = calc_policy.template to_cart3d(a1); + vec3d_t const a2v = calc_policy.template to_cart3d(a2); + vec3d_t const b1v = calc_policy.template to_cart3d(b1); + vec3d_t const b2v = calc_policy.template to_cart3d(b2); - vec3d_t norm1 = cross_product(a1v, a2v); - vec3d_t norm2 = cross_product(b1v, b2v); - side_info sides; - // not normalized normals, the same as in SSF - sides.set<0>(sph_side_value(norm2, a1v), sph_side_value(norm2, a2v)); + + 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>()) { // Both points are at same side of other segment, we can leave return Policy::disjoint(); } - // not normalized normals, the same as in SSF - sides.set<1>(sph_side_value(norm1, b1v), sph_side_value(norm1, b2v)); + + 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>()) { // Both points are at same side of other segment, we can leave @@ -261,13 +316,10 @@ struct relate_spherical_segments // NOTE: at this point the segments may still be disjoint - bool collinear = sides.collinear(); + calc_t len1, len2; - calc_t const len1 = math::sqrt(dot_product(norm1, norm1)); - calc_t const len2 = math::sqrt(dot_product(norm2, norm2)); - - // point or opposite sides of a sphere, assume point - if (math::equals(len1, c0)) + // point or opposite sides of a sphere/spheroid, assume point + if (! detail::vec_normalize(plane1.normal, len1)) { a_is_point = true; if (sides.get<0, 0>() == 0 || sides.get<0, 1>() == 0) @@ -275,13 +327,8 @@ struct relate_spherical_segments sides.set<0>(0, 0); } } - else - { - // normalize - divide_value(norm1, len1); - } - if (math::equals(len2, c0)) + if (! detail::vec_normalize(plane2.normal, len2)) { b_is_point = true; if (sides.get<1, 0>() == 0 || sides.get<1, 1>() == 0) @@ -289,11 +336,6 @@ struct relate_spherical_segments sides.set<1>(0, 0); } } - else - { - // normalize - divide_value(norm2, len2); - } // check both degenerated once more if (a_is_point && b_is_point) @@ -304,16 +346,42 @@ struct relate_spherical_segments ; } + // NOTE: at this point the segments may still be disjoint // NOTE: at this point one of the segments may be degenerated - // and the segments may still be disjoint - calc_t dot_n1n2 = dot_product(norm1, norm2); + bool collinear = sides.collinear(); + + if (! collinear) + { + // NOTE: for some approximations it's possible that both points may lie + // on the same geodesic but still some of the sides may be != 0. + // This is e.g. true for long segments represented as elliptic arcs + // with origin different than the center of the coordinate system. + // So make the sides consistent + + // WARNING: the side strategy doesn't have the info about the other + // segment so it may return results inconsistent with this intersection + // strategy, as it checks both segments for consistency + + if (sides.get<0, 0>() == 0 && sides.get<0, 1>() == 0) + { + collinear = true; + sides.set<1>(0, 0); + } + else if (sides.get<1, 0>() == 0 && sides.get<1, 1>() == 0) + { + collinear = true; + sides.set<0>(0, 0); + } + } + + calc_t dot_n1n2 = dot_product(plane1.normal, plane2.normal); // NOTE: this is technically not needed since theoretically above sides // are calculated, but just in case check the normals. // Have in mind that SSF side strategy doesn't check this. // collinear if normals are equal or opposite: cos(a) in {-1, 1} - if (!collinear && math::equals(math::abs(dot_n1n2), c1)) + if (! collinear && math::equals(math::abs(dot_n1n2), c1)) { collinear = true; sides.set<0>(0, 0); @@ -324,12 +392,12 @@ struct relate_spherical_segments { if (a_is_point) { - return collinear_one_degenerted(a, true, b1, b2, a1, a2, b1v, b2v, norm2, a1v); + return collinear_one_degenerated(a, true, b1, b2, a1, a2, b1v, b2v, plane2, a1v); } else if (b_is_point) { // b2 used to be consistent with (degenerated) checks above (is it needed?) - return collinear_one_degenerted(b, false, a1, a2, b1, b2, a1v, a2v, norm1, b1v); + return collinear_one_degenerated(b, false, a1, a2, b1, b2, a1v, a2v, plane1, b1v); } else { @@ -338,16 +406,16 @@ struct relate_spherical_segments // use shorter segment if (len1 <= len2) { - calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, norm1, b1v, dist_a1_a2, dist_a1_b1); - calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, norm1, b2v, dist_a1_a2, dist_a1_b2); + 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); 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, norm2, a1v, dist_b1_b2, dist_b1_a1); - calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, norm2, a2v, dist_b1_b2, dist_b1_a2); + 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); dist_a1_a2 = dist_b1_a2 - dist_b1_a1; dist_a1_b1 = -dist_b1_a1; dist_a1_b2 = dist_b1_b2 - dist_b1_a1; @@ -408,7 +476,8 @@ struct relate_spherical_segments vec3d_t i1; intersection_point_flag ip_flag; calc_t dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1; - if (calculate_ip_data(a1, a2, b1, b2, a1v, a2v, b1v, b2v, norm1, norm2, sides, + 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)) { // intersects @@ -417,7 +486,7 @@ struct relate_spherical_segments calc_t, segment_ratio, vec3d_t - > sinfo; + > sinfo(calc_policy); sinfo.robust_ra.assign(dist_a1_i1, dist_a1_a2); sinfo.robust_rb.assign(dist_b1_i1, dist_b1_b2); @@ -434,31 +503,32 @@ struct relate_spherical_segments } private: - template + template static inline typename Policy::return_type - collinear_one_degenerted(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& norm, - Vec3d const& vother) + 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, + Plane const& plane, + Vec3d const& vother) { CalcT dist_1_2, dist_1_o; - return ! calculate_collinear_data(a1, a2, b1, b2, v1, v2, norm, vother, dist_1_2, dist_1_o) + return ! calculate_collinear_data(a1, a2, b1, b2, v1, v2, plane, vother, dist_1_2, dist_1_o) ? Policy::disjoint() : Policy::one_degenerate(segment, segment_ratio(dist_1_o, dist_1_2), degenerated_a); } - template - static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, - Point2 const& b1, Point2 const& b2, - Vec3d const& a1v, // in - Vec3d const& a2v, // in - Vec3d const& norm1, // in - Vec3d const& b1v_or_b2v, // in + template + static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, // in + Point2 const& b1, Point2 const& b2, // in + 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 { // calculate dist_a1_a2 and dist_a1_i1 - calculate_dists(a1v, a2v, norm1, b1v_or_b2v, dist_a1_a2, dist_a1_i1); + calculate_dists(a1v, a2v, plane1, b1v_or_b2v, dist_a1_a2, 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)) @@ -477,54 +547,53 @@ private: return segment_ratio(dist_a1_i1, dist_a1_a2).on_segment(); } - template + template static inline bool calculate_ip_data(Point1 const& a1, Point1 const& a2, // in Point2 const& b1, Point2 const& b2, // in Vec3d const& a1v, Vec3d const& a2v, // in Vec3d const& b1v, Vec3d const& b2v, // in - Vec3d const& norm1, Vec3d const& norm2, // in - side_info const& sides, // in - Vec3d & i1, // in-out - CalcT& dist_a1_a2, CalcT& dist_a1_i1, // out - CalcT& dist_b1_b2, CalcT& dist_b1_i1, // out + Plane const& plane1, // in + 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 { - // great circles intersections - i1 = cross_product(norm1, norm2); - // NOTE: the length should be greater than 0 at this point - // if the normals were not normalized and their dot product - // not checked before this function is called the length - // should be checked here (math::equals(len, c0)) - CalcT const len = math::sqrt(dot_product(i1, i1)); - divide_value(i1, len); // normalize i1 - - calculate_dists(a1v, a2v, norm1, i1, dist_a1_a2, dist_a1_i1); + Vec3d ip1, ip2; + calc_policy.intersection_points(plane1, plane2, ip1, ip2); + calculate_dists(a1v, a2v, plane1, ip1, dist_a1_a2, dist_a1_ip); + ip = ip1; + // choose the opposite side of the globe if the distance is shorter { - CalcT const d = abs_distance(dist_a1_a2, dist_a1_i1); + CalcT const d = abs_distance(dist_a1_a2, dist_a1_ip); if (d > CalcT(0)) { - CalcT const dist_a1_i2 = dist_of_i2(dist_a1_i1); + // TODO: this should be ok not only for sphere + // but requires more investigation + CalcT const dist_a1_i2 = dist_of_i2(dist_a1_ip); CalcT const d2 = abs_distance(dist_a1_a2, dist_a1_i2); if (d2 < d) { - dist_a1_i1 = dist_a1_i2; - multiply_value(i1, CalcT(-1)); // the opposite intersection + dist_a1_ip = dist_a1_i2; + ip = ip2; } } } bool is_on_a = false, is_near_a1 = false, is_near_a2 = false; - if (! is_potentially_crossing(dist_a1_a2, dist_a1_i1, is_on_a, is_near_a1, is_near_a2)) + if (! is_potentially_crossing(dist_a1_a2, dist_a1_ip, is_on_a, is_near_a1, is_near_a2)) { return false; } - calculate_dists(b1v, b2v, norm2, i1, dist_b1_b2, dist_b1_i1); + calculate_dists(b1v, b2v, plane2, ip, dist_b1_b2, 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_i1, is_on_b, is_near_b1, is_near_b2)) + if (! is_potentially_crossing(dist_b1_b2, dist_b1_ip, is_on_b, is_near_b1, is_near_b2)) { return false; } @@ -535,8 +604,8 @@ private: { if (is_near_b1 && equals_point_point(a1, b1)) { - dist_a1_i1 = 0; - dist_b1_i1 = 0; + dist_a1_ip = 0; + dist_b1_ip = 0; //i1 = a1v; ip_flag = ipi_at_a1; return true; @@ -544,8 +613,8 @@ private: if (is_near_b2 && equals_point_point(a1, b2)) { - dist_a1_i1 = 0; - dist_b1_i1 = dist_b1_b2; + dist_a1_ip = 0; + dist_b1_ip = dist_b1_b2; //i1 = a1v; ip_flag = ipi_at_a1; return true; @@ -556,8 +625,8 @@ private: { if (is_near_b1 && equals_point_point(a2, b1)) { - dist_a1_i1 = dist_a1_a2; - dist_b1_i1 = 0; + dist_a1_ip = dist_a1_a2; + dist_b1_ip = 0; //i1 = a2v; ip_flag = ipi_at_a2; return true; @@ -565,8 +634,8 @@ private: if (is_near_b2 && equals_point_point(a2, b2)) { - dist_a1_i1 = dist_a1_a2; - dist_b1_i1 = dist_b1_b2; + dist_a1_ip = dist_a1_a2; + dist_b1_ip = dist_b1_b2; //i1 = a2v; ip_flag = ipi_at_a2; return true; @@ -580,7 +649,7 @@ private: { if (is_near_b1 && sides.template get<1, 0>() == 0) // b1 wrt a { - dist_b1_i1 = 0; + dist_b1_ip = 0; //i1 = b1v; ip_flag = ipi_at_b1; return true; @@ -588,7 +657,7 @@ private: if (is_near_b2 && sides.template get<1, 1>() == 0) // b2 wrt a { - dist_b1_i1 = dist_b1_b2; + dist_b1_ip = dist_b1_b2; //i1 = b2v; ip_flag = ipi_at_b2; return true; @@ -599,7 +668,7 @@ private: { if (is_near_a1 && sides.template get<0, 0>() == 0) // a1 wrt b { - dist_a1_i1 = 0; + dist_a1_ip = 0; //i1 = a1v; ip_flag = ipi_at_a1; return true; @@ -607,7 +676,7 @@ private: if (is_near_a2 && sides.template get<0, 1>() == 0) // a2 wrt b { - dist_a1_i1 = dist_a1_a2; + dist_a1_ip = dist_a1_a2; //i1 = a2v; ip_flag = ipi_at_a2; return true; @@ -619,24 +688,26 @@ private: return is_on_a && is_on_b; } - template - static inline void calculate_dists(Vec3d const& a1v, // in - Vec3d const& a2v, // in - Vec3d const& norm1, // in - Vec3d const& i1, // in - CalcT& dist_a1_a2, CalcT& dist_a1_i1) // out + 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 { - CalcT const c0 = 0; + //CalcT const c0 = 0; CalcT const c1 = 1; CalcT const c2 = 2; CalcT const c4 = 4; - CalcT cos_a1_a2 = dot_product(a1v, a2v); + CalcT cos_a1_a2 = plane1.cos_angle_between(a1v, a2v); dist_a1_a2 = -cos_a1_a2 + c1; // [1, -1] -> [0, 2] representing [0, pi] - CalcT cos_a1_i1 = dot_product(a1v, i1); + bool is_forward = true; + CalcT cos_a1_i1 = plane1.cos_angle_between(a1v, i1, is_forward); dist_a1_i1 = -cos_a1_i1 + c1; // [0, 2] representing [0, pi] - if (dot_product(norm1, cross_product(a1v, i1)) < c0) // left or right of a1 on a + if (! is_forward) // left or right of a1 on a { dist_a1_i1 = -dist_a1_i1; // [0, 2] -> [0, -2] representing [0, -pi] } @@ -716,6 +787,88 @@ private: } }; +struct relate_spherical_segments_calc_policy +{ + template + static Point from_cart3d(Point3d const& point_3d) + { + return formula::cart3d_to_sph(point_3d); + } + + template + static Point3d to_cart3d(Point const& point) + { + return formula::sph_to_cart3d(point); + } + + template + struct plane + { + typedef typename coordinate_type::type coord_t; + + // not normalized + plane(Point3d const& p1, Point3d const& p2) + : normal(cross_product(p1, p2)) + {} + + int side_value(Point3d const& pt) const + { + return formula::sph_side_value(normal, pt); + } + + static coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) + { + return dot_product(p1, p2); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const + { + coord_t const c0 = 0; + is_forward = dot_product(normal, cross_product(p1, p2)) >= c0; + return dot_product(p1, p2); + } + + Point3d normal; + }; + + template + static plane get_plane(Point3d const& p1, Point3d const& p2) + { + return plane(p1, p2); + } + + template + static bool intersection_points(plane const& plane1, + plane const& plane2, + Point3d & ip1, Point3d & ip2) + { + typedef typename coordinate_type::type coord_t; + + ip1 = cross_product(plane1.normal, plane2.normal); + // NOTE: the length should be greater than 0 at this point + // if the normals were not normalized and their dot product + // not checked before this function is called the length + // should be checked here (math::equals(len, c0)) + coord_t const len = math::sqrt(dot_product(ip1, ip1)); + divide_value(ip1, len); // normalize i1 + + ip2 = ip1; + multiply_value(ip2, coord_t(-1)); + + return true; + } +}; + + +template +struct relate_spherical_segments + : relate_ecef_segments + < + relate_spherical_segments_calc_policy, + CalculationType + > +{}; + #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS namespace services @@ -736,6 +889,11 @@ struct default_strategy template struct default_strategy { + // NOTE: Spherical strategy returns the same result as the geographic one + // representing segments as great elliptic arcs. If the elliptic arcs are + // not great elliptic arcs (the origin not in the center of the coordinate + // system) then there may be problems with consistency of the side and + // intersection strategies. typedef relate_spherical_segments type; }; diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index a53c328dd..30b15075e 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -80,10 +80,14 @@ #include #include #include +#include #include #include #include #include +//#include +//#include +//#include //#include //#include //#include diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp index 1874fe76a..b1c3648c9 100644 --- a/include/boost/geometry/util/math.hpp +++ b/include/boost/geometry/util/math.hpp @@ -71,6 +71,19 @@ inline T const& greatest(T const& v1, T const& v2, T const& v3, T const& v4, T c } +template +inline T bounded(T const& v, T const& lower, T const& upper) +{ + return (std::min)((std::max)(v, lower), upper); +} + +template +inline T bounded(T const& v, T const& lower) +{ + return (std::max)(v, lower); +} + + template ::value> struct abs diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp index 377051eef..19d4d33d2 100644 --- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp +++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp @@ -282,6 +282,40 @@ inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitud return diff; } +/*! +\brief The abs difference between longitudes in range [0, 180]. +\tparam Units The units of the coordindate system in the spheroid +\tparam CoordinateType The type of the coordinates +\param longitude1 Longitude 1 +\param longitude2 Longitude 2 +\ingroup utility +*/ +template +inline CoordinateType longitude_difference(CoordinateType const& longitude1, + CoordinateType const& longitude2) +{ + return math::abs(math::longitude_distance_signed(longitude1, longitude2)); +} + +template +inline CoordinateType longitude_interval_distance_signed(CoordinateType const& longitude_a1, + CoordinateType const& longitude_a2, + CoordinateType const& longitude_b) +{ + CoordinateType const c0 = 0; + CoordinateType dist_a12 = longitude_distance_signed(longitude_a1, longitude_a2); + CoordinateType dist_a1b = longitude_distance_signed(longitude_a1, longitude_b); + if (dist_a12 < c0) + { + dist_a12 = -dist_a12; + dist_a1b = -dist_a1b; + } + + return dist_a1b < c0 ? dist_a1b + : dist_a1b > dist_a12 ? dist_a1b - dist_a12 + : c0; +} + } // namespace math diff --git a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp index 2b736af00..01deaffba 100644 --- a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp +++ b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp @@ -50,6 +50,7 @@ #include "test_envelope_expand_on_spheroid.hpp" + template < template class Inverse, typename CS_Tag @@ -152,11 +153,54 @@ private: BOOST_CHECK_MESSAGE(same_boxes, stream.str()); } - template + template + < + typename Geometry, typename Box, + typename T1, typename T2, typename T3, typename T4 + > + static inline void check_message(bool same_boxes, + std::string const& case_id, + std::string const& units_str, + Geometry const& geometry, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, + Box const& detected) + { + std::ostringstream stream; + stream << "case ID: " << case_id << ", " + << "MBR units: " << units_str << "; " + << "geometry: "; + + write_geometry::apply(stream, geometry); + + stream << std::setprecision(17); + + stream << "; " << "expected: "; + + if (BOOST_GEOMETRY_CONDITION(bg::dimension::value == 2)) + { + stream << "(" << lon_min << " " << lat_min + << ", " << lon_max << " " << lat_max << ")"; + } + else + { + stream << "(" << lon_min << " " << lat_min << " " << height_min + << ", " << lon_max << " " << lat_max << " " << height_max << ")"; + } + stream << ", " << "detected: " << bg::dsv(detected); + + BOOST_CHECK_MESSAGE(same_boxes, stream.str()); + } + + template + < + typename Box, typename Geometry, + typename T1, typename T2, typename T3, typename T4 + > static inline void base_test(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, double height_min, - double lon_max, double lat_max, double height_max, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, double tolerance) { typedef typename bg::coordinate_system::type::units box_units_type; @@ -168,27 +212,40 @@ private: typename bg::cs_tag::type >::apply(geometry, detected); - Box expected; - initialize_box::apply(expected, - lon_min, lat_min, height_min, - lon_max, lat_max, height_max); - #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "geometry: "; write_geometry::apply(std::cout, geometry); std::cout << std::endl << "MBR units: " << units_str - << std::endl - << "expected: " << bg::dsv(expected) - << std::endl + << std::endl; + std::cout << "expected: "; + if (BOOST_GEOMETRY_CONDITION(bg::dimension::value == 2)) + { + std::cout << "(" << lon_min << " " << lat_min + << ", " << lon_max << " " << lat_max << ")"; + } + else + { + std::cout << "(" << lon_min << " " << lat_min << " " << height_min + << ", " << lon_max << " " << lat_max << " " << height_max << ")"; + } + std::cout << std::endl << "detected: " << bg::dsv(detected) << std::endl << std::endl; #endif - check_message(box_equals::apply(detected, expected, tolerance), + bool check = box_check_equals::apply(detected, + lon_min, lat_min, height_min, + lon_max, lat_max, height_max, + tolerance); + + check_message(check, case_id, units_str, - geometry, expected, detected); + geometry, + lon_min, lat_min, height_min, + lon_max, lat_max, height_max, + detected); // if valid box is expected, check the validity if (lon_min <= lon_max && lat_min <= lat_max && height_min <= height_max) @@ -201,11 +258,15 @@ private: } public: - template + template + < + typename Geometry, + typename T1, typename T2, typename T3, typename T4 + > static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, double height_min, - double lon_max, double lat_max, double height_max, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, double tolerance) { typedef other_system_info @@ -291,12 +352,14 @@ template > struct test_envelope_on_sphere_or_spheroid { + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min1, double lat_min1, double height_min1, - double lon_max1, double lat_max1, double height_max1, - double lon_min2, double lat_min2, double height_min2, - double lon_max2, double lat_max2, double height_max2, + T1 const& lon_min1, T2 const& lat_min1, double height_min1, + T3 const& lon_max1, T4 const& lat_max1, double height_max1, + T5 const& lon_min2, T6 const& lat_min2, double height_min2, + T7 const& lon_max2, T8 const& lat_max2, double height_max2, double tolerance = std::numeric_limits::epsilon()) { envelope_on_spheroid_basic_tester @@ -328,12 +391,14 @@ struct test_envelope_on_sphere_or_spheroid #endif } + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min1, double lat_min1, - double lon_max1, double lat_max1, - double lon_min2, double lat_min2, - double lon_max2, double lat_max2, + T1 const& lon_min1, T2 const& lat_min1, + T3 const& lon_max1, T4 const& lat_max1, + T5 const& lon_min2, T6 const& lat_min2, + T7 const& lon_max2, T8 const& lat_max2, double tolerance = std::numeric_limits::epsilon()) { apply(case_id, geometry, @@ -342,10 +407,11 @@ struct test_envelope_on_sphere_or_spheroid tolerance); } + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, double height_min, - double lon_max, double lat_max, double height_max, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, double tolerance = std::numeric_limits::epsilon()) { apply(case_id, geometry, @@ -356,10 +422,11 @@ struct test_envelope_on_sphere_or_spheroid tolerance); } + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, - double lon_max, double lat_max, + T1 const& lon_min, T2 const& lat_min, + T3 const& lon_max, T4 const& lat_max, double tolerance = std::numeric_limits::epsilon()) { apply(case_id, geometry, @@ -373,13 +440,15 @@ struct test_envelope_on_sphere_or_spheroid template struct test_envelope_on_sphere_or_spheroid { + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min1, double lat_min1, - double lon_max1, double lat_max1, - double lon_min2, double lat_min2, - double lon_max2, double lat_max2, - double tolerance = std::numeric_limits::epsilon()) + T1 const& lon_min1, T2 const& lat_min1, + T3 const& lon_max1, T4 const& lat_max1, + T5 const& lon_min2, T6 const& lat_min2, + T7 const& lon_max2, T8 const& lat_max2, + double const& tolerance = std::numeric_limits::epsilon()) { envelope_on_spheroid_basic_tester < @@ -409,10 +478,11 @@ struct test_envelope_on_sphere_or_spheroid static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, - double lon_max, double lat_max, + T1 const& lon_min, T2 const& lat_min, + T3 const& lon_max, T4 const& lat_max, double tolerance = std::numeric_limits::epsilon()) { apply(case_id, geometry, @@ -822,26 +892,24 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid ) tester::apply("s09", from_wkt("SEGMENT(1 -45,179 30)"), - 1, -85.392785243526134, 179, 30, - 3 * eps); + 1, rng(-85.392785243526134, -85.392785243525253), 179, 30); tester::apply("s09a", from_wkt("SEGMENT(2 -45,181 30)"), - 2, -87.689300911353811, 181, 30); + 2, rng(-87.689300911353811, -87.689300911353371), 181, 30); // very long segment tester::apply("s10", from_wkt("SEGMENT(0 -45,181 30)"), - -179, -87.689300911353797, 0, 30, - 2.0 * eps); + -179, rng(-87.689300911353797, -87.689300911353385), 0, 30); tester::apply("s11", from_wkt("SEGMENT(260 30,20 45)"), - -100, 30, 20, 57.990810958016965); + -100, 30, 20, rng(57.990810958016482, 57.990810958016965)); tester::apply("s11a", from_wkt("SEGMENT(260 45,20 30)"), - -100, 30, 20, 57.990810958016965); + -100, 30, 20, rng(57.990810958016453, 57.990810958016965)); // segment degenerating to the north pole tester::apply("s12", @@ -859,7 +927,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid ) tester::apply("s15", from_wkt("SEGMENT(50 45,185 45)"), - 50, 45, 185, 69.098479073903178); + 50, 45, 185, rng(69.098479073902851, 69.098479073903178)); // segment that lies on the equator tester::apply("s16", @@ -924,7 +992,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid ) 1-heps, 1, 1, 1); tester::apply("s104", G(P(2, 1), P(1, 1-heps)), - 1, 1-heps, 2, 1.0000383271569036); + 1, 1-heps, 2, rng(1.0000383271568751, 1.0000383271569036)); tester::apply("s105", G(P(1, 2), P(1-heps, 1)), 1-heps, 1, 1, 2); @@ -940,13 +1008,11 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_thomas ) typedef test_envelope_on_sphere_or_spheroid < G, B, - typename bg::tag::type, + bg::tag::type, test_reverse_geometry::value, bg::formula::thomas_inverse > tester; - double const eps = std::numeric_limits::epsilon(); - tester::apply("s01", from_wkt("SEGMENT(10 10,40 40)"), 10, 10, 40, 40); @@ -1011,26 +1077,24 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_thomas ) tester::apply("s09", from_wkt("SEGMENT(1 -45,179 30)"), - 1, -85.392785243526134, 179, 30, - 3 * eps); + 1, rng(-85.392785243526134, -85.392785243525253), 179, 30); tester::apply("s09a", from_wkt("SEGMENT(2 -45,181 30)"), - 2, -87.689300911353811, 181, 30); + 2, rng(-87.689300911353811, -87.689300911353371), 181, 30); // very long segment tester::apply("s10", from_wkt("SEGMENT(0 -45,181 30)"), - -179, -87.689300911353797, 0, 30, - 2.0 * eps); + -179, rng(-87.689300911353797, -87.689300911353385), 0, 30); tester::apply("s11", from_wkt("SEGMENT(260 30,20 45)"), - -100, 30, 20, 57.990810958016965); + -100, 30, 20, rng(57.990810958016482, 57.990810958016965)); tester::apply("s11a", from_wkt("SEGMENT(260 45,20 30)"), - -100, 30, 20, 57.990810958016965); + -100, 30, 20, rng(57.990810958016453, 57.990810958016965)); // segment degenerating to the north pole tester::apply("s12", @@ -1048,7 +1112,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_thomas ) tester::apply("s15", from_wkt("SEGMENT(50 45,185 45)"), - 50, 45, 185, 69.098479073903178); + 50, 45, 185, rng(69.098479073902851, 69.098479073903178)); // segment that lies on the equator tester::apply("s16", @@ -1098,13 +1162,11 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_andoyer ) typedef test_envelope_on_sphere_or_spheroid < G, B, - typename bg::tag::type, + bg::tag::type, test_reverse_geometry::value, bg::formula::andoyer_inverse > tester; - double const eps = std::numeric_limits::epsilon(); - tester::apply("s01", from_wkt("SEGMENT(10 10,40 40)"), 10, 10, 40, 40); @@ -1169,26 +1231,24 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_andoyer ) tester::apply("s09", from_wkt("SEGMENT(1 -45,179 30)"), - 1, -85.394745211091248, 179, 30, - 3 * eps); + 1, rng(-85.394745211091248, -85.394745211090353), 179, 30); tester::apply("s09a", from_wkt("SEGMENT(2 -45,181 30)"), - 2, -87.690317839849726, 181, 30); + 2, rng(-87.690317839849726, -87.690317839849271), 181, 30); // very long segment tester::apply("s10", from_wkt("SEGMENT(0 -45,181 30)"), - -179, -87.69031783984974, 0, 30, - 2.0 * eps); + -179, rng(-87.69031783984974, -87.690317839849271), 0, 30); tester::apply("s11", from_wkt("SEGMENT(260 30,20 45)"), - -100, 30, 20, 57.990742552280153); + -100, 30, 20, rng(57.990742552279649, 57.990742552280153)); tester::apply("s11a", from_wkt("SEGMENT(260 45,20 30)"), - -100, 30, 20, 57.990742552280118); + -100, 30, 20, rng(57.99074255227962, 57.990742552280118)); // segment degenerating to the north pole tester::apply("s12", @@ -1206,7 +1266,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_andoyer ) tester::apply("s15", from_wkt("SEGMENT(50 45,185 45)"), - 50, 45, 185, 69.09844689340845); + 50, 45, 185, rng(69.098446893408124, 69.09844689340845)); // segment that lies on the equator tester::apply("s16", @@ -1256,36 +1316,34 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_vincenty ) typedef test_envelope_on_sphere_or_spheroid < G, B, - typename bg::tag::type, + bg::tag::type, test_reverse_geometry::value, bg::formula::vincenty_inverse > tester; - double const eps = std::numeric_limits::epsilon(); - tester::apply("s01", from_wkt("SEGMENT(10 10,40 40)"), 10, 10, 40, 40); tester::apply("s02", from_wkt("SEGMENT(10 10,40 10)"), - 10, 10, 40, 10.347587628821941); + 10, 10, 40, rng(10.347587628821937, 10.347587628821941)); tester::apply("s02a", from_wkt("SEGMENT(40 10,10 10)"), - 10, 10, 40, 10.347587628821941); + 10, 10, 40, rng(10.347587628821937, 10.347587628821941)); tester::apply("s03", from_wkt("SEGMENT(160 10,-170 10)"), - 160, 10, 190, 10.347587628821941); + 160, 10, 190, rng(10.347587628821937, 10.347587628821941)); tester::apply("s03a", from_wkt("SEGMENT(-170 10,160 10)"), - 160, 10, 190, 10.347587628821941); + 160, 10, 190, rng(10.347587628821937, 10.347587628821941)); tester::apply("s03b", from_wkt("SEGMENT(-170 -10,160 -10)"), - 160, -10.347587628821941, 190, -10); + 160, rng(-10.347587628821941, -10.347587628821937), 190, -10); tester::apply("s04", from_wkt("SEGMENT(-40 45,140 60)"), @@ -1327,26 +1385,24 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_vincenty ) tester::apply("s09", from_wkt("SEGMENT(1 -45,179 30)"), - 1, -85.392840929577218, 179, 30, - 3 * eps); + 1, rng(-85.392840929577218, -85.392840929576352), 179, 30); tester::apply("s09a", from_wkt("SEGMENT(2 -45,181 30)"), - 2, -87.689330275867817, 181, 30); + 2, rng(-87.689330275867817, -87.689330275867405), 181, 30); // very long segment tester::apply("s10", from_wkt("SEGMENT(0 -45,181 30)"), - -179, -87.689330275867832, 0, 30, - 2.0 * eps); + -179, rng(-87.689330275867832, -87.689330275867405), 0, 30); tester::apply("s11", from_wkt("SEGMENT(260 30,20 45)"), - -100, 30, 20, 57.990810647057032); + -100, 30, 20, rng(57.990810647056549, 57.990810647057032)); tester::apply("s11a", from_wkt("SEGMENT(260 45,20 30)"), - -100, 30, 20, 57.990810647057032); + -100, 30, 20, rng(57.990810647056541, 57.990810647057032)); // segment degenerating to the north pole tester::apply("s12", @@ -1364,7 +1420,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_vincenty ) tester::apply("s15", from_wkt("SEGMENT(50 45,185 45)"), - 50, 45, 185, 69.098479136978497); + 50, 45, 185, rng(69.098479136978156, 69.098479136978497)); // segment that lies on the equator tester::apply("s16", @@ -2012,8 +2068,7 @@ BOOST_AUTO_TEST_CASE( envelope_spheroid_linestring ) // linestring that circles the entire globe tester::apply("l03", from_wkt("LINESTRING(-185 0,-170 25,-50 10,10 10,20 20,100 5,180 15)"), - -180, 0, 180, 33.702476580413318, - 4.0 * std::numeric_limits::epsilon()); + -180, 0, 180, rng(33.702476580412359, 33.702476580413318)); // linestring that crosses the antimeridian but staying close to it tester::apply("l04", @@ -2261,19 +2316,19 @@ BOOST_AUTO_TEST_CASE( envelope_spheroid_multilinestring ) tester::apply("ml04", from_wkt("MULTILINESTRING((-150 40,-100 80),(10 35,100 80))"), - -150, 35, 100, 80.082544902477267); + -150, 35, 100, rng(80.07385383411011, 80.082544902477267)); tester::apply("ml04a", from_wkt("MULTILINESTRING((-150 40,-100 80),(10 35,100 80),(170 25,-160 80))"), - 10, 25, 260, 80.082544902477267); + 10, 25, 260, rng(80.07385383411011, 80.082544902477267)); tester::apply("ml05", from_wkt("MULTILINESTRING((-140 40,-100 80),(10 35,100 80))"), - -140, 35, 100, 80.082544902477267); + -140, 35, 100, rng(80.07385383411011, 80.082544902477267)); tester::apply("ml05a", from_wkt("MULTILINESTRING((-140 40,-100 80),(10 35,100 80),(170 25,-160 80))"), - 10, 25, 260, 80.082544902477267); + 10, 25, 260, rng(80.07385383411011, 80.082544902477267)); } 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 14066cd47..ba04c85ef 100644 --- a/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp +++ b/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp @@ -11,9 +11,11 @@ #ifndef BOOST_GEOMETRY_TEST_ENVELOPE_EXPAND_ON_SPHEROID_HPP #define BOOST_GEOMETRY_TEST_ENVELOPE_EXPAND_ON_SPHEROID_HPP + +#include #include #include -#include +#include #include @@ -29,6 +31,37 @@ #include +struct rng +{ + typedef double type; + + rng(double l, double h) + : lo(l), hi(h) + { + BOOST_GEOMETRY_ASSERT(lo <= hi); + } + + friend rng operator*(rng const& l, double v) { return rng(l.lo * v, l.hi * v); } + + friend bool operator<=(rng const& l, rng const& r) { return l.lo <= r.hi; } + friend bool operator<=(double l, rng const& r) { return l <= r.hi; } + friend bool operator<=(rng const& l, double r) { return l.lo <= r; } + + friend bool operator<(rng const& l, rng const& r) { return !operator<=(r, l); } + friend bool operator<(double l, rng const& r) { return !operator<=(r, l); } + friend bool operator<(rng const& l, double r) { return !operator<=(r, l); } + + friend bool operator==(double l, rng const& r) { return r.lo <= l && l <= r.hi; } + + friend std::ostream & operator<<(std::ostream & os, rng const& v) + { + return (os << "[" << v.lo << ", " << v.hi << "]"); + } + + double lo, hi; +}; + + template char const* units2string() { @@ -49,10 +82,14 @@ struct other_system_info > typedef bg::degree units; typedef bg::cs::spherical_equatorial type; - template - static inline T convert(T const& value) + static inline double convert(double value) { - return value * bg::math::r2d(); + return value * bg::math::r2d(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::r2d(); } }; @@ -62,10 +99,14 @@ struct other_system_info > typedef bg::radian units; typedef bg::cs::spherical_equatorial type; - template - static inline T convert(T const& value) + static inline double convert(double value) { - return value * bg::math::d2r(); + return value * bg::math::d2r(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::d2r(); } }; @@ -75,10 +116,14 @@ struct other_system_info > typedef bg::degree units; typedef bg::cs::geographic type; - template - static inline T convert(T const& value) + static inline double convert(double value) { - return value * bg::math::r2d(); + return value * bg::math::r2d(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::r2d(); } }; @@ -88,15 +133,18 @@ struct other_system_info > typedef bg::radian units; typedef bg::cs::geographic type; - template - static inline T convert(T const& value) + static inline double convert(double value) { - return value * bg::math::d2r(); + return value * bg::math::d2r(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::d2r(); } }; - class equals_with_tolerance { private: @@ -118,11 +166,99 @@ private: public: equals_with_tolerance(double tolerence) : m_tolerence(tolerence) {} - template - inline bool operator()(T const& value1, T const& value2) const + inline bool operator()(double value1, double value2) const { return check_close(value1, value2, m_tolerence); } + + inline bool operator()(double l, rng const& r) const + { + return (r.lo < l && l < r.hi) + || check_close(l, r.lo, m_tolerence) + || check_close(l, r.hi, m_tolerence); + } +}; + + +bool equals_with_eps(double l, double r) +{ + return bg::math::equals(l, r); +} + +bool equals_with_eps(double l, rng r) +{ + return (r.lo < l && l < r.hi) + || bg::math::equals(l, r.lo) + || bg::math::equals(l, r.hi); +} + + +template +< + typename Box, + std::size_t DimensionCount = bg::dimension::value +> +struct box_check_equals +{ + template + static inline bool apply(Box const& box, + T1 const& lon_min, T2 const& lat_min, double, + T3 const& lon_max, T4 const& lat_max, double, + double tol) + { + equals_with_tolerance equals(tol); + +#ifndef BOOST_GEOMETRY_TEST_ENABLE_FAILING + // check latitude with tolerance when necessary + return equals_with_eps(bg::get<0, 0>(box), lon_min) + && (bg::get<0, 1>(box) < 0 + ? equals(bg::get<0, 1>(box), lat_min) + : equals_with_eps(bg::get<0, 1>(box), lat_min)) + && equals_with_eps(bg::get<1, 0>(box), lon_max) + && (bg::get<1, 1>(box) > 0 + ? equals(bg::get<1, 1>(box), lat_max) + : equals_with_eps(bg::get<1, 1>(box), lat_max)); +#else + // check latitude with tolerance when necessary + return bg::get<0, 0>(box) == lon_min + && (bg::get<0, 1>(box) < 0 + ? equals(bg::get<0, 1>(box), lat_min) + : bg::get<0, 1>(box) == lat_min) + && bg::get<1, 0>(box) == lon_max + && (bg::get<1, 1>(box) > 0 + ? equals(bg::get<1, 1>(box), lat_max) + : bg::get<1, 1>(box) == lat_max); +#endif + } +}; + +template +struct box_check_equals +{ + template + static inline bool apply(Box const& box, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, + double tol) + { +#ifndef BOOST_GEOMETRY_TEST_ENABLE_FAILING + equals_with_tolerance equals(tol); + + return box_check_equals::apply(box, + lon_min, lat_min, height_min, + lon_max, lat_max, height_max, + tol) + && equals(bg::get<0, 2>(box), height_min) + && equals(bg::get<1, 2>(box), height_max); +#else + return box_equals::apply(box, + lon_min, lat_min, height_min, + lon_max, lat_max, height_max, + tol) + && bg::get<0, 2>(box) == height_min + && bg::get<1, 2>(box) == height_max; +#endif + } }; @@ -136,48 +272,22 @@ struct box_equals { static inline bool apply(Box1 const& box1, Box2 const& box2, double tol) { - equals_with_tolerance equals(tol); - -#ifndef BOOST_GEOMETRY_TEST_ENABLE_FAILING - // check latitude with tolerance when necessary - return bg::math::equals(bg::get<0, 0>(box1), bg::get<0, 0>(box2)) - && (bg::get<0, 1>(box1) < 0 - ? equals(bg::get<0, 1>(box1), bg::get<0, 1>(box2)) - : bg::math::equals(bg::get<0, 1>(box1), bg::get<0, 1>(box2))) - && bg::math::equals(bg::get<1, 0>(box1), bg::get<1, 0>(box2)) - && (bg::get<1, 1>(box1) > 0 - ? equals(bg::get<1, 1>(box1), bg::get<1, 1>(box2)) - : bg::math::equals(bg::get<1, 1>(box1), bg::get<1, 1>(box2))); -#else - // check latitude with tolerance when necessary - return bg::get<0, 0>(box1) == bg::get<0, 0>(box2) - && (bg::get<0, 1>(box1) < 0 - ? equals(bg::get<0, 1>(box1), bg::get<0, 1>(box2)) - : bg::get<0, 1>(box1) == bg::get<0, 1>(box2)) - && bg::get<1, 0>(box1) == bg::get<1, 0>(box2) - && (bg::get<1, 1>(box1) > 0 - ? equals(bg::get<1, 1>(box1), bg::get<1, 1>(box2)) - : bg::get<1, 1>(box1) == bg::get<1, 1>(box2)); -#endif + return box_check_equals::apply(box1, + bg::get<0, 0>(box2), bg::get<0, 1>(box2), 0.0, + bg::get<1, 0>(box2), bg::get<1, 1>(box2), 0.0, + tol); } }; -template +template struct box_equals { static inline bool apply(Box1 const& box1, Box2 const& box2, double tol) { -#ifndef BOOST_GEOMETRY_TEST_ENABLE_FAILING - equals_with_tolerance equals(tol); - - return box_equals::apply(box1, box2, tol) - && equals(bg::get<0, 2>(box1), bg::get<0, 2>(box2)) - && equals(bg::get<1, 2>(box1), bg::get<1, 2>(box2)); -#else - return box_equals::apply(box1, box2, tol) - && bg::get<0, 2>(box1) == bg::get<0, 2>(box2) - && bg::get<1, 2>(box1) == bg::get<1, 2>(box2); -#endif + return box_check_equals::apply(box1, + bg::get<0, 0>(box2), bg::get<0, 1>(box2), bg::get<0, 2>(box2), + bg::get<1, 0>(box2), bg::get<1, 1>(box2), bg::get<1, 2>(box2), + tol); } }; diff --git a/test/algorithms/is_simple.cpp b/test/algorithms/is_simple.cpp index 17cbb4b7c..a61ba6216 100644 --- a/test/algorithms/is_simple.cpp +++ b/test/algorithms/is_simple.cpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// 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 @@ -63,7 +64,7 @@ typedef bg::model::box box_type; //---------------------------------------------------------------------------- -template +template void test_simple(Geometry const& geometry, bool expected_result, bool check_validity = true) { @@ -72,12 +73,22 @@ void test_simple(Geometry const& geometry, bool expected_result, #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); @@ -92,6 +103,26 @@ void test_simple(Geometry const& geometry, bool expected_result, } +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/length/length_geo.cpp b/test/algorithms/length/length_geo.cpp index a5e340884..66754eaf0 100644 --- a/test/algorithms/length/length_geo.cpp +++ b/test/algorithms/length/length_geo.cpp @@ -80,7 +80,7 @@ template void test_vincenty() { typename geo_strategies

    ::vincenty_type vincenty; - test_with_strategy

    (1116828.8624 + 1116161.625, vincenty); + test_with_strategy

    (1116825.857 + 1116159.144, vincenty); } template diff --git a/test/algorithms/perimeter/perimeter_geo.cpp b/test/algorithms/perimeter/perimeter_geo.cpp index 675f6bf68..9b4452dcd 100644 --- a/test/algorithms/perimeter/perimeter_geo.cpp +++ b/test/algorithms/perimeter/perimeter_geo.cpp @@ -83,7 +83,7 @@ template void test_vincenty() { typename geo_strategies

    ::vincenty_type vincenty; - test_with_strategy

    (1116828.8624 + 1116161.625, vincenty); + test_with_strategy

    (1116825.857 + 1116159.144, vincenty); } template diff --git a/test/algorithms/set_operations/difference/test_difference.hpp b/test/algorithms/set_operations/difference/test_difference.hpp index 947d09131..69c296b5a 100644 --- a/test/algorithms/set_operations/difference/test_difference.hpp +++ b/test/algorithms/set_operations/difference/test_difference.hpp @@ -3,8 +3,8 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// 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 // Use, modification and distribution is subject to the Boost Software License, @@ -131,20 +131,28 @@ std::string test_difference(std::string const& caseid, G1 const& g1, G2 const& g typedef typename bg::coordinate_type::type coordinate_type; boost::ignore_unused(); - bg::model::multi_polygon result; + bg::model::multi_polygon result, result_s; + + typedef typename bg::strategy::relate::services::default_strategy + < + G1, G2 + >::type strategy_type; if (sym) { bg::sym_difference(g1, g2, result); + bg::sym_difference(g1, g2, result_s, strategy_type()); } else { bg::difference(g1, g2, result); + bg::difference(g1, g2, result_s, strategy_type()); } if (settings.remove_spikes) { bg::remove_spikes(result); + bg::remove_spikes(result_s); } std::ostringstream return_string; @@ -233,6 +241,8 @@ std::string test_difference(std::string const& caseid, G1 const& g1, G2 const& g } BOOST_CHECK_CLOSE(area, expected_area, settings.percentage); + + BOOST_CHECK_EQUAL(bg::num_points(result), bg::num_points(result_s)); #endif diff --git a/test/algorithms/set_operations/difference/test_difference_linear_linear.hpp b/test/algorithms/set_operations/difference/test_difference_linear_linear.hpp index 258d5cfb4..2fd934768 100644 --- a/test/algorithms/set_operations/difference/test_difference_linear_linear.hpp +++ b/test/algorithms/set_operations/difference/test_difference_linear_linear.hpp @@ -1,11 +1,12 @@ // 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_TEST_DIFFERENCE_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_TEST_DIFFERENCE_LINEAR_LINEAR_HPP @@ -25,6 +26,23 @@ //================================================================== //================================================================== +template +inline void check_result(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MultiLineString const& mls_output, + MultiLineString const& mls_diff, + std::string const& case_id, + double tolerance) +{ + BOOST_CHECK_MESSAGE( equals::apply(mls_diff, mls_output, tolerance), + "case id: " << case_id + << ", difference L/L: " << bg::wkt(geometry1) + << " " << bg::wkt(geometry2) + << " -> Expected: " << bg::wkt(mls_diff) + << std::setprecision(20) + << " computed: " << bg::wkt(mls_output) ); +} + template < typename Geometry1, typename Geometry2, @@ -52,6 +70,22 @@ private: linestring_vector ls_vector_output; linestring_deque ls_deque_output; + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + bg::difference(geometry1, geometry2, mls_output, strategy_type()); + + if (reverse_output_for_checking) + { + bg::reverse(mls_output); + } + + check_result(geometry1, geometry2, mls_output, mls_diff, case_id, tolerance); + + // Check normal behaviour + bg::clear(mls_output); bg::difference(geometry1, geometry2, mls_output); if ( reverse_output_for_checking ) @@ -59,14 +93,8 @@ private: bg::reverse(mls_output); } - BOOST_CHECK_MESSAGE( equals::apply(mls_diff, mls_output, tolerance), - "case id: " << case_id - << ", difference L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_diff) - << std::setprecision(20) - << " computed: " << bg::wkt(mls_output) ); - + check_result(geometry1, geometry2, mls_output, mls_diff, case_id, tolerance); + set_operation_output("difference", case_id, geometry1, geometry2, mls_output); diff --git a/test/algorithms/set_operations/intersection/test_intersection.hpp b/test/algorithms/set_operations/intersection/test_intersection.hpp index 58b0d6f28..bc3f16728 100644 --- a/test/algorithms/set_operations/intersection/test_intersection.hpp +++ b/test/algorithms/set_operations/intersection/test_intersection.hpp @@ -3,8 +3,8 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// 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 // Use, modification and distribution is subject to the Boost Software License, @@ -189,6 +189,18 @@ 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, + expected_holes_count, expected_point_count, expected_length_or_area, + settings); + + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + G1, G2 + >::type strategy_type; + intersection_output.clear(); + bg::intersection(g1, g2, intersection_output, strategy_type()); + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); diff --git a/test/algorithms/set_operations/intersection/test_intersection_linear_linear.hpp b/test/algorithms/set_operations/intersection/test_intersection_linear_linear.hpp index b4ade8310..f916b023c 100644 --- a/test/algorithms/set_operations/intersection/test_intersection_linear_linear.hpp +++ b/test/algorithms/set_operations/intersection/test_intersection_linear_linear.hpp @@ -1,11 +1,12 @@ // 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_TEST_INTERSECTION_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_TEST_INTERSECTION_LINEAR_LINEAR_HPP @@ -26,6 +27,25 @@ //================================================================== //================================================================== +template +inline void check_result(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MultiLineString const& mls_output, + MultiLineString const& mls_int1, + MultiLineString const& mls_int2, + std::string const& case_id, + double tolerance) +{ + BOOST_CHECK_MESSAGE( equals::apply(mls_int1, mls_output, tolerance) + || equals::apply(mls_int2, mls_output, tolerance), + "case id: " << case_id + << ", intersection L/L: " << bg::wkt(geometry1) + << " " << bg::wkt(geometry2) + << " -> Expected: " << bg::wkt(mls_int1) + << " or: " << bg::wkt(mls_int2) + << " computed: " << bg::wkt(mls_output) ); +} + template < typename Geometry1, typename Geometry2, @@ -53,16 +73,20 @@ private: linestring_vector ls_vector_output; linestring_deque ls_deque_output; + // Check normal behaviour bg::intersection(geometry1, geometry2, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_int1, mls_output, tolerance) - || equals::apply(mls_int2, mls_output, tolerance), - "case id: " << case_id - << ", intersection L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_int1) - << " or: " << bg::wkt(mls_int2) - << " computed: " << bg::wkt(mls_output) ); + check_result(geometry1, geometry2, mls_output, mls_int1, mls_int2, case_id, tolerance); + + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + bg::clear(mls_output); + bg::intersection(geometry1, geometry2, mls_output, strategy_type()); + + check_result(geometry1, geometry2, mls_output, mls_int1, mls_int2, case_id, tolerance); set_operation_output("intersection", case_id, geometry1, geometry2, mls_output); @@ -109,14 +133,7 @@ private: bg::clear(mls_output); bg::intersection(geometry2, geometry1, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_int1, mls_output, tolerance) - || equals::apply(mls_int2, mls_output, tolerance), - "case id: " << case_id - << ", intersection L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_int1) - << " or: " << bg::wkt(mls_int2) - << " computed: " << bg::wkt(mls_output) ); + check_result(geometry1, geometry2, mls_output, mls_int1, mls_int2, case_id, tolerance); #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "Geometry #1: " << bg::wkt(geometry2) << std::endl; diff --git a/test/algorithms/set_operations/sym_difference/test_sym_difference_linear_linear.hpp b/test/algorithms/set_operations/sym_difference/test_sym_difference_linear_linear.hpp index 3676fd9a2..843345247 100644 --- a/test/algorithms/set_operations/sym_difference/test_sym_difference_linear_linear.hpp +++ b/test/algorithms/set_operations/sym_difference/test_sym_difference_linear_linear.hpp @@ -1,11 +1,12 @@ // 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_TEST_SYM_DIFFERENCE_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_TEST_SYM_DIFFERENCE_LINEAR_LINEAR_HPP @@ -24,6 +25,22 @@ //================================================================== //================================================================== +template +inline void check_result(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MultiLineString const& mls_output, + MultiLineString const& mls_sym_diff, + std::string const& case_id, + double tolerance) +{ + BOOST_CHECK_MESSAGE( equals::apply(mls_sym_diff, mls_output, tolerance), + "case id: " << case_id + << ", sym diff L/L: " << bg::wkt(geometry1) + << " " << bg::wkt(geometry2) + << " -> Expected: " << bg::wkt(mls_sym_diff) + << " computed: " << bg::wkt(mls_output) ); +} + template < typename Geometry1, typename Geometry2, @@ -50,15 +67,20 @@ private: linestring_vector ls_vector_output; linestring_deque ls_deque_output; + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + bg::sym_difference(geometry1, geometry2, mls_output, strategy_type()); + + check_result(geometry1, geometry2, mls_output, mls_sym_diff, case_id, tolerance); + + // Check normal behaviour + bg::clear(mls_output); bg::sym_difference(geometry1, geometry2, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_sym_diff, mls_output, tolerance), - "case id: " << case_id - << ", sym diff L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_sym_diff) - << " computed: " << bg::wkt(mls_output) ); - + check_result(geometry1, geometry2, mls_output, mls_sym_diff, case_id, tolerance); set_operation_output("sym_difference", case_id, geometry1, geometry2, mls_output); diff --git a/test/algorithms/set_operations/union/test_union.hpp b/test/algorithms/set_operations/union/test_union.hpp index 810c7c353..32bc72aba 100644 --- a/test/algorithms/set_operations/union/test_union.hpp +++ b/test/algorithms/set_operations/union/test_union.hpp @@ -3,8 +3,8 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// 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 @@ -70,6 +70,18 @@ inline void check_input_validity(std::string const& caseid, int case_index, } #endif +template +inline std::size_t num_points(Range const& rng, bool add_for_open = false) +{ + std::size_t result = 0; + for (typename boost::range_iterator::type it = boost::begin(rng); + it != boost::end(rng); ++it) + { + result += bg::num_points(*it, add_for_open); + } + return result; +} + template void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, int expected_count, int expected_hole_count, @@ -82,7 +94,7 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, // Declare output (vector of rings or multi_polygon) typedef typename setop_output_type::type result_type; - result_type clip; + result_type clip, clip_s; #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) std::cout << "*** UNION " << caseid << std::endl; @@ -93,8 +105,16 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, check_input_validity(caseid, 1, g2); #endif + // Check normal behaviour bg::union_(g1, g2, clip); + // Check strategy passed explicitly + typedef typename bg::strategy::intersection::services::default_strategy + < + typename bg::cs_tag::type + >::type strategy_type; + bg::union_(g1, g2, clip_s, strategy_type()); + typename bg::default_area_result::type area = 0; std::size_t n = 0; std::size_t holes = 0; @@ -178,6 +198,8 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, BOOST_CHECK_CLOSE(area, expected_area, settings.percentage); + BOOST_CHECK_EQUAL(num_points(clip), num_points(clip_s)); + #if defined(TEST_WITH_SVG) { bool const ccw = diff --git a/test/algorithms/set_operations/union/test_union_linear_linear.hpp b/test/algorithms/set_operations/union/test_union_linear_linear.hpp index 1b6dda37c..e4357b603 100644 --- a/test/algorithms/set_operations/union/test_union_linear_linear.hpp +++ b/test/algorithms/set_operations/union/test_union_linear_linear.hpp @@ -1,11 +1,12 @@ // 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_TEST_UNION_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_TEST_UNION_LINEAR_LINEAR_HPP @@ -24,6 +25,22 @@ //================================================================== //================================================================== +template +inline void check_result(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MultiLineString const& mls_output, + MultiLineString const& mls_union, + std::string const& case_id, + double tolerance) +{ + BOOST_CHECK_MESSAGE( equals::apply(mls_union, mls_output, tolerance), + "case id: " << case_id + << ", union L/L: " << bg::wkt(geometry1) + << " " << bg::wkt(geometry2) + << " -> Expected: " << bg::wkt(mls_union) + << " computed: " << bg::wkt(mls_output) ); +} + template < typename Geometry1, typename Geometry2, @@ -51,14 +68,20 @@ private: linestring_vector ls_vector_output; linestring_deque ls_deque_output; + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + bg::union_(geometry1, geometry2, mls_output, strategy_type()); + + check_result(geometry1, geometry2, mls_output, mls_union1, case_id, tolerance); + + // Check normal behaviour + bg::clear(mls_output); bg::union_(geometry1, geometry2, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_union1, mls_output, tolerance), - "case id: " << case_id - << ", union L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_union1) - << " computed: " << bg::wkt(mls_output) ); + check_result(geometry1, geometry2, mls_output, mls_union1, case_id, tolerance); set_operation_output("union", case_id, geometry1, geometry2, mls_output); @@ -101,17 +124,12 @@ private: #endif } - // check the symmetric difference where the order of the two + // check the union where the order of the two // geometries is reversed bg::clear(mls_output); bg::union_(geometry2, geometry1, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_union2, mls_output, tolerance), - "case id: " << case_id - << ", union L/L: " << bg::wkt(geometry2) - << " " << bg::wkt(geometry1) - << " -> Expected: " << bg::wkt(mls_union2) - << " computed: " << bg::wkt(mls_output) ); + check_result(geometry1, geometry2, mls_output, mls_union2, case_id, tolerance); #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "Geometry #1: " << bg::wkt(geometry2) << std::endl; diff --git a/test/algorithms/test_is_valid.hpp b/test/algorithms/test_is_valid.hpp index 02ebe164e..2776327d9 100644 --- a/test/algorithms/test_is_valid.hpp +++ b/test/algorithms/test_is_valid.hpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// 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 @@ -287,7 +288,7 @@ struct validity_tester_linear { bool const irrelevant = true; bg::is_valid_default_policy visitor; - return bg::is_valid(geometry, visitor); + return bg::is_valid(geometry, visitor, bg::default_strategy()); } template @@ -296,7 +297,7 @@ struct validity_tester_linear bool const irrelevant = true; std::ostringstream oss; bg::failing_reason_policy visitor(oss); - bg::is_valid(geometry, visitor); + bg::is_valid(geometry, visitor, bg::default_strategy()); return oss.str(); } }; @@ -309,7 +310,7 @@ struct validity_tester_areal static inline bool apply(Geometry const& geometry) { bg::is_valid_default_policy visitor; - return bg::is_valid(geometry, visitor); + return bg::is_valid(geometry, visitor, bg::default_strategy()); } template @@ -317,7 +318,7 @@ struct validity_tester_areal { std::ostringstream oss; bg::failing_reason_policy visitor(oss); - bg::is_valid(geometry, visitor); + bg::is_valid(geometry, visitor, bg::default_strategy()); return oss.str(); } diff --git a/test/arithmetic/cross_product.cpp b/test/arithmetic/cross_product.cpp index e53662861..cb1ddf617 100644 --- a/test/arithmetic/cross_product.cpp +++ b/test/arithmetic/cross_product.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include @@ -60,6 +62,7 @@ void test_4d() bg::assign_values(p2, 45, 70, 20); bg::set<3>(p2, 35); P c = bg::cross_product(p1, p2); + boost::ignore_unused(c); } #endif diff --git a/test/formulas/intersection.cpp b/test/formulas/intersection.cpp index f0b1b49d7..85516a2e0 100644 --- a/test/formulas/intersection.cpp +++ b/test/formulas/intersection.cpp @@ -13,6 +13,7 @@ #include "intersection_cases.hpp" #include +#include #include #include #include @@ -20,14 +21,23 @@ #include #include -void check_inverse(expected_result const& result, expected_result const& expected, expected_result const& reference, double reference_error) +void check_result(expected_result const& result, expected_result const& expected, + expected_result const& reference, double reference_error, + bool check_reference_only) { - check_one(result.lon, expected.lon, reference.lon, reference_error); - check_one(result.lat, expected.lat, reference.lat, reference_error); + //BOOST_CHECK_MESSAGE((false), "(" << result.lon << " " << result.lat << ") vs (" << expected.lon << " " << expected.lat << ")"); + check_one(result.lon, expected.lon, reference.lon, reference_error, false, check_reference_only); + check_one(result.lat, expected.lat, reference.lat, reference_error, false, check_reference_only); } -void test_all(expected_results const& results) +void test_formulas(expected_results const& results, bool check_reference_only) { + // reference result + if (results.sjoberg_vincenty.lon == ND) + { + return; + } + double const d2r = bg::math::d2r(); double const r2d = bg::math::r2d(); @@ -45,35 +55,115 @@ void test_all(expected_results const& results) // WGS84 bg::srs::spheroid spheroid(6378137.0, 6356752.3142451793); - bg::formula::gnomonic_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.gnomonic_vincenty, results.gnomonic_karney, 0.00000001); + if (results.gnomonic_vincenty.lon != ND) + { + bg::formula::gnomonic_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.gnomonic_vincenty, results.sjoberg_vincenty, 0.00000001, check_reference_only); + } - bg::formula::gnomonic_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.gnomonic_thomas, results.gnomonic_karney, 0.0000001); + if (results.gnomonic_thomas.lon != ND) + { + bg::formula::gnomonic_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.gnomonic_thomas, results.sjoberg_vincenty, 0.0000001, check_reference_only); + } - bg::formula::sjoberg_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.sjoberg_vincenty, results.sjoberg_karney, 0.00000001); + if (results.sjoberg_vincenty.lon != ND) + { + bg::formula::sjoberg_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.sjoberg_vincenty, results.sjoberg_vincenty, 0.00000001, check_reference_only); + } - bg::formula::sjoberg_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.sjoberg_thomas, results.sjoberg_karney, 0.0000001); + if (results.sjoberg_thomas.lon != ND) + { + bg::formula::sjoberg_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.sjoberg_thomas, results.sjoberg_vincenty, 0.0000001, check_reference_only); + } - bg::formula::sjoberg_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.sjoberg_andoyer, results.sjoberg_karney, 0.0001); + if (results.sjoberg_andoyer.lon != ND) + { + bg::formula::sjoberg_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.sjoberg_andoyer, results.sjoberg_vincenty, 0.0001, check_reference_only); + } + + if (results.great_elliptic.lon != ND) + { + typedef bg::model::point > point_geo; + typedef bg::model::point point_3d; + point_geo a1(results.p1.lon, results.p1.lat); + point_geo a2(results.p2.lon, results.p2.lat); + point_geo b1(results.q1.lon, results.q1.lat); + point_geo b2(results.q2.lon, results.q2.lat); + point_3d a1v = bg::formula::geo_to_cart3d(a1, spheroid); + point_3d a2v = bg::formula::geo_to_cart3d(a2, spheroid); + point_3d b1v = bg::formula::geo_to_cart3d(b1, spheroid); + point_3d b2v = bg::formula::geo_to_cart3d(b2, spheroid); + point_3d resv(0, 0, 0); + point_geo res(0, 0); + bg::formula::great_elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); + res = bg::formula::cart3d_to_geo(resv, spheroid); + result.lon = bg::get<0>(res); + result.lat = bg::get<1>(res); + check_result(result, results.great_elliptic, results.sjoberg_vincenty, 0.01, check_reference_only); + } +} + +void test_4_input_combinations(expected_results const& results, bool check_reference_only) +{ + test_formulas(results, check_reference_only); + +#ifdef BOOST_GEOMETRY_TEST_GEO_INTERSECTION_TEST_SIMILAR + { + expected_results results_alt = results; + std::swap(results_alt.p1, results_alt.p2); + test_formulas(results_alt, true); + } + { + expected_results results_alt = results; + std::swap(results_alt.q1, results_alt.q2); + test_formulas(results_alt, true); + } + { + expected_results results_alt = results; + std::swap(results_alt.p1, results_alt.p2); + std::swap(results_alt.q1, results_alt.q2); + test_formulas(results_alt, true); + } +#endif +} + +void test_all(expected_results const& results) +{ + test_4_input_combinations(results, false); + +#ifdef BOOST_GEOMETRY_TEST_GEO_INTERSECTION_TEST_SIMILAR + expected_results results_alt = results; + results_alt.p1.lat *= -1; + results_alt.p2.lat *= -1; + results_alt.q1.lat *= -1; + results_alt.q2.lat *= -1; + results_alt.gnomonic_vincenty.lat *= -1; + results_alt.gnomonic_thomas.lat *= -1; + results_alt.sjoberg_vincenty.lat *= -1; + results_alt.sjoberg_thomas.lat *= -1; + results_alt.sjoberg_andoyer.lat *= -1; + results_alt.great_elliptic.lat *= -1; + test_4_input_combinations(results_alt, true); +#endif } int test_main(int, char*[]) diff --git a/test/formulas/intersection_cases.hpp b/test/formulas/intersection_cases.hpp index d3d148db3..483d52b3b 100644 --- a/test/formulas/intersection_cases.hpp +++ b/test/formulas/intersection_cases.hpp @@ -30,97 +30,297 @@ struct expected_results coordinates p2; coordinates q1; coordinates q2; - expected_result gnomonic_karney; expected_result gnomonic_vincenty; expected_result gnomonic_thomas; - expected_result sjoberg_karney; expected_result sjoberg_vincenty; expected_result sjoberg_thomas; expected_result sjoberg_andoyer; + expected_result great_elliptic; }; +#define ND 1000 + expected_results expected[] = { { { -1, -1 },{ 1, 1 }, { -1, 1 },{ 1, -1 }, - { 0.0000000000000000, 0.0000000000000002 }, { 0.0000000000000000, 0.0000000000000000 }, { 0.0000000000000000, 0.0000000000000000 }, - { 0.0000000000633173, 0.0000000000000003 }, { 0.0000000000626537, -0.0000000000000000 }, { -0.0000000187861002, -0.0000000000000000 }, - { -0.0000055778585615, -0.0000000000000000 } + { -0.0000055778585615, -0.0000000000000000 }, + { -0.0000000000000000, -0.0000000000000000 } },{ { 1, 1 },{ -1, -1 }, { -1, 1 },{ 1, -1 }, - { 0.0000000000000000, 0.0000000000000002 }, { 0.0000000000000000, 0.0000000000000000 }, { 0.0000000000000000, 0.0000000000000000 }, - { 0.0000000000000000, 0.0000000000633274 }, { 0.0000000000000000, 0.0000000000626632 }, { -0.0000000000000006, -0.0000000187889745 }, - { -0.0000000000000001, -0.0000055787431353 } + { -0.0000000000000001, -0.0000055787431353 }, + { -0.0000000000000000, 0.0000000000000000 } },{ { -1, -1 },{ 1, 1 }, { 1, -1 },{ -1, 1 }, - { 0.0000000000000000, 0.0000000000000002 }, { 0.0000000000000000, 0.0000000000000000 }, { 0.0000000000000000, 0.0000000000000000 }, - { -0.0000000000000000, -0.0000000000633268 }, { -0.0000000000000000, -0.0000000000626632 }, { -0.0000000000000004, 0.0000000187889746 }, - { 0.0000000000000001, 0.0000055787431353 } + { 0.0000000000000001, 0.0000055787431353 }, + { 0.0000000000000000, 0.0000000000000000 } },{ { 1, 1 },{ -1, -1 }, { 1, -1 },{ -1, 1 }, - { 0.0000000000000000, 0.0000000000000002 }, { 0.0000000000000000, 0.0000000000000000 }, { 0.0000000000000000, 0.0000000000000000 }, - { -0.0000000000633173, 0.0000000000000003 }, { -0.0000000000626537, -0.0000000000000000 }, { 0.0000000187860994, 0.0000000000000001 }, - { 0.0000055778585615, -0.0000000000000000 } + { 0.0000055778585615, -0.0000000000000000 }, + { -0.0000000000000000, 0.0000000000000000 } },{ { 0, 0 },{ 1, 1 }, { 0, 1 },{ 1, 0 }, - { 0.5000000000000000, 0.5000573755188484 }, { 0.5000000000000000, 0.5000573755188470 }, { 0.5000000000000000, 0.5000573755109839 }, - { 0.5000000000316606, 0.5000573755188390 }, { 0.5000000000313266, 0.5000573755188389 }, { 0.4999999906069524, 0.5000573755152582 }, - { 0.4999972102164753, 0.5000573755151276 } + { 0.4999972102164753, 0.5000573755151276 }, + { 0.4999999999999999, 0.5000571197534014 } },{ { 1, 1 },{ 0, 0 }, { 0, 1 },{ 1, 0 }, - { 0.5000000000000000, 0.5000573755188484 }, { 0.5000000000000000, 0.5000573755188470 }, { 0.5000000000000000, 0.5000573755109839 }, - { 0.5000000000000000, 0.5000573755505008 }, { 0.5000000000000000, 0.5000573755501669 }, { 0.4999999999999996, 0.5000573661218464 }, - { 0.4999999999999999, 0.5000545856093679 } + { 0.4999999999999999, 0.5000545856093679 }, + { 0.4999999999999999, 0.5000571197534014 } },{ { 0, 0 },{ 1, 1 }, { 1, 0 },{ 0, 1 }, - { 0.5000000000000000, 0.5000573755188484 }, { 0.5000000000000000, 0.5000573755188470 }, { 0.5000000000000000, 0.5000573755109839 }, - { 0.5000000000000001, 0.5000573754871773 }, { 0.4999999999999999, 0.5000573754875109 }, { 0.4999999999999999, 0.5000573849086647 }, - { 0.5000000000000000, 0.5000601654208935 } + { 0.5000000000000000, 0.5000601654208935 }, + { 0.4999999999999999, 0.5000571197534014 } },{ { 1, 1 },{ 0, 0 }, { 1, 0 },{ 0, 1 }, - { 0.5000000000000000, 0.5000573755188484 }, { 0.5000000000000000, 0.5000573755188470 }, { 0.5000000000000000, 0.5000573755109839 }, - { 0.4999999999683394, 0.5000573755188390 }, { 0.4999999999686731, 0.5000573755188389 }, { 0.5000000093930521, 0.5000573755152582 }, - { 0.5000027897835244, 0.5000573755151276 } + { 0.5000027897835244, 0.5000573755151276 }, + { 0.4999999999999999, 0.5000571197534014 } + },{ + { 1, 1 }, {2, 2}, + {-2, -1}, {-1, -1}, + { ND, ND }, + { ND, ND }, + { -0.9981650042162076, -0.999999718164758 }, + { ND, ND }, + { ND, ND }, + { -0.9981731758085121, -0.9999997213000095 } + },{ + {-25, -31}, {3, 44}, + {-66, -14}, {-1, -1}, + { ND, ND }, + { ND, ND }, + { -15.83269406235058, -4.87746450262433 }, + { ND, ND }, + { ND, ND }, + { -15.82846301029918, -4.869650527718342 } + },{ + {-47, -8}, {-1, -4}, + {-40, -5}, {-5, -5}, + { ND, ND }, + { ND, ND }, + { -10.28209064194141, -5.124101297536392 }, + { ND, ND }, + { ND, ND }, + { -10.29663941896089, -5.123527178300465 } + }, { + {-43, -8}, {3, -4}, + {-43, -5}, {3, -7}, + { ND, ND }, + { ND, ND }, + { -19.95519754153282, -6.521540589691206 }, + { ND, ND }, + { ND, ND }, + { -19.95535387979888, -6.517861579906892 } + }, { + {-1, -17}, {-5, 3}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -3.424525475838061, -5.070550173747042 }, + { ND, ND }, + { ND, ND }, + { -3.424493504575876, -5.070060631936228 } + }, { + {-29, -4}, {-1, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -0.9999999978280006, -5.00000000000684 }, + { ND, ND }, + { ND, ND }, + { -0.9999999999999919, -5 } + }, { + {-4, -6}, {-40, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -40.00000000470697, -4.999999999854269 }, + { ND, ND }, + { ND, ND }, + { -40.00000000000003, -4.999999999999998 } + }, { + {5, -4}, {-25, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -40.04870859732954, -4.998490166905039 }, + { ND, ND }, + { ND, ND }, + { -39.87674154148858, -5.003778171392734 } + }, { + {-44, -1}, {38, -7}, + {-54, -10}, {27, 3}, + { ND, ND }, + { ND, ND }, + { -12.49994722584679, -4.492062263169279 }, + { ND, ND }, + { ND, ND }, + { -12.49786552878283, -4.48224349980724 } + }, { + {-29, -5}, {10, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -14.99999999880286, -5.280237387890117 }, + { ND, ND }, + { ND, ND }, + { -14.99999999999999, -5.278284829442087 } + }, { + {-29, -5}, {7, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -12.68814198534678, -5.255404335144863 }, + { ND, ND }, + { ND, ND }, + { -12.68883814704657, -5.253634733357015 } + }, { + {-29, -5}, {2, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -6.793546728871506, -5.153444544719724 }, + { ND, ND }, + { ND, ND }, + { -6.794933162619669, -5.152409434095275 } + }, { + {-29, -5}, {2, -5}, + {-30, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -22.13104411461423, -5.130155316882458 }, + { ND, ND }, + { ND, ND }, + { -22.13016591567375, -5.129277412801533 } + }, { + {-29, -5}, {2, -5}, + {-32, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -14.99999999908104, -5.187213913248173 }, + { ND, ND }, + { ND, ND }, + { -15.00000000000009, -5.185931965560752 } + }, { + {-92, -24}, {44, 19}, + {-78, -5}, {50, -5}, + { ND, ND }, + { ND, ND }, + { -31.65127861442256, -10.84411293410938 }, + { ND, ND }, + { ND, ND }, + { -31.65294079308247, -10.76785432928444 } + }, { + {-93, -15}, {22, 3}, + {-78, -5}, {50, -5}, + { ND, ND }, + { ND, ND }, + { -32.33383382062637, -10.80295061031259 }, + { ND, ND }, + { ND, ND }, + { -32.26069578187354, -10.73176059393484 } + }, { + {-93, -15}, {28, 3}, + {-78, -5}, {50, -5}, + { ND, ND }, + { ND, ND }, + { -25.71257598566304, -11.13752154787724 }, + { ND, ND }, + { ND, ND }, + { -25.67761999514295, -11.05881610957402 } + }, { + {-54, -21}, {20, 17}, + {-59, -5}, {13, -5}, + { ND, ND }, + { ND, ND }, + { -22.96397490169162, -6.181426780426698 }, + { ND, ND }, + { ND, ND }, + { -22.95999443662035, -6.172089364736989 } + }, { + {-31, -10}, {-31, -2}, + {-37, -10}, {-25, -2}, + { ND, ND }, + { ND, ND }, + { -31.00000000000001, -6.062729839503469 }, + { ND, ND }, + { ND, ND }, + { -31, -6.062411484514648 } + }, { + {-26, -10}, {-26, 13}, + {-37, -4}, {-18, -4}, + { ND, ND }, + { ND, ND }, + { -25.99999999999969, -4.05441766735837 }, + { ND, ND }, + { ND, ND }, + { -26, -4.05405101510235 } + }, { + {-26, -10}, {154, 78}, + {-46, 41}, {-13, 36}, + { ND, ND }, + { ND, ND }, + { -26, 39.19492836828103 }, + { ND, ND }, + { ND, ND }, + { -26, 39.19004133747198 } + }, { + {-26, -10}, {154, 68}, + {-85, 79}, {22, 80}, + { ND, ND }, + { ND, ND }, + { -26.00000000000013, 83.7164889918963 }, + { ND, ND }, + { ND, ND }, + { -26.00000000000002, 83.71603758698208 } + }, { + {-25, 55}, {155, 68}, + {178, 79}, {124, 80}, + { ND, ND }, + { ND, ND }, + { 155, 80.55982670305147 }, + { ND, ND }, + { ND, ND }, + { 155, 80.55961176607357 } } }; diff --git a/test/formulas/inverse.cpp b/test/formulas/inverse.cpp index 4c33cae28..8fa06d80a 100644 --- a/test/formulas/inverse.cpp +++ b/test/formulas/inverse.cpp @@ -39,25 +39,25 @@ void test_all(expected_results const& results) // WGS84 bg::srs::spheroid spheroid(6378137.0, 6356752.3142451793); - bg::formula::result_inverse result; + bg::formula::result_inverse result_v, result_t, result_a; typedef bg::formula::vincenty_inverse vi_t; - result = vi_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); - result.azimuth *= r2d; - result.reverse_azimuth *= r2d; - check_inverse(result, results.vincenty, results.karney, 0.00000001); + 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); typedef bg::formula::thomas_inverse th_t; - result = th_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); - result.azimuth *= r2d; - result.reverse_azimuth *= r2d; - check_inverse(result, results.thomas, results.karney, 0.0000001); + 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); typedef bg::formula::andoyer_inverse an_t; - result = an_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); - result.azimuth *= r2d; - result.reverse_azimuth *= r2d; - check_inverse(result, results.andoyer, results.karney, 0.0001); + 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); } int test_main(int, char*[]) diff --git a/test/formulas/inverse_cases.hpp b/test/formulas/inverse_cases.hpp index 1abc01e85..b3b868d56 100644 --- a/test/formulas/inverse_cases.hpp +++ b/test/formulas/inverse_cases.hpp @@ -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 @@ -31,10 +31,11 @@ struct expected_results { coordinates p1; coordinates p2; - expected_result karney; + expected_result reference; // karney or vincenty expected_result vincenty; expected_result thomas; expected_result andoyer; + }; expected_results expected[] = @@ -472,7 +473,131 @@ expected_results expected[] = { 156876.14925185780157335103, 134.82952991584656388113, 134.80335129775846780831, 156860.22615983051946386695, 0.99969549954768643918 }, { 156876.14903264911845326424, 134.82953099167994537311, 134.80335237457538255512, 156860.22319510785746388137, 0.99969549955919856377 }, { 156874.38594904550700448453, 134.82985169207830722371, 134.80367307400518939176, 156859.34477911840076558292, 0.99969550299904519353 } - } + }, { + // near pole + {90, -45}, {90, -80}, + {3900195.49395913071930408478, -180.00000000000000000000, -180.00000000000000000000, 3662502.04478993499651551247, 0.81922976250863166481}, + {3900195.49395686946809291840, 180.00000000000000000000, 180.00000000000000000000, 3662502.06126507185399532318, 0.81922976508736944368}, + {3900195.49053949490189552307, 180.00000000000000000000, 180.00000000000000000000, 3662502.06126507185399532318, 0.81922976508736944368}, + {3900203.61653685756027698517, 180.00000000000000000000, 180.00000000000000000000, 3662507.91531699104234576225, 0.81923068138101373670} + }, { + // pole, same lon + {90, -45}, {90, -90}, + {5017021.35133497882634401321, -180.00000000000000000000, -180.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.35133314039558172226, 180.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.34791153203696012497, 180.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017017.85355509258806705475, 180.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172} + }, { + // pole + {90, -45}, {0, -90}, + {5017021.35133497882634401321, -180.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.35133314039558172226, 180.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.34791153203696012497, 180.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017017.85355509258806705475, 180.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172} + }, { + // near pole + {90, 45}, {90, 80}, + {3900195.49395913071930408478, 0.00000000000000000000, 0.00000000000000000000, 3662502.04478993499651551247, 0.81922976250863166481}, + {3900195.49395686946809291840, 0.00000000000000000000, 0.00000000000000000000, 3662502.06126507185399532318, 0.81922976508736944368}, + {3900195.49053949490189552307, 0.00000000000000000000, 0.00000000000000000000, 3662502.06126507185399532318, 0.81922976508736944368}, + {3900203.61653685756027698517, 0.00000000000000000076, 0.00000000000000000071, 3662507.91531699104234576225, 0.81923068138101373670} + }, { + // pole, same lon + {90, 45}, {90, 90}, + {5017021.35133497882634401321, 0.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.35133314039558172226, 0.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.34791153203696012497, 0.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017017.85355509258806705475, 0.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172} + }, { + // pole + {90, 45}, {0, 90}, + {5017021.35133497882634401321, -0.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.35133314039558172226, -0.00000000000000496992, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.34791153203696012497, 0.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017017.85355509258806705475, 0.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172} + }, { + // pole + {0, -90}, {90, -45}, + {5017021.35133497882634401321, 90.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70778496454077310940}, + {5017021.35133314039558172226, 90.00000000000000000000, 0.00000000000000496992, 4517590.87884893082082271576, 0.70778494508412304054}, + {5017021.34791153203696012497, 90.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70778494508412304054}, + {5017017.85355509258806705475, 90.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70777827352623101653} + }, { + // pole + {0, 90}, {90, 45}, + {5017021.35133497882634401321, 90.00000000000000000000, -180.00000000000000000000, 4517590.87884893082082271576, 0.70778496454077310940}, + {5017021.35133314039558172226, 90.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70778494508412304054}, + {5017021.34791153203696012497, 90.00000000000001421085, 180.00000000000000000000, 4517590.87884893082082271576, 0.70778494508412304054}, + {5017017.85355509258806705475, 90.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70777827352623101653} + }, { + {90, -45}, {90, 45}, + {9969888.75595548748970031738, 0.00000000000000000000, 0.00000000000000000000, 6361290.27546359039843082428, 0.00431189731318673553}, + {9969888.75595730356872081757, 0.00000000000000000000, 0.00000000000000000000, 6361290.36011654324829578400, 0.00431188406305459994}, + {9969888.73915978334844112396, 0.00000000000000000000, 0.00000000000000000000, 6361290.36011654324829578400, 0.00431188406305459994}, + {9969881.64984572120010852814, 0.00000000000000001848, 0.00000000000000001176, 6361314.54906367603689432144, 0.00430809793699618842} + }, { + {90, 45}, {90, -45}, + {9969888.75595548748970031738, -180.00000000000000000000, -180.00000000000000000000, 6361290.27546359039843082428, 0.00431189731318673553}, + {9969888.75595730356872081757, 180.00000000000000000000, 180.00000000000000000000, 6361290.36011654324829578400, 0.00431188406305459994}, + {9969888.73915978334844112396, 180.00000000000000000000, 180.00000000000000000000, 6361290.36011654324829578400, 0.00431188406305459994}, + {9969881.64984572120010852814, 180.00000000000000000000, 180.00000000000000000000, 6361314.54906367603689432144, 0.00430809793699618842} + }, { + // pole, same lon + {90, 0}, {90, 90}, + {10001965.72931272350251674652, 0.00000000000000000000, 0.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, 0.00000000000000000000, 0.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 0.00000000000000000000, 0.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 0.00000000000000000000, 0.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole + {90, 0}, {0, 90}, + {10001965.72931272350251674652, -0.00000000000000000000, -90.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, -0.00000000000000352016, -90.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 0.00000000000000000000, -90.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 0.00000000000000000000, -90.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole, different lon + {0, 0}, {1, 90}, + {10001965.72931272350251674652, 0.00000000000000000000, 1.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, 0.00000000000000006144, 1.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 0.00000000000000000000, 0.99999999999998867573, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 0.00000000000000000000, 0.99999999999998867573, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole, different lon + {0, 0}, {-1, 90}, + {10001965.72931272350251674652, -0.00000000000000000000, -1.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, -0.00000000000000006144, -1.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 0.00000000000000000000, -1.00000000000001421085, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 0.00000000000000000000, -0.99999999999998867573, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole, different lon + {0, 0}, {1, -90}, + {10001965.72931272350251674652, -180.00000000000000000000, 179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, 180.00000000000000000000, 179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 180.00000000000000000000, 179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 180.00000000000000000000, 179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole, different lon + {0, 0}, {-1, -90}, + {10001965.72931272350251674652, -180.00000000000000000000, -179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, -180.00000000000000000000, -179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 180.00000000000000000000, -179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 180.00000000000000000000, -179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000} + }/*, { + // antipodal + {-90, -45}, {90, 45}, + {20003931.45862544700503349304, -180.00000000000000000000, 0.00000000000000000000, 33675.52452803074265830219, -0.99472900658949181540}, + {19987083.06974205747246742249, 90.00000000000000000000, 90.00000000000000000000, 00000.00000000078240703623, -1.00000000000000000000}, + {00000000.00000000000000000000, 0.00000000000000000000, 0.00000000000000000000, 000000.00000000000000000000, 1.00000000000000000000}, + {20020712.84987257421016693115, 0.00000000000000000000, 0.00000000000000000000, 6361314.54906367603689432144, 0.00430809793699618842} + },{ + // antipodal + {-90, 45}, {90, -45}, + {20003931.45862544700503349304, 0.00000000000000000000, -180.00000000000000000000, 33675.52452803074265830219, -0.99472900658949181540}, + {19987083.06974205747246742249, 90.00000000000000000000, 90.00000000000000000000, -000000.00000000078240703623, -1.00000000000000000000}, + {00000000.00000000000000000000, 0.00000000000000000000, 0.00000000000000000000, 000000.00000000000000000000, 1.00000000000000000000}, + {20020712.84987257421016693115, 180.00000000000000000000, 0.00000000000000000000, 33590.79639541531651047990, -1.00525773151080621837} + }*/ }; size_t const expected_size = sizeof(expected) / sizeof(expected_results); diff --git a/test/formulas/test_formula.hpp b/test/formulas/test_formula.hpp index 92f690bd3..60502281d 100644 --- a/test/formulas/test_formula.hpp +++ b/test/formulas/test_formula.hpp @@ -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 @@ -24,7 +24,8 @@ void normalize_deg(double & deg) deg += 360.0; } -void check_one(double result, double expected, double reference, double reference_error, bool normalize = false) +void check_one(double result, double expected, double reference, double reference_error, + bool normalize = false, bool check_reference_only = false) { if (normalize) { @@ -33,18 +34,31 @@ void check_one(double result, double expected, double reference, double referenc normalize_deg(reference); } - double res_max = (std::max)(bg::math::abs(result), bg::math::abs(expected)); - if (res_max > 100 * std::numeric_limits::epsilon()) + if (! check_reference_only) { - BOOST_CHECK_CLOSE(result, expected, 0.001); - } - else if (res_max > 10 * std::numeric_limits::epsilon()) - { - BOOST_CHECK_CLOSE(result, expected, 0.1); - } - else if (res_max > std::numeric_limits::epsilon()) - { - BOOST_CHECK_CLOSE(result, expected, 10); + double eps = std::numeric_limits::epsilon(); + double abs_result = bg::math::abs(result); + double abs_expected = bg::math::abs(expected); + double res_max = (std::max)(abs_result, abs_expected); + double res_min = (std::min)(abs_result, abs_expected); + if (res_min <= eps) // including 0 + { + bool is_close = abs_result <= 30 * eps && abs_expected <= 30 * eps; + BOOST_CHECK_MESSAGE((is_close), + std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); + } + else if (res_max > 100 * eps) + { + BOOST_CHECK_CLOSE(result, expected, 0.1); + } + else if (res_max > 10 * eps) + { + BOOST_CHECK_CLOSE(result, expected, 10); + } + else if (res_max > eps) + { + BOOST_CHECK_CLOSE(result, expected, 1000); + } } // NOTE: in some cases it probably will be necessary to normalize @@ -52,8 +66,7 @@ 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(16) << "{" << result << "} and {" << reference << "} not close enough."); + BOOST_CHECK_MESSAGE((is_ref_close), std::setprecision(20) << "result {" << result << "} and reference {" << reference << "} not close enough."); } #endif // BOOST_GEOMETRY_TEST_FORMULA_HPP diff --git a/test/strategies/Jamfile.v2 b/test/strategies/Jamfile.v2 index 9fd350c39..2d7442d3f 100644 --- a/test/strategies/Jamfile.v2 +++ b/test/strategies/Jamfile.v2 @@ -30,6 +30,7 @@ test-suite boost-geometry-strategies [ run pythagoras_point_box.cpp : : : : strategies_pythagoras_point_box ] [ run segment_envelope.cpp : : : : strategies_segment_envelope ] [ run segment_intersection_collinear.cpp : : : : strategies_segment_intersection_collinear ] + [ run segment_intersection_geo.cpp : : : : strategies_segment_intersection_geo ] [ run segment_intersection_sph.cpp : : : : strategies_segment_intersection_sph ] [ run side_of_intersection.cpp : : : : strategies_side_of_intersection ] [ run spherical_side.cpp : : : : strategies_spherical_side ] diff --git a/test/strategies/segment_intersection_geo.cpp b/test/strategies/segment_intersection_geo.cpp new file mode 100644 index 000000000..23c1c0d7e --- /dev/null +++ b/test/strategies/segment_intersection_geo.cpp @@ -0,0 +1,364 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + + +#include "segment_intersection_geo.hpp" + + +template +void test_geographic() +{ + typedef bg::model::point > point_t; + typedef bg::model::segment segment_t; + + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -2, -1 -1)", 'a', "POINT(-1 -1)"); + test_all_strategies( + "SEGMENT(-1 -2, 1 1)", "SEGMENT(-1 -2, -1 -1)", 'f', "POINT(-1 -2)"); + test_all_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(-1 -2, -1 -1)", 't', "POINT(-1 -1)"); + test_all_strategies( + "SEGMENT(1 1, -1 -2)", "SEGMENT(-1 -2, -1 -1)", 'a', "POINT(-1 -2)"); + test_all_strategies( + "SEGMENT(-1 -2, -1 -1)", "SEGMENT(-1 -1, 1 1)", 'a', "POINT(-1 -1)"); + test_all_strategies( + "SEGMENT(-1 -2, -1 -1)", "SEGMENT(-1 -2, 1 1)", 'f', "POINT(-1 -2)"); + test_all_strategies( + "SEGMENT(-1 -2, -1 -1)", "SEGMENT(1 1, -1 -1)", 't', "POINT(-1 -1)"); + test_all_strategies( + "SEGMENT(-1 -2, -1 -1)", "SEGMENT(1 1, -1 -2)", 'a', "POINT(-1 -2)"); + + test_strategies( + "SEGMENT(-1 -2, -1 2)", "SEGMENT(-2 -2, 2 2)", + great_elliptic('i', "POINT(-1 -1.000457053724121)"), + geodesic_vincenty('i', "POINT(-1 -1.000459099991114)"), + geodesic_andoyer('i', "POINT(-1 -1.000453510849886)")); + test_strategies( + "SEGMENT(-2 -2, 2 2)", "SEGMENT(-1 -2, -1 2)", + great_elliptic('i', "POINT(-1 -1.000457053724121)"), + geodesic_vincenty('i', "POINT(-1 -1.000459099991114)"), + geodesic_andoyer('i', "POINT(-1 -1.000453510849886)")); + + // crossing X + test_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 45, 45 -45)", + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0 -0.0000000019543337739)"), + geodesic_andoyer('i', "POINT(0 0.0003512140328446071)")); + test_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 -45, -45 45)", + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0 -0.0000000019543337739)"), + geodesic_andoyer('i', "POINT(0 0.0003512140328446071)")); + test_strategies( + "SEGMENT(45 45, -45 -45)", "SEGMENT(-45 45, 45 -45)", + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0 -0.0000000019543337739)"), + geodesic_andoyer('i', "POINT(0 0.0003512140328446071)")); + test_strategies( + "SEGMENT(45 45, -45 -45)", "SEGMENT(45 -45, -45 45)", + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0 -0.0000000019543337739)"), + geodesic_andoyer('i', "POINT(0 0.0003512140328446071)")); + + // crossing X + test_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 1, 1 -1)", + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.000000000000000596354 -0.0000000000626626779)"), + geodesic_andoyer('i', "POINT(-0.000000000000000596354 0.0000055787431347552)")); + test_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 -1, -1 1)", + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.000000000000000596354 -0.0000000000626626779)"), + geodesic_andoyer('i', "POINT(-0.000000000000000596354 0.0000055787431347552)")); + test_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(-1 1, 1 -1)", + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.000000000000000596354 -0.0000000000626626779)"), + geodesic_andoyer('i', "POINT(-0.000000000000000596354 0.0000055787431347552)")); + test_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(1 -1, -1 1)", + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.000000000000000596354 -0.0000000000626626779)"), + geodesic_andoyer('i', "POINT(-0.000000000000000596354 0.0000055787431347552)")); + + // equal + // // + test_all_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 -45, 45 45)", 'e', "POINT(-45 -45)", "POINT(45 45)", false); + // // + test_all_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 45, -45 -45)", 'e', "POINT(-45 -45)", "POINT(45 45)", true); + + // starting outside s1 + // / + // | + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -1 -1)", 'a', "POINT(-1 -1)"); + // / + // /| + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 0 0)", 'm', "POINT(0 0)"); + // /| + // / | + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 1 1)", 't', "POINT(1 1)"); + // |/ + // /| + test_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 2 2)", + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.00000013628420059 0.00000013624239008)"), + geodesic_thomas('i', "POINT(-0.00004079969079346 -0.00004078714535240)"), + geodesic_andoyer('i', "POINT(-0.01217344899138908 -0.01216980051876599)")); + // ------ + // ------ + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -1 0)", 'a', "POINT(-1 0)"); + // ------ + // ------ + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 0 0)", 'c', "POINT(-1 0)", "POINT(0 0)", false); + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, -2 0)", 'c', "POINT(-1 0)", "POINT(0 0)", true); + test_all_strategies( + "SEGMENT(1 0, -1 0)", "SEGMENT(-2 0, 0 0)", 'c', "POINT(0 0)", "POINT(-1 0)", true); + test_all_strategies( + "SEGMENT(1 0, -1 0)", "SEGMENT(0 0, -2 0)", 'c', "POINT(0 0)", "POINT(-1 0)", false); + // ------ + // --------- + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 1 0)", 'c', "POINT(-1 0)", "POINT(1 0)", false); + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, -2 0)", 'c', "POINT(-1 0)", "POINT(1 0)", true); + test_all_strategies( + "SEGMENT(1 0, -1 0)", "SEGMENT(-2 0, 1 0)", 'c', "POINT(1 0)", "POINT(-1 0)", true); + test_all_strategies( + "SEGMENT(1 0, -1 0)", "SEGMENT(1 0, -2 0)", 'c', "POINT(1 0)", "POINT(-1 0)", false); + // ------ + // ------------ + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 2 0)", 'c', "POINT(-1 0)", "POINT(1 0)", false); + + // starting at s1 + // / + // // + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 0 0)", 'c', "POINT(-1 -1)", "POINT(0 0)", false); + // // + // // + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 1 1)", 'e', "POINT(-1 -1)", "POINT(1 1)", false); + // | / + // |/ + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 2 2)", 'f', "POINT(-1 -1)"); + // ------ + // --- + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 0 0)", 'c', "POINT(-1 0)", "POINT(0 0)", false); + // ------ + // ------ + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 1 0)", 'e', "POINT(-1 0)", "POINT(1 0)", false); + // ------ + // --------- + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 2 0)", 'c', "POINT(-1 0)", "POINT(1 0)", false); + + // starting inside + // // + // / + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 1 1)", 'c', "POINT(0 0)", "POINT(1 1)", false); + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 0 0)", 'c', "POINT(0 0)", "POINT(1 1)", true); + test_all_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(0 0, 1 1)", 'c', "POINT(1 1)", "POINT(0 0)", true); + test_all_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(1 1, 0 0)", 'c', "POINT(1 1)", "POINT(0 0)", false); + test_all_strategies( + "SEGMENT(0 0, 1 1)", "SEGMENT(-1 -1, 1 1)", 'c', "POINT(0 0)", "POINT(1 1)", false); + test_all_strategies( + "SEGMENT(1 1, 0 0)", "SEGMENT(-1 -1, 1 1)", 'c', "POINT(1 1)", "POINT(0 0)", true); + test_all_strategies( + "SEGMENT(0 0, 1 1)", "SEGMENT(1 1, -1 -1)", 'c', "POINT(0 0)", "POINT(1 1)", true); + test_all_strategies( + "SEGMENT(1 1, 0 0)", "SEGMENT(1 1, -1 -1)", 'c', "POINT(1 1)", "POINT(0 0)", false); + // |/ + // / + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 2 2)", 's', "POINT(0 0)"); + // ------ + // --- + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 1 0)", 'c', "POINT(0 0)", "POINT(1 0)", false); + // ------ + // ------ + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 2 0)", 'c', "POINT(0 0)", "POINT(1 0)", false); + + // starting at p2 + // | + // / + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 2 2)", 'a', "POINT(1 1)"); + // ------ + // --- + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 2 0)", 'a', "POINT(1 0)"); + + // disjoint, crossing + // / + // | + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-3 -3, -2 -2)", 'd'); + // | + // / + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 3 3)", 'd'); + // disjoint, collinear + // ------ + // ------ + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-3 0, -2 0)", 'd'); + // ------ + // ------ + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 3 0)", 'd'); + + // degenerated + // / + // * + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -2 -2)", 'd'); + // / + // * + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, -1 -1)", '0', "POINT(-1 -1)"); + // / + // * + // / + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 0 0)", '0', "POINT(0 0)"); + // * + // / + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 1 1)", '0', "POINT(1 1)"); + // * + // / + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 2 2)", 'd'); + // similar to above, collinear + // * ------ + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -2 0)", 'd'); + // *------ + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, -1 0)", '0', "POINT(-1 0)"); + // ---*--- + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 0 0)", '0', "POINT(0 0)"); + // ------* + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 1 0)", '0', "POINT(1 0)"); + // ------ * + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 2 0)", 'd'); + + // Northern hemisphere + // --- ------ + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-3 50, -2 50)", 'd'); + // ------ + // --- + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, -1 50)", 'a', "POINT(-1 50)"); + // \/ + // /\ (avoid multi-line comment) + test_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 0 50)", + great_elliptic('i', "POINT(-0.5 50.0032229484023)"), + geodesic_vincenty('i', "POINT(-0.4999999996073994 50.00323192256208)"), + geodesic_thomas('i', "POINT(-0.4999999963998482 50.00323192258598)"), + geodesic_andoyer('i', "POINT(-0.4999990340391772 50.00323192463806)")); + // ________ + // / _____\ (avoid multi-line comment) + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 1 50)", 't', "POINT(1 50)"); + // _________ + // / _____ \ (avoid multi-line comment) + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 2 50)", 'd'); + // ______ + // /___ \ (avoid multi-line comment) + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 0 50)", 'f', "POINT(-1 50)"); + // ------ + // ------ + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 1 50)", 'e', "POINT(-1 50)", "POINT(1 50)", false); + // ________ + // /_____ \ (avoid multi-line comment) + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 2 50)", 'f', "POINT(-1 50)"); + // ______ + // / ___\ (avoid multi-line comment) + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 1 50)", 't', "POINT(1 50)"); + // \/ + // /\ (avoid multi-line comment) + test_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 2 50)", + great_elliptic('i', "POINT(0.5 50.0032229484023)"), + geodesic_vincenty('i', "POINT(0.4999999996112415 50.00323192256208)"), + geodesic_thomas('i', "POINT(0.5000000051005989 50.00323192258598)"), + geodesic_andoyer('i', "POINT(0.5000009655139563 50.00323192463806)")); + // ------ + // --- + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(1 50, 2 50)", 'a', "POINT(1 50)"); + // ------ --- + test_all_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(2 50, 3 50)", 'd'); + + // ___| + test_all_strategies( + "SEGMENT(0 0, 1 0)", "SEGMENT(1 0, 1 1)", 'a', "POINT(1 0)"); + // ___| + test_all_strategies( + "SEGMENT(1 0, 1 1)", "SEGMENT(0 0, 1 0)", 'a', "POINT(1 0)"); + + // |/ + // /| + test_strategies( + "SEGMENT(10 -1, 20 1)", "SEGMENT(12.5 -1, 12.5 1)", + great_elliptic('i', "POINT(12.5 -0.50051443471392)"), + geodesic_vincenty('i', "POINT(12.5 -0.5005177749487734)"), + geodesic_thomas('i', "POINT(12.5 -0.5005177654827411)"), + geodesic_andoyer('i', "POINT(12.5 -0.500525380045163)")); + // |/ + // /| + test_strategies( + "SEGMENT(10 -1, 20 1)", "SEGMENT(17.5 -1, 17.5 1)", + great_elliptic('i', "POINT(17.5 0.50051443471392)"), + geodesic_vincenty('i', "POINT(17.5 0.5005177748229335)"), + geodesic_thomas('i', "POINT(17.5 0.5005178030678186)"), + geodesic_andoyer('i', "POINT(17.5 0.5004949944844279)")); +} + +int test_main(int, char* []) +{ + //test_geographic(); + test_geographic(); + + return 0; +} diff --git a/test/strategies/segment_intersection_geo.hpp b/test/strategies/segment_intersection_geo.hpp new file mode 100644 index 000000000..38350d691 --- /dev/null +++ b/test/strategies/segment_intersection_geo.hpp @@ -0,0 +1,289 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2016, 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_TEST_STRATEGIES_SEGMENT_INTERSECTION_GEO_HPP +#define BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_GEO_HPP + + +#include "segment_intersection_sph.hpp" + + +#include +#include +#include + +#include +#include + + +template +void test_default_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) +{ + typename bg::strategy::intersection::services::default_strategy + < + bg::geographic_tag, + void + >::type strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); +} + +template +void test_great_elliptic(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) +{ + bg::strategy::intersection::relate_great_elliptic_segments<> strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); +} +/* +template +void test_experimental_elliptic(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) +{ + bg::strategy::intersection::relate_experimental_elliptic_segments<> strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); +} +*/ +template +void test_geodesic_vincenty(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) +{ + bg::strategy::intersection::relate_geodesic_segments + < + bg::srs::spheroid, + bg::formula::vincenty_inverse, + 4 + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); +} + +template +void test_geodesic_thomas(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) +{ + bg::strategy::intersection::relate_geodesic_segments + < + bg::srs::spheroid, + bg::formula::thomas_inverse, + 2 + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); +} + +template +void test_geodesic_andoyer(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) +{ + bg::strategy::intersection::relate_geodesic_segments + < + bg::srs::spheroid, + bg::formula::andoyer_inverse, + 1 + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); +} + + +struct strategy_base +{ + strategy_base(char m_) + : m(m_), expected_count(0), opposite(-1) + {} + strategy_base(char m_, std::string const& wkt1_) + : m(m_), expected_count(1), wkt1(wkt1_), opposite(-1) + {} + strategy_base(char m_, std::string const& wkt1_, std::string const& wkt2_, bool opposite_) + : m(m_), expected_count(1), wkt1(wkt1_), opposite(opposite_ ? 1 : 0) + {} + + char m; + std::size_t expected_count; + std::string wkt1, wkt2; + int opposite; +}; +struct strategy_default : strategy_base +{ + strategy_default(char m) + : strategy_base(m) + {} + strategy_default(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + strategy_default(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) + {} +}; +struct geodesic_vincenty : strategy_base +{ + geodesic_vincenty(char m) + : strategy_base(m) + {} + geodesic_vincenty(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + geodesic_vincenty(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) + {} +}; +struct geodesic_thomas : strategy_base +{ + geodesic_thomas(char m) + : strategy_base(m) + {} + geodesic_thomas(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + geodesic_thomas(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) + {} +}; +struct geodesic_andoyer : strategy_base +{ + geodesic_andoyer(char m) + : strategy_base(m) + {} + geodesic_andoyer(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + geodesic_andoyer(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) + {} +}; +struct great_elliptic : strategy_base +{ + great_elliptic(char m) + : strategy_base(m) + {} + great_elliptic(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + great_elliptic(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) + {} +}; + + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + strategy_default const& s) +{ + test_default_strategy(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + great_elliptic const& s) +{ + test_great_elliptic(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + geodesic_vincenty const& s) +{ + test_geodesic_vincenty(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + geodesic_thomas const& s) +{ + test_geodesic_thomas(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + geodesic_andoyer const& s) +{ + test_geodesic_andoyer(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + + +template +void test_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + SR1 const& sr1) +{ + test_strategy(s1_wkt, s2_wkt, sr1); +} +template +void test_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + SR1 const& sr1, SR2 const& sr2) +{ + test_strategy(s1_wkt, s2_wkt, sr1); + test_strategy(s1_wkt, s2_wkt, sr2); +} +template +void test_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + SR1 const& sr1, SR2 const& sr2, SR3 const& sr3) +{ + test_strategy(s1_wkt, s2_wkt, sr1); + test_strategy(s1_wkt, s2_wkt, sr2); + test_strategy(s1_wkt, s2_wkt, sr3); +} +template +void test_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + SR1 const& sr1, SR2 const& sr2, SR3 const& sr3, SR4 const& sr4) +{ + test_strategy(s1_wkt, s2_wkt, sr1); + test_strategy(s1_wkt, s2_wkt, sr2); + test_strategy(s1_wkt, s2_wkt, sr3); + test_strategy(s1_wkt, s2_wkt, sr4); +} + + +template +void test_all_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::string const& ip0_wkt = "") +{ + std::size_t expected_count = ip0_wkt.empty() ? 0 : 1; + + test_default_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + test_great_elliptic(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + //test_experimental_elliptic(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + test_geodesic_vincenty(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + test_geodesic_thomas(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + test_geodesic_andoyer(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); +} + +template +void test_all_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + char m, + std::string const& ip0_wkt, std::string const& ip1_wkt, + bool opposite) +{ + int opposite_id = opposite ? 1 : 0; + + test_default_strategy(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + test_great_elliptic(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + //test_experimental_elliptic(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + test_geodesic_vincenty(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + test_geodesic_thomas(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + test_geodesic_andoyer(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); +} + +#endif // BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_GEO_HPP diff --git a/test/strategies/segment_intersection_sph.cpp b/test/strategies/segment_intersection_sph.cpp index 7073696fd..47980f992 100644 --- a/test/strategies/segment_intersection_sph.cpp +++ b/test/strategies/segment_intersection_sph.cpp @@ -9,151 +9,21 @@ // http://www.boost.org/LICENSE_1_0.txt) -#include +#define BOOST_GEOMETRY_TEST_GEO_INTERSECTION_TEST_SIMILAR +#include "segment_intersection_sph.hpp" -#include - -#include -#include - -#include -#include - -#include -#include -#include #include -template -bool equals_relaxed(P1 const& p1, P2 const& p2, T const& eps_scale) -{ - typedef typename bg::select_coordinate_type::type calc_t; - calc_t c1 = 1; - calc_t p10 = bg::get<0>(p1); - calc_t p11 = bg::get<1>(p1); - calc_t p20 = bg::get<0>(p2); - calc_t p21 = bg::get<1>(p2); - calc_t relaxed_eps = std::numeric_limits::epsilon() - * bg::math::detail::greatest(c1, bg::math::abs(p10), bg::math::abs(p11), bg::math::abs(p20), bg::math::abs(p21)) - * eps_scale; - calc_t diff0 = p10 - p20; - // needed e.g. for -179.999999 - 180.0 - if (diff0 < -180) - diff0 += 360; - return bg::math::abs(diff0) <= relaxed_eps - && bg::math::abs(p11 - p21) <= relaxed_eps; -} - -template -void test_spherical_strategy_one(S1 const& s1, S2 const& s2, char m, std::size_t expected_count, P const& ip0 = P(), P const& ip1 = P()) -{ - typedef typename bg::coordinate_type

    ::type coord_t; - typedef bg::policies::relate::segments_tupled - < - bg::policies::relate::segments_intersection_points - < - bg::segment_intersection_points > - >, - bg::policies::relate::segments_direction - > policy_t; - typedef bg::strategy::intersection::relate_spherical_segments<> strategy_t; - typedef typename policy_t::return_type return_type; - - // NOTE: robust policy is currently ignored - return_type res = strategy_t::apply(s1, s2, policy_t(), 0); - - size_t const res_count = boost::get<0>(res).count; - char const res_method = boost::get<1>(res).how; - - BOOST_CHECK_MESSAGE(res_method == m, - "IP method: " << res_method << " different than expected: " << m - << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); - - BOOST_CHECK_MESSAGE(res_count == expected_count, - "IP count: " << res_count << " different than expected: " << expected_count - << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); - - // The EPS is scaled because during the conversion various angles may be not converted - // to cartesian 3d the same way which results in a different intersection point - // For more information read the info in spherical intersection strategy file. - - int eps_scale = res_method != 'i' ? 1 : 1000; - - if (res_count > 0 && expected_count > 0) - { - P const& res_i0 = boost::get<0>(res).intersections[0]; - BOOST_CHECK_MESSAGE(equals_relaxed(res_i0, ip0, eps_scale), - "IP0: " << std::setprecision(16) << bg::wkt(res_i0) << " different than expected: " << bg::wkt(ip0) - << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); - } - if (res_count > 1 && expected_count > 1) - { - P const& res_i1 = boost::get<0>(res).intersections[1]; - BOOST_CHECK_MESSAGE(equals_relaxed(res_i1, ip1, eps_scale), - "IP1: " << std::setprecision(16) << bg::wkt(res_i1) << " different than expected: " << bg::wkt(ip1) - << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); - } -} - -template -T translated(T v, double t) -{ - v += T(t); - if (v > 180) - v -= 360; - return v; -} - -template -void test_spherical_strategy(S1 const& s1, S2 const& s2, char m, std::size_t expected_count, P const& ip0 = P(), P const& ip1 = P()) -{ - S1 s1t = s1; - S2 s2t = s2; - P ip0t = ip0; - P ip1t = ip1; - - double t = 0; - for (int i = 0; i < 4; ++i, t += 90) - { - bg::set<0, 0>(s1t, translated(bg::get<0, 0>(s1), t)); - bg::set<1, 0>(s1t, translated(bg::get<1, 0>(s1), t)); - bg::set<0, 0>(s2t, translated(bg::get<0, 0>(s2), t)); - bg::set<1, 0>(s2t, translated(bg::get<1, 0>(s2), t)); - if (expected_count > 0) - bg::set<0>(ip0t, translated(bg::get<0>(ip0), t)); - if (expected_count > 1) - bg::set<0>(ip1t, translated(bg::get<0>(ip1), t)); - - test_spherical_strategy_one(s1t, s2t, m, expected_count, ip0t, ip1t); - } -} - -template -void test_spherical_strategy(std::string const& s1_wkt, std::string const& s2_wkt, - char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") -{ - S1 s1; - S2 s2; - P ip0, ip1; - - bg::read_wkt(s1_wkt, s1); - bg::read_wkt(s2_wkt, s2); - if (! ip0_wkt.empty()) - bg::read_wkt(ip0_wkt, ip0); - if (!ip1_wkt.empty()) - bg::read_wkt(ip1_wkt, ip1); - - test_spherical_strategy(s1, s2, m, expected_count, ip0, ip1); -} template void test_spherical_strategy(std::string const& s1_wkt, std::string const& s2_wkt, char m, std::size_t expected_count, std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") { - test_spherical_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + bg::strategy::intersection::relate_spherical_segments<> strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); } template diff --git a/test/strategies/segment_intersection_sph.hpp b/test/strategies/segment_intersection_sph.hpp new file mode 100644 index 000000000..31f00b1e8 --- /dev/null +++ b/test/strategies/segment_intersection_sph.hpp @@ -0,0 +1,205 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2016, 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_TEST_STRATEGIES_SEGMENT_INTERSECTION_SPH_HPP +#define BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_SPH_HPP + + +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include + +template +bool equals_relaxed_val(T const& v1, T const& v2, T const& eps_scale) +{ + T const c1 = 1; + T relaxed_eps = std::numeric_limits::epsilon() + * bg::math::detail::greatest(c1, bg::math::abs(v1), bg::math::abs(v2)) + * eps_scale; + return bg::math::abs(v1 - v2) <= relaxed_eps; +} + +template +bool equals_relaxed(P1 const& p1, P2 const& p2, T const& eps_scale) +{ + typedef typename bg::select_coordinate_type::type calc_t; + calc_t c1 = 1; + calc_t p10 = bg::get<0>(p1); + calc_t p11 = bg::get<1>(p1); + calc_t p20 = bg::get<0>(p2); + calc_t p21 = bg::get<1>(p2); + calc_t relaxed_eps = std::numeric_limits::epsilon() + * bg::math::detail::greatest(c1, bg::math::abs(p10), bg::math::abs(p11), bg::math::abs(p20), bg::math::abs(p21)) + * eps_scale; + calc_t diff0 = p10 - p20; + // needed e.g. for -179.999999 - 180.0 + if (diff0 < -180) + diff0 += 360; + return bg::math::abs(diff0) <= relaxed_eps + && bg::math::abs(p11 - p21) <= relaxed_eps; +} + +template +void test_strategy_one(S1 const& s1, S2 const& s2, + Strategy const& strategy, + char m, std::size_t expected_count, + P const& ip0 = P(), P const& ip1 = P(), + int opposite_id = -1) +{ + typedef typename bg::coordinate_type

    ::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + typedef typename policy_t::return_type return_type; + + // NOTE: robust policy is currently ignored + return_type res = strategy.apply(s1, s2, policy_t(), 0); + + size_t const res_count = boost::get<0>(res).count; + char const res_method = boost::get<1>(res).how; + + BOOST_CHECK_MESSAGE(res_method == m, + "IP method: " << res_method << " different than expected: " << m + << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); + + BOOST_CHECK_MESSAGE(res_count == expected_count, + "IP count: " << res_count << " different than expected: " << expected_count + << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); + + // The EPS is scaled because during the conversion various angles may be not converted + // to cartesian 3d the same way which results in a different intersection point + // For more information read the info in spherical intersection strategy file. + + // Plus in geographic CS the result strongly depends on the compiler, + // probably due to differences in FP trigonometric function implementations + + int eps_scale = 1; + bool is_geographic = boost::is_same::type, bg::geographic_tag>::value; + if (is_geographic) + { + eps_scale = 100000; + } + else + { + eps_scale = res_method != 'i' ? 1 : 1000; + } + + if (res_count > 0 && expected_count > 0) + { + P const& res_i0 = boost::get<0>(res).intersections[0]; + BOOST_CHECK_MESSAGE(equals_relaxed(res_i0, ip0, eps_scale), + "IP0: " << std::setprecision(16) << bg::wkt(res_i0) << " different than expected: " << bg::wkt(ip0) + << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); + } + if (res_count > 1 && expected_count > 1) + { + P const& res_i1 = boost::get<0>(res).intersections[1]; + BOOST_CHECK_MESSAGE(equals_relaxed(res_i1, ip1, eps_scale), + "IP1: " << std::setprecision(16) << bg::wkt(res_i1) << " different than expected: " << bg::wkt(ip1) + << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); + } + + if (opposite_id >= 0) + { + bool opposite = opposite_id != 0; + BOOST_CHECK_MESSAGE(opposite == boost::get<1>(res).opposite, + bg::wkt(s1) << " and " << bg::wkt(s2) << (opposite_id == 0 ? " are not " : " are ") << "opposite" ); + } +} + +template +T translated(T v, double t) +{ + v += T(t); + if (v > 180) + v -= 360; + return v; +} + +template +void test_strategy(S1 const& s1, S2 const& s2, + Strategy const& strategy, + char m, std::size_t expected_count, + P const& ip0 = P(), P const& ip1 = P(), + int opposite_id = -1) +{ + S1 s1t = s1; + S2 s2t = s2; + P ip0t = ip0; + P ip1t = ip1; + +#ifndef BOOST_GEOMETRY_TEST_GEO_INTERSECTION_TEST_SIMILAR + test_strategy_one(s1t, s2t, strategy, m, expected_count, ip0t, ip1t, opposite_id); +#else + double t = 0; + for (int i = 0; i < 4; ++i, t += 90) + { + bg::set<0, 0>(s1t, translated(bg::get<0, 0>(s1), t)); + bg::set<1, 0>(s1t, translated(bg::get<1, 0>(s1), t)); + bg::set<0, 0>(s2t, translated(bg::get<0, 0>(s2), t)); + bg::set<1, 0>(s2t, translated(bg::get<1, 0>(s2), t)); + if (expected_count > 0) + bg::set<0>(ip0t, translated(bg::get<0>(ip0), t)); + if (expected_count > 1) + bg::set<0>(ip1t, translated(bg::get<0>(ip1), t)); + + test_strategy_one(s1t, s2t, strategy, m, expected_count, ip0t, ip1t, opposite_id); + } +#endif +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + Strategy const& strategy, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) +{ + S1 s1; + S2 s2; + P ip0, ip1; + + bg::read_wkt(s1_wkt, s1); + bg::read_wkt(s2_wkt, s2); + if (! ip0_wkt.empty()) + bg::read_wkt(ip0_wkt, ip0); + if (!ip1_wkt.empty()) + bg::read_wkt(ip1_wkt, ip1); + + test_strategy(s1, s2, strategy, m, expected_count, ip0, ip1, opposite_id); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + Strategy const& strategy, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) +{ + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); +} + +#endif // BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_SPH_HPP