diff --git a/Jamfile.v2 b/Jamfile.v2 new file mode 100644 index 000000000..4f318205e --- /dev/null +++ b/Jamfile.v2 @@ -0,0 +1,22 @@ +# Boost.Geometry (aka GGL, Generic Geometry Library) +# +# Copyright (c) 2007-2013 Barend Gehrels, Amsterdam, the Netherlands. +# Copyright (c) 2008-2013 Bruno Lalande, Paris, France. +# Copyright (c) 2009-2013 Mateusz Loskot, London, UK. +# +# Use, modification and distribution is subject to the Boost Software License, +# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +project boost-geometry + : + requirements + ../../boost/geometry/extensions/contrib/ttmath + msvc:on + ; + +build-project test ; +build-project example ; +build-project doc/src/examples ; +build-project extensions ; +build-project index ; diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 1deb3fd94..924d4786f 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -8,18 +8,15 @@ # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) - project geometry/doc ; # Auto-index, experimental. Commented otherwise does not build without. #using auto-index ; - using quickbook ; path-constant here : . ; path-constant images_location : html ; - boostbook geometry : geometry.qbk : Jamfile.v2 @@ -43,3 +40,4 @@ boostbook geometry install pdfinstall : geometry/pdf : . geometry.pdf PDF ; explicit pdfinstall ; + diff --git a/doc/compiling.qbk b/doc/compiling.qbk index f549fabd0..01c99fb74 100644 --- a/doc/compiling.qbk +++ b/doc/compiling.qbk @@ -49,7 +49,8 @@ compilers: * gcc 4.2.1 [/reported by Trunk report May 8, 2011] * gcc 3.4.6 [/reported by Trunk report March 26, 2011] * clang - * clang x.x [/reported by Trunk report March 26, 2011] + * clang 3.3 [/reported by mloskot on October, 2013] + * clang 3.2 [/reported by Trunk report March 26, 2011] * darwin * darwin 4.0.1 [/reported by Trunk report March 26, 2011] * darwin 4.4 [/reported by Trunk report March 26, 2011] @@ -86,17 +87,24 @@ Another often used header is `geometries.hpp`: #include -This includes definitions of all provided geometry types: point, -linestring, polygon, ring, box. The file `geometries.hpp` is not included in +This includes definitions of all provided geometry types: + +* point, +* linestring, +* polygon, +* ring, +* multi_point, +* multi_linestring, +* multi_polygon, +* box, +* segment. + +The file `geometries.hpp` is not included in the `geometry.hpp` headerfile because users should be given the liberty to use their own geometries and not the provided ones. However, for the __boost_geometry__ users who want to use the provided geometries it is useful to include. -For users using multi-geometries: - - #include - [heading Advanced Includes] Users who have their own geometries and want to use algorithms from diff --git a/doc/doxy/Doxyfile b/doc/doxy/Doxyfile index 2cd86526b..e3b1ee1be 100644 --- a/doc/doxy/Doxyfile +++ b/doc/doxy/Doxyfile @@ -57,7 +57,6 @@ ALIASES = qbk{1}="\xmlonly \1 \endxmlonly" \ tparam_radius="numeric type for radius (of sphere, earth)" \ tparam_container="container type, for example std::vector, std::deque" \ tparam_dimension_required="Dimension, this template parameter is required. Should contain \\[0 .. n-1\\] for an n-dimensional geometry" \ - tparam_first_point="first point type" \ tparam_functor="Function or class with operator()" \ tparam_output_collection="output collection, either a multi-geometry, or a std::vector / std::deque etc" \ tparam_geometry="Any type fulfilling a Geometry Concept" \ @@ -67,8 +66,10 @@ ALIASES = qbk{1}="\xmlonly \1 \endxmlonly" \ tparam_out{1}="A valid output iterator type, accepting geometries of \1 Concept" \ tparam_point="Any type fulfilling a Point Concept" \ tparam_range_point="Any type fulfilling a Range Concept where it range_value type fulfills the Point Concept" \ - tparam_first_point="point type" \ + tparam_first_point="first point type" \ + tparam_first_box="first box type" \ tparam_second_point="second point type" \ + tparam_second_box="second box type" \ tparam_segment_point="segment point type" \ tparam_strategy{1}="Any type fulfilling a \1 Strategy Concept" \ tparam_strategy_overlay="Compound strategy for segment intersection" \ @@ -176,9 +177,14 @@ WARN_LOGFILE = INPUT = . .. ../../../../boost/geometry/core \ ../../../../boost/geometry/algorithms \ ../../../../boost/geometry/algorithms/detail \ - ../../../../boost/geometry/algorithms/detail/overlay \ + ../../../../boost/geometry/algorithms/detail/disjoint \ + ../../../../boost/geometry/algorithms/detail/distance \ ../../../../boost/geometry/algorithms/detail/equals \ + ../../../../boost/geometry/algorithms/detail/overlay \ + ../../../../boost/geometry/algorithms/detail/relate \ ../../../../boost/geometry/algorithms/detail/sections \ + ../../../../boost/geometry/algorithms/detail/turns \ + ../../../../boost/geometry/algorithms/detail/within \ ../../../../boost/geometry/arithmetic \ ../../../../boost/geometry/geometries/concepts \ ../../../../boost/geometry/geometries \ @@ -233,7 +239,7 @@ FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- -SOURCE_BROWSER = YES +SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO @@ -250,7 +256,7 @@ IGNORE_PREFIX = # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES -HTML_OUTPUT = html +HTML_OUTPUT = html_by_doxygen HTML_FILE_EXTENSION = .html HTML_HEADER = doxygen_input/ggl_doxygen_header.html HTML_FOOTER = doxygen_input/ggl_doxygen_footer.html diff --git a/doc/doxy/doxygen_input/groups/groups.hpp b/doc/doxy/doxygen_input/groups/groups.hpp index 6128a44e8..cb6cc3337 100644 --- a/doc/doxy/doxygen_input/groups/groups.hpp +++ b/doc/doxy/doxygen_input/groups/groups.hpp @@ -24,6 +24,7 @@ \defgroup core core: meta-functions for geometry types \defgroup correct correct: correct geometries \defgroup covered_by covered_by: detect if a geometry is inside or on the border of another geometry, a.o. point-in-polygon (border included) +\defgroup crosses crosses: detect if two geometries crosses each other \defgroup cs coordinate systems \defgroup difference difference: difference of two geometries \defgroup disjoint disjoint: detect if geometries are not spatially related diff --git a/doc/doxy/doxygen_output/html/doxygen.css b/doc/doxy/doxygen_output/html/doxygen.css deleted file mode 100644 index 4f78a1bf0..000000000 --- a/doc/doxy/doxygen_output/html/doxygen.css +++ /dev/null @@ -1,1184 +0,0 @@ -/* The standard CSS for doxygen 1.8.3.1-20130324 */ - -body, table, div, p, dl { - font: 400 14px/19px Roboto,sans-serif; -} - -/* @group Heading Levels */ - -h1.groupheader { - font-size: 150%; -} - -.title { - font-size: 150%; - font-weight: bold; - margin: 10px 2px; -} - -h2.groupheader { - border-bottom: 1px solid #879ECB; - color: #354C7B; - font-size: 150%; - font-weight: normal; - margin-top: 1.75em; - padding-top: 8px; - padding-bottom: 4px; - width: 100%; -} - -h3.groupheader { - font-size: 100%; -} - -h1, h2, h3, h4, h5, h6 { - -webkit-transition: text-shadow 0.5s linear; - -moz-transition: text-shadow 0.5s linear; - -ms-transition: text-shadow 0.5s linear; - -o-transition: text-shadow 0.5s linear; - transition: text-shadow 0.5s linear; - margin-right: 15px; -} - -h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { - text-shadow: 0 0 15px cyan; -} - -dt { - font-weight: bold; -} - -div.multicol { - -moz-column-gap: 1em; - -webkit-column-gap: 1em; - -moz-column-count: 3; - -webkit-column-count: 3; -} - -p.startli, p.startdd, p.starttd { - margin-top: 2px; -} - -p.endli { - margin-bottom: 0px; -} - -p.enddd { - margin-bottom: 4px; -} - -p.endtd { - margin-bottom: 2px; -} - -/* @end */ - -caption { - font-weight: bold; -} - -span.legend { - font-size: 70%; - text-align: center; -} - -h3.version { - font-size: 90%; - text-align: center; -} - -div.qindex, div.navtab{ - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; -} - -div.qindex, div.navpath { - width: 100%; - line-height: 140%; -} - -div.navtab { - margin-right: 15px; -} - -/* @group Link Styling */ - -a { - color: #3D578C; - font-weight: normal; - text-decoration: none; -} - -.contents a:visited { - color: #4665A2; -} - -a:hover { - text-decoration: underline; -} - -a.qindex { - font-weight: bold; -} - -a.qindexHL { - font-weight: bold; - background-color: #9CAFD4; - color: #ffffff; - border: 1px double #869DCA; -} - -.contents a.qindexHL:visited { - color: #ffffff; -} - -a.el { - font-weight: bold; -} - -a.elRef { -} - -a.code, a.code:visited { - color: #4665A2; -} - -a.codeRef, a.codeRef:visited { - color: #4665A2; -} - -/* @end */ - -dl.el { - margin-left: -1cm; -} - -pre.fragment { - border: 1px solid #C4CFE5; - background-color: #FBFCFD; - padding: 4px 6px; - margin: 4px 8px 4px 2px; - overflow: auto; - word-wrap: break-word; - font-size: 9pt; - line-height: 125%; - font-family: monospace, fixed; - font-size: 105%; -} - -div.fragment { - padding: 4px; - margin: 4px; - background-color: #FBFCFD; - border: 1px solid #C4CFE5; -} - -div.line { - font-family: monospace, fixed; - font-size: 13px; - min-height: 13px; - line-height: 1.0; - text-wrap: unrestricted; - white-space: -moz-pre-wrap; /* Moz */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - white-space: pre-wrap; /* CSS3 */ - word-wrap: break-word; /* IE 5.5+ */ - text-indent: -53px; - padding-left: 53px; - padding-bottom: 0px; - margin: 0px; - -webkit-transition-property: background-color, box-shadow; - -webkit-transition-duration: 0.5s; - -moz-transition-property: background-color, box-shadow; - -moz-transition-duration: 0.5s; - -ms-transition-property: background-color, box-shadow; - -ms-transition-duration: 0.5s; - -o-transition-property: background-color, box-shadow; - -o-transition-duration: 0.5s; - transition-property: background-color, box-shadow; - transition-duration: 0.5s; -} - -div.line.glow { - background-color: cyan; - box-shadow: 0 0 10px cyan; -} - - -span.lineno { - padding-right: 4px; - text-align: right; - border-right: 2px solid #0F0; - background-color: #E8E8E8; - white-space: pre; -} -span.lineno a { - background-color: #D8D8D8; -} - -span.lineno a:hover { - background-color: #C8C8C8; -} - -div.ah { - background-color: black; - font-weight: bold; - color: #ffffff; - margin-bottom: 3px; - margin-top: 3px; - padding: 0.2em; - border: solid thin #333; - border-radius: 0.5em; - -webkit-border-radius: .5em; - -moz-border-radius: .5em; - box-shadow: 2px 2px 3px #999; - -webkit-box-shadow: 2px 2px 3px #999; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); - background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); -} - -div.groupHeader { - margin-left: 16px; - margin-top: 12px; - font-weight: bold; -} - -div.groupText { - margin-left: 16px; - font-style: italic; -} - -body { - background-color: white; - color: black; - margin: 0; -} - -div.contents { - margin-top: 10px; - margin-left: 12px; - margin-right: 8px; -} - -td.indexkey { - background-color: #EBEFF6; - font-weight: bold; - border: 1px solid #C4CFE5; - margin: 2px 0px 2px 0; - padding: 2px 10px; - white-space: nowrap; - vertical-align: top; -} - -td.indexvalue { - background-color: #EBEFF6; - border: 1px solid #C4CFE5; - padding: 2px 10px; - margin: 2px 0px; -} - -tr.memlist { - background-color: #EEF1F7; -} - -p.formulaDsp { - text-align: center; -} - -img.formulaDsp { - -} - -img.formulaInl { - vertical-align: middle; -} - -div.center { - text-align: center; - margin-top: 0px; - margin-bottom: 0px; - padding: 0px; -} - -div.center img { - border: 0px; -} - -address.footer { - text-align: right; - padding-right: 12px; -} - -img.footer { - border: 0px; - vertical-align: middle; -} - -/* @group Code Colorization */ - -span.keyword { - color: #008000 -} - -span.keywordtype { - color: #604020 -} - -span.keywordflow { - color: #e08000 -} - -span.comment { - color: #800000 -} - -span.preprocessor { - color: #806020 -} - -span.stringliteral { - color: #002080 -} - -span.charliteral { - color: #008080 -} - -span.vhdldigit { - color: #ff00ff -} - -span.vhdlchar { - color: #000000 -} - -span.vhdlkeyword { - color: #700070 -} - -span.vhdllogic { - color: #ff0000 -} - -blockquote { - background-color: #F7F8FB; - border-left: 2px solid #9CAFD4; - margin: 0 24px 0 4px; - padding: 0 12px 0 16px; -} - -/* @end */ - -/* -.search { - color: #003399; - font-weight: bold; -} - -form.search { - margin-bottom: 0px; - margin-top: 0px; -} - -input.search { - font-size: 75%; - color: #000080; - font-weight: normal; - background-color: #e8eef2; -} -*/ - -td.tiny { - font-size: 75%; -} - -.dirtab { - padding: 4px; - border-collapse: collapse; - border: 1px solid #A3B4D7; -} - -th.dirtab { - background: #EBEFF6; - font-weight: bold; -} - -hr { - height: 0px; - border: none; - border-top: 1px solid #4A6AAA; -} - -hr.footer { - height: 1px; -} - -/* @group Member Descriptions */ - -table.memberdecls { - border-spacing: 0px; - padding: 0px; -} - -.memberdecls td, .fieldtable tr { - -webkit-transition-property: background-color, box-shadow; - -webkit-transition-duration: 0.5s; - -moz-transition-property: background-color, box-shadow; - -moz-transition-duration: 0.5s; - -ms-transition-property: background-color, box-shadow; - -ms-transition-duration: 0.5s; - -o-transition-property: background-color, box-shadow; - -o-transition-duration: 0.5s; - transition-property: background-color, box-shadow; - transition-duration: 0.5s; -} - -.memberdecls td.glow, .fieldtable tr.glow { - background-color: cyan; - box-shadow: 0 0 15px cyan; -} - -.mdescLeft, .mdescRight, -.memItemLeft, .memItemRight, -.memTemplItemLeft, .memTemplItemRight, .memTemplParams { - background-color: #F9FAFC; - border: none; - margin: 4px; - padding: 1px 0 0 8px; -} - -.mdescLeft, .mdescRight { - padding: 0px 8px 4px 8px; - color: #555; -} - -.memSeparator { - border-bottom: 1px solid #DEE4F0; - line-height: 1px; - margin: 0px; - padding: 0px; -} - -.memItemLeft, .memTemplItemLeft { - white-space: nowrap; -} - -.memItemRight { - width: 100%; -} - -.memTemplParams { - color: #4665A2; - white-space: nowrap; - font-size: 80%; -} - -/* @end */ - -/* @group Member Details */ - -/* Styles for detailed member documentation */ - -.memtemplate { - font-size: 80%; - color: #4665A2; - font-weight: normal; - margin-left: 9px; -} - -.memnav { - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; - margin: 2px; - margin-right: 15px; - padding: 2px; -} - -.mempage { - width: 100%; -} - -.memitem { - padding: 0; - margin-bottom: 10px; - margin-right: 5px; - -webkit-transition: box-shadow 0.5s linear; - -moz-transition: box-shadow 0.5s linear; - -ms-transition: box-shadow 0.5s linear; - -o-transition: box-shadow 0.5s linear; - transition: box-shadow 0.5s linear; - display: table !important; - width: 100%; -} - -.memitem.glow { - box-shadow: 0 0 15px cyan; -} - -.memname { - font-weight: bold; - margin-left: 6px; -} - -.memname td { - vertical-align: bottom; -} - -.memproto, dl.reflist dt { - border-top: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 6px 0px 6px 0px; - color: #253555; - font-weight: bold; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - background-image:url('nav_f.png'); - background-repeat:repeat-x; - background-color: #E2E8F2; - /* opera specific markup */ - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - border-top-right-radius: 4px; - border-top-left-radius: 4px; - /* firefox specific markup */ - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - -moz-border-radius-topright: 4px; - -moz-border-radius-topleft: 4px; - /* webkit specific markup */ - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - -webkit-border-top-right-radius: 4px; - -webkit-border-top-left-radius: 4px; - -} - -.memdoc, dl.reflist dd { - border-bottom: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 6px 10px 2px 10px; - background-color: #FBFCFD; - border-top-width: 0; - background-image:url('nav_g.png'); - background-repeat:repeat-x; - background-color: #FFFFFF; - /* opera specific markup */ - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - /* firefox specific markup */ - -moz-border-radius-bottomleft: 4px; - -moz-border-radius-bottomright: 4px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - /* webkit specific markup */ - -webkit-border-bottom-left-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); -} - -dl.reflist dt { - padding: 5px; -} - -dl.reflist dd { - margin: 0px 0px 10px 0px; - padding: 5px; -} - -.paramkey { - text-align: right; -} - -.paramtype { - white-space: nowrap; -} - -.paramname { - color: #602020; - white-space: nowrap; -} -.paramname em { - font-style: normal; -} -.paramname code { - line-height: 14px; -} - -.params, .retval, .exception, .tparams { - margin-left: 0px; - padding-left: 0px; -} - -.params .paramname, .retval .paramname { - font-weight: bold; - vertical-align: top; -} - -.params .paramtype { - font-style: italic; - vertical-align: top; -} - -.params .paramdir { - font-family: "courier new",courier,monospace; - vertical-align: top; -} - -table.mlabels { - border-spacing: 0px; -} - -td.mlabels-left { - width: 100%; - padding: 0px; -} - -td.mlabels-right { - vertical-align: bottom; - padding: 0px; - white-space: nowrap; -} - -span.mlabels { - margin-left: 8px; -} - -span.mlabel { - background-color: #728DC1; - border-top:1px solid #5373B4; - border-left:1px solid #5373B4; - border-right:1px solid #C4CFE5; - border-bottom:1px solid #C4CFE5; - text-shadow: none; - color: white; - margin-right: 4px; - padding: 2px 3px; - border-radius: 3px; - font-size: 7pt; - white-space: nowrap; - vertical-align: middle; -} - - - -/* @end */ - -/* these are for tree view when not used as main index */ - -div.directory { - margin: 10px 0px; - border-top: 1px solid #A8B8D9; - border-bottom: 1px solid #A8B8D9; - width: 100%; -} - -.directory table { - border-collapse:collapse; -} - -.directory td { - margin: 0px; - padding: 0px; - vertical-align: top; -} - -.directory td.entry { - white-space: nowrap; - padding-right: 6px; -} - -.directory td.entry a { - outline:none; -} - -.directory td.entry a img { - border: none; -} - -.directory td.desc { - width: 100%; - padding-left: 6px; - padding-right: 6px; - padding-top: 3px; - border-left: 1px solid rgba(0,0,0,0.05); -} - -.directory tr.even { - padding-left: 6px; - background-color: #F7F8FB; -} - -.directory img { - vertical-align: -30%; -} - -.directory .levels { - white-space: nowrap; - width: 100%; - text-align: right; - font-size: 9pt; -} - -.directory .levels span { - cursor: pointer; - padding-left: 2px; - padding-right: 2px; - color: #3D578C; -} - -div.dynheader { - margin-top: 8px; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -address { - font-style: normal; - color: #2A3D61; -} - -table.doxtable { - border-collapse:collapse; - margin-top: 4px; - margin-bottom: 4px; -} - -table.doxtable td, table.doxtable th { - border: 1px solid #2D4068; - padding: 3px 7px 2px; -} - -table.doxtable th { - background-color: #374F7F; - color: #FFFFFF; - font-size: 110%; - padding-bottom: 4px; - padding-top: 5px; -} - -table.fieldtable { - /*width: 100%;*/ - margin-bottom: 10px; - border: 1px solid #A8B8D9; - border-spacing: 0px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); - box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); -} - -.fieldtable td, .fieldtable th { - padding: 3px 7px 2px; -} - -.fieldtable td.fieldtype, .fieldtable td.fieldname { - white-space: nowrap; - border-right: 1px solid #A8B8D9; - border-bottom: 1px solid #A8B8D9; - vertical-align: top; -} - -.fieldtable td.fieldname { - padding-top: 3px; -} - -.fieldtable td.fielddoc { - border-bottom: 1px solid #A8B8D9; - /*width: 100%;*/ -} - -.fieldtable td.fielddoc p:first-child { - margin-top: 0px; -} - -.fieldtable td.fielddoc p:last-child { - margin-bottom: 2px; -} - -.fieldtable tr:last-child td { - border-bottom: none; -} - -.fieldtable th { - background-image:url('nav_f.png'); - background-repeat:repeat-x; - background-color: #E2E8F2; - font-size: 90%; - color: #253555; - padding-bottom: 4px; - padding-top: 5px; - text-align:left; - -moz-border-radius-topleft: 4px; - -moz-border-radius-topright: 4px; - -webkit-border-top-left-radius: 4px; - -webkit-border-top-right-radius: 4px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom: 1px solid #A8B8D9; -} - - -.tabsearch { - top: 0px; - left: 10px; - height: 36px; - background-image: url('tab_b.png'); - z-index: 101; - overflow: hidden; - font-size: 13px; -} - -.navpath ul -{ - font-size: 11px; - background-image:url('tab_b.png'); - background-repeat:repeat-x; - background-position: 0 -5px; - height:30px; - line-height:30px; - color:#8AA0CC; - border:solid 1px #C2CDE4; - overflow:hidden; - margin:0px; - padding:0px; -} - -.navpath li -{ - list-style-type:none; - float:left; - padding-left:10px; - padding-right:15px; - background-image:url('bc_s.png'); - background-repeat:no-repeat; - background-position:right; - color:#364D7C; -} - -.navpath li.navelem a -{ - height:32px; - display:block; - text-decoration: none; - outline: none; - color: #283A5D; - font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - text-decoration: none; -} - -.navpath li.navelem a:hover -{ - color:#6884BD; -} - -.navpath li.footer -{ - list-style-type:none; - float:right; - padding-left:10px; - padding-right:15px; - background-image:none; - background-repeat:no-repeat; - background-position:right; - color:#364D7C; - font-size: 8pt; -} - - -div.summary -{ - float: right; - font-size: 8pt; - padding-right: 5px; - width: 50%; - text-align: right; -} - -div.summary a -{ - white-space: nowrap; -} - -div.ingroups -{ - font-size: 8pt; - width: 50%; - text-align: left; -} - -div.ingroups a -{ - white-space: nowrap; -} - -div.header -{ - background-image:url('nav_h.png'); - background-repeat:repeat-x; - background-color: #F9FAFC; - margin: 0px; - border-bottom: 1px solid #C4CFE5; -} - -div.headertitle -{ - padding: 5px 5px 5px 10px; -} - -dl -{ - padding: 0 0 0 10px; -} - -/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ -dl.section -{ - margin-left: 0px; - padding-left: 0px; -} - -dl.note -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #D0C000; -} - -dl.warning, dl.attention -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #FF0000; -} - -dl.pre, dl.post, dl.invariant -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #00D000; -} - -dl.deprecated -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #505050; -} - -dl.todo -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #00C0E0; -} - -dl.test -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #3030E0; -} - -dl.bug -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #C08050; -} - -dl.section dd { - margin-bottom: 6px; -} - - -#projectlogo -{ - text-align: center; - vertical-align: bottom; - border-collapse: separate; -} - -#projectlogo img -{ - border: 0px none; -} - -#projectname -{ - font: 300% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 2px 0px; -} - -#projectbrief -{ - font: 120% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; -} - -#projectnumber -{ - font: 50% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; -} - -#titlearea -{ - padding: 0px; - margin: 0px; - width: 100%; - border-bottom: 1px solid #5373B4; -} - -.image -{ - text-align: center; -} - -.dotgraph -{ - text-align: center; -} - -.mscgraph -{ - text-align: center; -} - -.caption -{ - font-weight: bold; -} - -div.zoom -{ - border: 1px solid #90A5CE; -} - -dl.citelist { - margin-bottom:50px; -} - -dl.citelist dt { - color:#334975; - float:left; - font-weight:bold; - margin-right:10px; - padding:5px; -} - -dl.citelist dd { - margin:2px 0; - padding:5px 0; -} - -div.toc { - padding: 14px 25px; - background-color: #F4F6FA; - border: 1px solid #D8DFEE; - border-radius: 7px 7px 7px 7px; - float: right; - height: auto; - margin: 0 20px 10px 10px; - width: 200px; -} - -div.toc li { - background: url("bdwn.png") no-repeat scroll 0 5px transparent; - font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; - margin-top: 5px; - padding-left: 10px; - padding-top: 2px; -} - -div.toc h3 { - font: bold 12px/1.2 Arial,FreeSans,sans-serif; - color: #4665A2; - border-bottom: 0 none; - margin: 0; -} - -div.toc ul { - list-style: none outside none; - border: medium none; - padding: 0px; -} - -div.toc li.level1 { - margin-left: 0px; -} - -div.toc li.level2 { - margin-left: 15px; -} - -div.toc li.level3 { - margin-left: 30px; -} - -div.toc li.level4 { - margin-left: 45px; -} - -.inherit_header { - font-weight: bold; - color: gray; - cursor: pointer; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.inherit_header td { - padding: 6px 0px 2px 5px; -} - -.inherit { - display: none; -} - -tr.heading h2 { - margin-top: 12px; - margin-bottom: 4px; -} - -@media print -{ - #top { display: none; } - #side-nav { display: none; } - #nav-path { display: none; } - body { overflow:visible; } - h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } - .summary { display: none; } - .memitem { page-break-inside: avoid; } - #doc-content - { - margin-left:0 !important; - height:auto !important; - width:auto !important; - overflow:inherit; - display:inline; - } -} - diff --git a/doc/doxy/doxygen_output/html/doxygen.png b/doc/doxy/doxygen_output/html/doxygen.png deleted file mode 100644 index 3ff17d807..000000000 Binary files a/doc/doxy/doxygen_output/html/doxygen.png and /dev/null differ diff --git a/doc/doxy/doxygen_output/html/tabs.css b/doc/doxy/doxygen_output/html/tabs.css deleted file mode 100644 index 9cf578f23..000000000 --- a/doc/doxy/doxygen_output/html/tabs.css +++ /dev/null @@ -1,60 +0,0 @@ -.tabs, .tabs2, .tabs3 { - background-image: url('tab_b.png'); - width: 100%; - z-index: 101; - font-size: 13px; - font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; -} - -.tabs2 { - font-size: 10px; -} -.tabs3 { - font-size: 9px; -} - -.tablist { - margin: 0; - padding: 0; - display: table; -} - -.tablist li { - float: left; - display: table-cell; - background-image: url('tab_b.png'); - line-height: 36px; - list-style: none; -} - -.tablist a { - display: block; - padding: 0 20px; - font-weight: bold; - background-image:url('tab_s.png'); - background-repeat:no-repeat; - background-position:right; - color: #283A5D; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - text-decoration: none; - outline: none; -} - -.tabs3 .tablist a { - padding: 0 10px; -} - -.tablist a:hover { - background-image: url('tab_h.png'); - background-repeat:repeat-x; - color: #fff; - text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); - text-decoration: none; -} - -.tablist li.current a { - background-image: url('tab_a.png'); - background-repeat:repeat-x; - color: #fff; - text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); -} diff --git a/doc/generated/covered_by_status.qbk b/doc/generated/covered_by_status.qbk deleted file mode 100644 index 2843670d5..000000000 --- a/doc/generated/covered_by_status.qbk +++ /dev/null @@ -1,13 +0,0 @@ -[heading Supported geometries] -[table -[[ ][Point][Segment][Box][Linestring][Ring][Polygon][MultiPoint][MultiLinestring][MultiPolygon]] -[[Point][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Segment][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Box][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Linestring][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Ring][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Polygon][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[MultiPoint][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[MultiLinestring][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[MultiPolygon][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -] diff --git a/doc/generated/disjoint_status.qbk b/doc/generated/disjoint_status.qbk deleted file mode 100644 index d7d9e977e..000000000 --- a/doc/generated/disjoint_status.qbk +++ /dev/null @@ -1,13 +0,0 @@ -[heading Supported geometries] -[table -[[ ][Point][Segment][Box][Linestring][Ring][Polygon][MultiPoint][MultiLinestring][MultiPolygon]] -[[Point][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ]] -[[Segment][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ]] -[[Box][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ]] -[[Linestring][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ]] -[[Ring][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ]] -[[Polygon][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ]] -[[MultiPoint][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ]] -[[MultiLinestring][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ]] -[[MultiPolygon][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ][ [$img/ok.png] ]] -] diff --git a/doc/generated/overlaps_status.qbk b/doc/generated/overlaps_status.qbk deleted file mode 100644 index 6caf4c90c..000000000 --- a/doc/generated/overlaps_status.qbk +++ /dev/null @@ -1,13 +0,0 @@ -[heading Supported geometries] -[table -[[ ][Point][Segment][Box][Linestring][Ring][Polygon][MultiPoint][MultiLinestring][MultiPolygon]] -[[Point][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Segment][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Box][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Linestring][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Ring][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Polygon][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[MultiPoint][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[MultiLinestring][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[MultiPolygon][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -] diff --git a/doc/generated/within_status.qbk b/doc/generated/within_status.qbk deleted file mode 100644 index 2843670d5..000000000 --- a/doc/generated/within_status.qbk +++ /dev/null @@ -1,13 +0,0 @@ -[heading Supported geometries] -[table -[[ ][Point][Segment][Box][Linestring][Ring][Polygon][MultiPoint][MultiLinestring][MultiPolygon]] -[[Point][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Segment][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Box][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Linestring][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Ring][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[Polygon][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[MultiPoint][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[MultiLinestring][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -[[MultiPolygon][ [$img/ok.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ][ [$img/nyi.png] ]] -] diff --git a/doc/geometry.qbk b/doc/geometry.qbk index 72935105c..214636142 100644 --- a/doc/geometry.qbk +++ b/doc/geometry.qbk @@ -12,8 +12,8 @@ [library Geometry [quickbook 1.5] - [authors [Gehrels, Barend], [Lalande, Bruno], [Loskot, Mateusz], [Wulkiewicz, Adam]] - [copyright 2009-2013 Barend Gehrels, Bruno Lalande, Mateusz Loskot, Adam Wulkiewicz] + [authors [Gehrels, Barend], [Lalande, Bruno], [Loskot, Mateusz], [Wulkiewicz, Adam], [Karavelas, Menelaos]] + [copyright 2009-2014 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. @@ -113,6 +113,7 @@ Boost.Geometry contains contributions by: * Federico Fern\u00E1ndez (preliminary version of R-tree spatial index) * Karsten Ahnert (patch for cross-track distance) * Mats Taraldsvik (documentation: adapting a legacy model) +* Samuel Debionne (variant support for distance, assign, crosses, intersection, ...) [include imports.qbk] @@ -141,5 +142,6 @@ Boost.Geometry contains contributions by: [include release_notes.qbk] +[/ TODO: [include guidelines.qbk] /] [include about_documentation.qbk] [include acknowledgments.qbk] diff --git a/doc/guidelines.qbk b/doc/guidelines.qbk new file mode 100644 index 000000000..e2e3fd75a --- /dev/null +++ b/doc/guidelines.qbk @@ -0,0 +1,232 @@ +[/============================================================================ + Boost.Geometry (aka GGL, Generic Geometry Library) + + Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + Copyright (c) 2008-2012 Bruno Lalande, Paris, France. + Copyright (c) 2009-2013 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) + +=============================================================================/] + +[section:guidelines Guidelines for developers] + +This library is maintained by several developers, and in order it to have +a consistent design, look and feel, a few guidelines need to be followed. + +Rules of [@boost:/development/requirements.html Boost Library Requirements and Guidelines] +and [@boost:/development/header.html Boost Header Policy] always have highest authority. + +Generally, prefer style of modern C++, conventions as used in latest C++ standard +document and C++ Standard Library. Boost.Spirit is a good example of +how to write and format high quality C++ code. + +Some guidelines specific to Boost.Geometry library are outlined below. + +[heading Code structure] + +* Every file shall have header with copyright and license information. +* Do not put any history or revision information in comments in source files. + Log it with VCS used in the Boost project. +* Every header shall have `#include` guard based on header path and file name: +`` +#ifndef BOOST_GEOMETRY____HPP +#define BOOST_GEOMETRY____HPP +... +#endif // BOOST_GEOMETRY____HPP +`` +* `#include` directives shall be ordered according the most authoritative header: + * C Standard Library (using C++ library names, i.e. ``) + * C++ Standard Library + * Boost C++ Libraries + * Boost.Geometry headers + * Other 3rd-party headers (only if applicable! in some samples only) +* Header within these sections should be ordered alphabetically, especially if there are many of them included. +* Namespaces don't increase the level of indentation. + In all other cases braces increase the level of indentation. +`` +namespace boost { namespace geometry +{ + +namespace mynewspace +{ + +template +struct my_new_model +{ + typedef point_type; +} + +} // namespace mynewspace + +}} // namespace boost::geometry +`` +* Namespace closing brace should have comment with the namespace name. +* All non-public headers should be placed into `boost/geometry/detail` or + `boost/geometry/*/detail` directory, depending on component level. +* All non-public names should reside in the `boost::geometry::detail` or + `boost::geometry::*::detail` namespace, depending on component level. +* All traits should be placed in dedicated `boost::geometry::traits` or + `boost::geometry::*::traits` namespace +* All tag dispatching routines should be placed in dedicated + `boost::geometry::*::dispatch` namespace. +* Access specifiers for class members shall be orderd as public first, then protected and private at the bottom. + The public members define class interface, thus they are of the highest interested for users, so show them first. + * Exceptions to this rule are allowed for typedef aliases required to be defined first. + +[heading Code formatting and indentation] + +* The code is indented with spaces, 4 spaces per tab. +* The preferred line length is 80 characters, with maximum length of 100. + * The limit is relaxed for very long string literals (e.g. Well-Known Text with data used in tests and examples). +* Member/base initialization list for constructors on the same line, + if it's small (1-2 members) or 1 member/base per line with leading comma on the left: +``` +struct T +{ + T(int a, int b) + : a(a) + , b(b) + {} + + int a; + int b; +}; +``` +* Template declaration with long template parameter list shall be formatted + with one template parameter per line, all parameters indented, + but `<` and `>` brackets not indented: +``` +template +< + typename T, + typename P, + typename C = std::vector +> +struct polygon +{ + typedef typename boost::remove_const + < + typename traits::point_type::type + >::type type +}; +``` +* References and pointers should be formatted emphasizing type, not syntax: +``` +T const& t; +T* t; +T* const t; +T const* t; +T const* const t; +``` +* Braces enclosing block of code (if-else, loops) should be placed in separate lines +``` +if (expr) +{ +} +``` +* Parentheses around expressions should not be pre/post-fixed with spaces. + +[heading Naming conventions] + +* All names follow style of the C++ Standard, lowercase with words separated with underscore `_`, + unless otherwise specified (see other rules). +* Template parameters are named in CamelCase. +* Concepts are named in CamelCase. +* Name of a class data member shall start with `m_` prefix. + The Boost sample header gives no prefix or suffix at all. + However, the `m_` prefix is used in some (not many) Boost libraries as well (e.g. math/tools/remez). +* All macro names shall be in upper-case, words separated with underscore `_`. +* All macro names shall start with `BOOST_GEOMETRY_`. +* All non-public macro names should start with `BOOST_GEOMETRY_DETAIL_` (not used often yet, if at all). +* All public names should reside in the `boost::geometry` namespace. + Nested namespaces are also possible. +* Avoid cryptic names and abbreviations for elements used in wider context (e.g. types, functions). + Short names are allowed if context of use is local, narrow and easily tracable + For example, use of `it` for `iterator` in body of a loop in function: +``` +template +static inline void apply(Range& range, Functor& f) +{ + for (typename boost::range_iterator::type it = boost::begin(range); + it != boost::end(range); ++it) + { + f(*it); + } +} +``` + +[heading C++ use conventions] + +* Keyword struct is preferred either for POD structures, or for classes used at compile-time + like metafunctions, tags, traits, etc. +* Keyword class is preferred for classes meant to produce actual objects, which have methods + and an active role in the runtime functioning of the program. +* In case of a template, prefer use of typename keyword over class. + +[heading Specialisations and dispatching conventions] + +* Algorithms are free inline functions, taking any geometry. Parameters are often one or two geometries +* There might be an overload for a strategy. The strategy takes, a.o. care of coordinate systems +* The free `inline` function forwards to a dispatch struct, specialized for the geometry type (so for point, polygon, etc.) +* They have an `static` (`inline`) function called apply +* The dispatch struct calls, or is derived from, an struct implemented in namespace detail +* There the same: a `struct` with a `static` (`inline`) function called apply +* This way the implementation structs are building blocks, they can be reused +* In fact they are reused often by the multi-versions of the algorithms + +``` +namespace boost { namespace geometry +{ + +namespace detail { namespace foo +{ + +template +struct foo_point +{ + // template parameters here + static inline int apply(Point const& p) + { + // do something here + return 1; + } +}; + +}} // namespace detail::foo + +namespace dispatch +{ + +template +< + Geometry, + Tag = typename geometry::tag::type +> +struct foo +{ +}; + +// Specialization for POINT +... + +} // namespace dispatch + +template +inline int foo(Point const& point) +{ + return dispatch::apply(point); +} + +}} // namespace boost::geometry +``` + +[heading Contributing code] + +* Create a patch, open a ticket in the Boost Trac with your patch attached. +* Alternatively, post your patch to the Boost.Geometry mailing list. +* If you contribute a code, always try to provide a minimal test for it. + +[endsect] diff --git a/doc/html/img/index/rtree/knn_box_box.png b/doc/html/img/index/rtree/knn_box_box.png new file mode 100644 index 000000000..6f70f884d Binary files /dev/null and b/doc/html/img/index/rtree/knn_box_box.png differ diff --git a/doc/html/img/index/rtree/knn_pt_box.png b/doc/html/img/index/rtree/knn_pt_box.png new file mode 100644 index 000000000..af9c14874 Binary files /dev/null and b/doc/html/img/index/rtree/knn_pt_box.png differ diff --git a/doc/html/img/index/rtree/knn_seg_box.png b/doc/html/img/index/rtree/knn_seg_box.png new file mode 100644 index 000000000..13288d1ac Binary files /dev/null and b/doc/html/img/index/rtree/knn_seg_box.png differ diff --git a/doc/html/img/index/rtree/rtree_pt.png b/doc/html/img/index/rtree/rtree_pt.png new file mode 100644 index 000000000..8c3f5807e Binary files /dev/null and b/doc/html/img/index/rtree/rtree_pt.png differ diff --git a/doc/html/img/index/rtree/rtree_pt_disjoint_box.png b/doc/html/img/index/rtree/rtree_pt_disjoint_box.png new file mode 100644 index 000000000..e82275f1a Binary files /dev/null and b/doc/html/img/index/rtree/rtree_pt_disjoint_box.png differ diff --git a/doc/html/img/index/rtree/rtree_pt_intersects_box.png b/doc/html/img/index/rtree/rtree_pt_intersects_box.png new file mode 100644 index 000000000..46baf009b Binary files /dev/null and b/doc/html/img/index/rtree/rtree_pt_intersects_box.png differ diff --git a/doc/html/img/index/rtree/rtree_pt_knn_box.png b/doc/html/img/index/rtree/rtree_pt_knn_box.png new file mode 100644 index 000000000..237de694c Binary files /dev/null and b/doc/html/img/index/rtree/rtree_pt_knn_box.png differ diff --git a/doc/html/img/index/rtree/rtree_pt_knn_pt.png b/doc/html/img/index/rtree/rtree_pt_knn_pt.png new file mode 100644 index 000000000..55d0a2d38 Binary files /dev/null and b/doc/html/img/index/rtree/rtree_pt_knn_pt.png differ diff --git a/doc/html/img/index/rtree/rtree_pt_knn_seg.png b/doc/html/img/index/rtree/rtree_pt_knn_seg.png new file mode 100644 index 000000000..fc3e13811 Binary files /dev/null and b/doc/html/img/index/rtree/rtree_pt_knn_seg.png differ diff --git a/doc/html/img/index/rtree/rtree_seg.png b/doc/html/img/index/rtree/rtree_seg.png new file mode 100644 index 000000000..080433318 Binary files /dev/null and b/doc/html/img/index/rtree/rtree_seg.png differ diff --git a/doc/html/img/index/rtree/rtree_seg_disjoint_box.png b/doc/html/img/index/rtree/rtree_seg_disjoint_box.png new file mode 100644 index 000000000..72cc1e71a Binary files /dev/null and b/doc/html/img/index/rtree/rtree_seg_disjoint_box.png differ diff --git a/doc/html/img/index/rtree/rtree_seg_intersects_box.png b/doc/html/img/index/rtree/rtree_seg_intersects_box.png new file mode 100644 index 000000000..b6135eeda Binary files /dev/null and b/doc/html/img/index/rtree/rtree_seg_intersects_box.png differ diff --git a/doc/html/img/index/rtree/rtree_seg_knn_box.png b/doc/html/img/index/rtree/rtree_seg_knn_box.png new file mode 100644 index 000000000..251117688 Binary files /dev/null and b/doc/html/img/index/rtree/rtree_seg_knn_box.png differ diff --git a/doc/html/img/index/rtree/rtree_seg_knn_pt.png b/doc/html/img/index/rtree/rtree_seg_knn_pt.png new file mode 100644 index 000000000..8def78502 Binary files /dev/null and b/doc/html/img/index/rtree/rtree_seg_knn_pt.png differ diff --git a/doc/html/img/index/rtree/rtree_seg_knn_seg.png b/doc/html/img/index/rtree/rtree_seg_knn_seg.png new file mode 100644 index 000000000..68891d685 Binary files /dev/null and b/doc/html/img/index/rtree/rtree_seg_knn_seg.png differ diff --git a/doc/html/index.html b/doc/html/index.html index 5699ea120..9719e84fa 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -34,7 +34,11 @@

Adam Wulkiewicz

-
+

+Menelaos Karavelas +

+

Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -123,7 +127,7 @@

- +

Last revised: June 23, 2013 at 18:26:39 GMT

Last revised: May 07, 2014 at 10:12:25 GMT


diff --git a/doc/imports.qbk b/doc/imports.qbk index 8ef073ab1..3d4706f2e 100644 --- a/doc/imports.qbk +++ b/doc/imports.qbk @@ -105,6 +105,8 @@ [import src/examples/geometries/register/multi_polygon.cpp] [import src/examples/io/svg.cpp] +[import src/examples/io/wkt.cpp] +[import src/examples/io/read_wkt.cpp] [import src/examples/views/box_view.cpp] [import src/examples/views/segment_view.cpp] diff --git a/doc/index/generated/dummy b/doc/index/generated/dummy new file mode 100644 index 000000000..6da9b3dcb --- /dev/null +++ b/doc/index/generated/dummy @@ -0,0 +1 @@ +This file is here because GIT doesn't handle empty directories diff --git a/doc/index/imports.qbk b/doc/index/imports.qbk index 5d81a7639..45a613f75 100644 --- a/doc/index/imports.qbk +++ b/doc/index/imports.qbk @@ -15,3 +15,4 @@ [import src/examples/rtree/value_shared_ptr.cpp] [import src/examples/rtree/value_index.cpp] [import src/examples/rtree/interprocess.cpp] +[import src/examples/rtree/mapped_file.cpp] diff --git a/doc/index/introduction.qbk b/doc/index/introduction.qbk index cb7bcf1d3..8e20ff903 100644 --- a/doc/index/introduction.qbk +++ b/doc/index/introduction.qbk @@ -51,7 +51,7 @@ The examples of structures of trees created by use of different algorithms and e [table [[] [Linear algorithm] [Quadratic algorithm] [R*-tree] [Packing algorithm]] [[*Example structure*] [[$img/index/rtree/linear.png]] [[$img/index/rtree/quadratic.png]] [[$img/index/rtree/rstar.png]] [[$img/index/rtree/bulk.png]]] -[[*1M Values inserts*] [1.76s] [2.47s] [6.19s] [1.67s]] +[[*1M Values inserts*] [1.76s] [2.47s] [6.19s] [0.64s]] [[*100k spatial queries*] [2.21s] [0.51s] [0.12s] [0.07s]] [[*100k knn queries*] [6.37s] [2.09s] [0.64s] [0.52s]] ] diff --git a/doc/index/make_qbk.py b/doc/index/make_qbk.py index 9185f3f10..bff630488 100755 --- a/doc/index/make_qbk.py +++ b/doc/index/make_qbk.py @@ -17,23 +17,34 @@ cmd = cmd + " --start_include boost/" cmd = cmd + " --output_style alt" cmd = cmd + " > generated/%s.qbk" -os.system("doxygen Doxyfile") -os.system(cmd % ("classboost_1_1geometry_1_1index_1_1rtree", "rtree")) -os.system(cmd % ("group__rtree__functions", "rtree_functions")) +def run_command(command): + if os.system(command) != 0: + raise Exception("Error running %s" % command) -os.system(cmd % ("structboost_1_1geometry_1_1index_1_1linear", "rtree_linear")) -os.system(cmd % ("structboost_1_1geometry_1_1index_1_1quadratic", "rtree_quadratic")) -os.system(cmd % ("structboost_1_1geometry_1_1index_1_1rstar", "rtree_rstar")) -os.system(cmd % ("classboost_1_1geometry_1_1index_1_1dynamic__linear", "rtree_dynamic_linear")) -os.system(cmd % ("classboost_1_1geometry_1_1index_1_1dynamic__quadratic", "rtree_dynamic_quadratic")) -os.system(cmd % ("classboost_1_1geometry_1_1index_1_1dynamic__rstar", "rtree_dynamic_rstar")) +def remove_all_files(dir): + if os.path.exists(dir): + for f in os.listdir(dir): + os.remove(dir+f) -os.system(cmd % ("structboost_1_1geometry_1_1index_1_1indexable", "indexable")) -os.system(cmd % ("structboost_1_1geometry_1_1index_1_1equal__to", "equal_to")) +remove_all_files("xml/") -os.system(cmd % ("group__predicates", "predicates")) -#os.system(cmd % ("group__nearest__relations", "nearest_relations")) -os.system(cmd % ("group__adaptors", "adaptors")) -os.system(cmd % ("group__inserters", "inserters")) +run_command("doxygen Doxyfile") +run_command(cmd % ("classboost_1_1geometry_1_1index_1_1rtree", "rtree")) +run_command(cmd % ("group__rtree__functions", "rtree_functions")) -#os.system("b2") +run_command(cmd % ("structboost_1_1geometry_1_1index_1_1linear", "rtree_linear")) +run_command(cmd % ("structboost_1_1geometry_1_1index_1_1quadratic", "rtree_quadratic")) +run_command(cmd % ("structboost_1_1geometry_1_1index_1_1rstar", "rtree_rstar")) +run_command(cmd % ("classboost_1_1geometry_1_1index_1_1dynamic__linear", "rtree_dynamic_linear")) +run_command(cmd % ("classboost_1_1geometry_1_1index_1_1dynamic__quadratic", "rtree_dynamic_quadratic")) +run_command(cmd % ("classboost_1_1geometry_1_1index_1_1dynamic__rstar", "rtree_dynamic_rstar")) + +run_command(cmd % ("structboost_1_1geometry_1_1index_1_1indexable", "indexable")) +run_command(cmd % ("structboost_1_1geometry_1_1index_1_1equal__to", "equal_to")) + +run_command(cmd % ("group__predicates", "predicates")) +#run_command(cmd % ("group__nearest__relations", "nearest_relations")) +run_command(cmd % ("group__adaptors", "adaptors")) +run_command(cmd % ("group__inserters", "inserters")) + +#run_command("b2") diff --git a/doc/index/rtree/creation.qbk b/doc/index/rtree/creation.qbk index 0cb5d998b..6ea609b08 100644 --- a/doc/index/rtree/creation.qbk +++ b/doc/index/rtree/creation.qbk @@ -30,13 +30,20 @@ __rtree__ has 5 parameters but only 2 are required: __rtree__ may store `__value__`s of any type as long as passed function objects know how to interpret those `__value__`s, that is extract an `__indexable__` that the __rtree__ can handle and compare `__value__`s. +The `__indexable__` is a type adapted to Point, Box or Segment concept. +The examples of rtrees storing `__value__`s translatable to various `__indexable__`s are presented below. + +[table +[[rtree] [rtree] [rtree]] +[[[$img/index/rtree/rtree_pt.png]] [[$img/index/rtree/rstar.png]] [[$img/index/rtree/rtree_seg.png]]] +] By default function objects `index::indexable` and `index::equal_to` are defined for some typically used `__value__` types which may be stored without defining any additional classes. By default the rtree may store pure `__indexable__`s, pairs and tuples. In the case of those two collection types, the `__indexable__` must be the first stored type. -* `__indexable__ = __point__ | __box__` -* `__value__ = Indexable | std::pair<__indexable__, T> | tuple<__indexable__, ...>` +* `__indexable__ = __point__ | __box__ | Segment` +* `__value__ = Indexable | std::pair<__indexable__, T> | boost::tuple<__indexable__, ...> [ | std::tuple<__indexable__, ...> ]` By default `boost::tuple<...>` is supported on all compilers. If the compiler supports C++11 tuples and variadic templates then `std::tuple<...>` may be used "out of the box" as well. @@ -46,6 +53,7 @@ Examples of default `__value__` types: geometry::model::point<...> geometry::model::point_xy<...> geometry::model::box<...> + geometry::model::segment<...> std::pair, unsigned> boost::tuple, int, float> @@ -55,7 +63,7 @@ The predefined `index::indexable` returns const reference to the `__index The predefined `index::equal_to`: -* for `__point__` and `__box__` - compares `__value__`s with geometry::equals(). +* for `__point__`, `__box__` and `Segment` - compares `__value__`s with geometry::equals(). * for `std::pair<...>` - compares both components of the `__value__`. The first value stored in the pair is compared before the second one. If the value stored in the pair is a Geometry, `geometry::equals()` is used. For other types it uses `operator==()`. * for `tuple<...>` - compares all components of the `__value__`. If the component is a `Geometry`, `geometry::equals()` diff --git a/doc/index/rtree/examples.qbk b/doc/index/rtree/examples.qbk index 8b57bde7e..24d54d466 100644 --- a/doc/index/rtree/examples.qbk +++ b/doc/index/rtree/examples.qbk @@ -12,30 +12,51 @@ [section Quick start] [rtree_quickstart] +[h4 Expected results] +[include ../src/examples/rtree/quick_start_results.qbk] [endsect] [section Index of polygons stored in vector] [rtree_polygons_vector] +[h4 Expected results] +[include ../src/examples/rtree/polygons_vector_results.qbk] [endsect] [section Index of shared pointers to polygons] [rtree_polygons_shared_ptr] +[h4 Expected results] +[include ../src/examples/rtree/polygons_shared_ptr_results.qbk] [endsect] [section Index of iterators of a map storing variant geometries] [rtree_variants_map] +[h4 Expected results] +[include ../src/examples/rtree/variants_map_results.qbk] [endsect] [section Specializing index::indexable function object - storing shared pointers in the rtree] [rtree_value_shared_ptr] +[h4 Expected results] +[include ../src/examples/rtree/value_shared_ptr_results.qbk] [endsect] [section Using IndexableGetter function object - storing indexes of external container's elements] [rtree_value_index] +[h4 Expected results] +[include ../src/examples/rtree/value_index_results.qbk] [endsect] [section Index stored in shared memory using Boost.Interprocess] [rtree_interprocess] +[h4 Expected results] +[include ../src/examples/rtree/interprocess_results.qbk] [endsect] +[section Index stored in mapped file using Boost.Interprocess] +[rtree_mapped_file] +[h4 Expected results] +[include ../src/examples/rtree/mapped_file_results.qbk] +[endsect] + + [endsect] diff --git a/doc/index/rtree/query.qbk b/doc/index/rtree/query.qbk index 7f10db949..0cf80acc7 100644 --- a/doc/index/rtree/query.qbk +++ b/doc/index/rtree/query.qbk @@ -73,6 +73,11 @@ Examples of some basic queries may be found in the tables below. The query regio [[[$img/index/rtree/intersects_ring.png]] [[$img/index/rtree/intersects_poly.png]] [[$img/index/rtree/intersects_mpoly.png]] [[$img/index/rtree/intersects_segment.png]] [[$img/index/rtree/intersects_linestring.png]]] ] +[table +[[intersects(Box)] [disjoint(Box)] [intersects(Box)] [disjoint(Box)]] +[[[$img/index/rtree/rtree_pt_intersects_box.png]] [[$img/index/rtree/rtree_pt_disjoint_box.png]] [[$img/index/rtree/rtree_seg_intersects_box.png]] [[$img/index/rtree/rtree_seg_disjoint_box.png]]] +] + Spatial predicates are generated by functions defined in `boost::geometry::index` namespace. rt.query(index::contains(box), std::back_inserter(result)); @@ -91,20 +96,39 @@ All spatial predicates may be negated, e.g.: [h4 Nearest neighbours queries] -Nearest neighbours queries returns `__value__`s which are closest to some point in space. -The example of knn query is presented below. 5 `__value__`s nearest to the point are orange. +Nearest neighbours queries returns `__value__`s which are closest to some Geometry. +The examples of k-NN queries are presented below. 5 `__value__`s nearest to the Geometry are orange. -[$img/index/rtree/knn.png] +[table +[[nearest(Point, k)] [nearest(Box, k)] [nearest(Point, k)] [nearest(Box, k)]] +[[[$img/index/rtree/knn_pt_box.png]] [[$img/index/rtree/knn_box_box.png]] [[$img/index/rtree/rtree_pt_knn_pt.png]] [[$img/index/rtree/rtree_pt_knn_box.png]]] +] +[table +[[nearest(Segment, k)] + [nearest(Point, k)] [nearest(Box, k)] [nearest(Segment, k)] + [nearest(Segment, k)]] +[[[$img/index/rtree/knn_seg_box.png]] + [[$img/index/rtree/rtree_seg_knn_pt.png]] [[$img/index/rtree/rtree_seg_knn_box.png]] [[$img/index/rtree/rtree_seg_knn_seg.png]] + [[$img/index/rtree/rtree_pt_knn_seg.png]]] +] To perform the knn query one must pass the nearest predicate generated by the `nearest()` function defined in `boost::geometry::index` namespace. -The following query returns `k` `__value__`s closest to some point in space. -For non-point `__indexable__`s the shortest distance is calculated. +For non-point `__indexable__`s the shortest distance is calculated using `bg::comparable_distance()` function. +The following query returns `k` `__value__`s closest to some Point in space. std::vector<__value__> returned_values; - __point__ pt(...); + __point__ pt(/*...*/); rt.query(bgi::nearest(pt, k), std::back_inserter(returned_values)); +The same way different query Geometries can be used: + + __box__ box(/*...*/); + rt.query(bgi::nearest(box, k), std::back_inserter(returned_values)); + + Segment seg(/*...*/); + rt.query(bgi::nearest(seg, k), std::back_inserter(returned_values)); + [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. @@ -173,10 +197,11 @@ Of course it's possible to connect different types of predicates together. BOOST_FOREACH(Value & v, rt | index::adaptors::queried(index::nearest(pt, k) && index::covered_by(b))) ; // do something with v -[h4 Breaking or pausing the query] +[h4 Iterative queries] The query performed using query iterators may be paused and resumed if needed, e.g. when the query takes too long, -or stopped at some point, e.g when all interesting values were gathered. +or may be stopped at some point, when all interesting values were gathered. The query iterator is returned by +`qbegin()` member function which requires passing predicates, like `query()` member function. for ( Rtree::const_query_iterator it = tree.qbegin(bgi::nearest(pt, 10000)) ; it != tree.qend() ; ++it ) @@ -186,6 +211,10 @@ or stopped at some point, e.g when all interesting values were gathered. 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] There are several ways of inserting Values returned by a query to the other R-tree container. @@ -208,8 +237,8 @@ The insert iterator may be passed directly into the query. RTree rt3; rt1.query(bgi::intersects(Box(/*...*/))), bgi::inserter(rt3)); -If you like Boost.Range you'll appreciate the third option. You may pass the result Range directly into the -constructor. +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. RTree rt4(rt1 | bgi::adaptors::queried(bgi::intersects(Box(/*...*/))))); diff --git a/doc/index/src/examples/rtree/Jamfile.v2 b/doc/index/src/examples/rtree/Jamfile.v2 index 79778b9ab..f45b5cb30 100644 --- a/doc/index/src/examples/rtree/Jamfile.v2 +++ b/doc/index/src/examples/rtree/Jamfile.v2 @@ -17,9 +17,25 @@ exe interprocess : interprocess.cpp /boost/thread//boost_thread : acc:-lrt acc-pa_risc:-lrt - gcc-mingw:"-lole32 -loleaut32 -lpsapi -ladvapi32" hpux,gcc:"-Wl,+as,mpas" +# gcc-mingw:"-lole32 -loleaut32 -lpsapi -ladvapi32" + gcc,windows:"-lole32 -loleaut32 -lpsapi -ladvapi32" + windows,clang:"-lole32 -loleaut32 -lpsapi -ladvapi32" : multi : # requirements ; + +exe mapped_file : mapped_file.cpp /boost/thread//boost_thread + : + acc:-lrt + acc-pa_risc:-lrt + hpux,gcc:"-Wl,+as,mpas" +# gcc-mingw:"-lole32 -loleaut32 -lpsapi -ladvapi32" + gcc,windows:"-lole32 -loleaut32 -lpsapi -ladvapi32" + windows,clang:"-lole32 -loleaut32 -lpsapi -ladvapi32" + : + multi + : # requirements + ; + diff --git a/doc/index/src/examples/rtree/interprocess_results.qbk b/doc/index/src/examples/rtree/interprocess_results.qbk new file mode 100644 index 000000000..897bd523c --- /dev/null +++ b/doc/index/src/examples/rtree/interprocess_results.qbk @@ -0,0 +1,24 @@ + Parent: Constructing container + Parent: Filling container with 100 boxes + Parent: Tree content + [(0, 0)(99.5, 99.5)] + Parent: Running child process + Child: Searching of the container in shared memory + Child: Querying for objects intersecting box = [(45, 45)(55, 55)] + Child: Found objects: + 11 + [(45, 45)(45.5, 45.5)] + [(46, 46)(46.5, 46.5)] + [(47, 47)(47.5, 47.5)] + [(48, 48)(48.5, 48.5)] + [(49, 49)(49.5, 49.5)] + [(50, 50)(50.5, 50.5)] + [(51, 51)(51.5, 51.5)] + [(52, 52)(52.5, 52.5)] + [(53, 53)(53.5, 53.5)] + [(54, 54)(54.5, 54.5)] + [(55, 55)(55.5, 55.5)] + + Child: Destroying container + Parent: Container was properly destroyed by the child process + diff --git a/doc/index/src/examples/rtree/mapped_file.cpp b/doc/index/src/examples/rtree/mapped_file.cpp new file mode 100644 index 000000000..18127505c --- /dev/null +++ b/doc/index/src/examples/rtree/mapped_file.cpp @@ -0,0 +1,66 @@ +// Boost.Geometry Index +// +// Quickbook Examples +// +// Copyright (c) 2011-2014 Adam Wulkiewicz, Lodz, Poland. +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +//[rtree_mapped_file + +#include + +#include + +#include +#include +#include + +namespace bi = boost::interprocess; +namespace bg = boost::geometry; +namespace bgm = bg::model; +namespace bgi = bg::index; + +int main() +{ + typedef bgm::point point_t; + + typedef point_t value_t; + typedef bgi::linear<32, 8> params_t; + typedef bgi::indexable indexable_t; + typedef bgi::equal_to equal_to_t; + typedef bi::allocator allocator_t; + typedef bgi::rtree rtree_t; + + { + bi::managed_mapped_file file(bi::open_or_create, "data.bin", 1024*1024); + allocator_t alloc(file.get_segment_manager()); + rtree_t * rtree_ptr = file.find_or_construct("rtree")(params_t(), indexable_t(), equal_to_t(), alloc); + + std::cout << rtree_ptr->size() << std::endl; + + rtree_ptr->insert(point_t(1.0, 1.0)); + rtree_ptr->insert(point_t(2.0, 2.0)); + + std::cout << rtree_ptr->size() << std::endl; + } + + { + bi::managed_mapped_file file(bi::open_or_create, "data.bin", 1024*1024); + allocator_t alloc(file.get_segment_manager()); + rtree_t * rtree_ptr = file.find_or_construct("rtree")(params_t(), indexable_t(), equal_to_t(), alloc); + + std::cout << rtree_ptr->size() << std::endl; + + rtree_ptr->insert(point_t(3.0, 3.0)); + rtree_ptr->insert(point_t(4.0, 4.0)); + + std::cout << rtree_ptr->size() << std::endl; + } + + return 0; +} + +//] diff --git a/doc/index/src/examples/rtree/mapped_file_results.qbk b/doc/index/src/examples/rtree/mapped_file_results.qbk new file mode 100644 index 000000000..b2d73c6d0 --- /dev/null +++ b/doc/index/src/examples/rtree/mapped_file_results.qbk @@ -0,0 +1,5 @@ + 0 + 2 + 2 + 4 + diff --git a/doc/index/src/examples/rtree/polygons_shared_ptr.cpp b/doc/index/src/examples/rtree/polygons_shared_ptr.cpp index c65ba5376..0d34e23fe 100644 --- a/doc/index/src/examples/rtree/polygons_shared_ptr.cpp +++ b/doc/index/src/examples/rtree/polygons_shared_ptr.cpp @@ -69,6 +69,11 @@ int main(void) std::vector result_n; rtree.query(bgi::nearest(point(0, 0), 5), std::back_inserter(result_n)); + // note: in Boost.Geometry the WKT representation of a box is polygon + + // note: the values store the bounding boxes of polygons + // the polygons aren't used for querying but are printed + // display results std::cout << "spatial query box:" << std::endl; std::cout << bg::wkt(query_box) << std::endl; diff --git a/doc/index/src/examples/rtree/polygons_shared_ptr_results.qbk b/doc/index/src/examples/rtree/polygons_shared_ptr_results.qbk new file mode 100644 index 000000000..5280647be --- /dev/null +++ b/doc/index/src/examples/rtree/polygons_shared_ptr_results.qbk @@ -0,0 +1,29 @@ + filling index with polygons shared pointers: + POLYGON((1 0,0.4 0.8,-0.5 0.8,-0.9 0,-0.4 -0.8,0.5 -0.8,1 0)) + POLYGON((2 1,1.4 1.8,0.5 1.8,0.1 1,0.6 0.2,1.5 0.2,2 1)) + POLYGON((3 2,2.4 2.8,1.5 2.8,1.1 2,1.6 1.2,2.5 1.2,3 2)) + POLYGON((4 3,3.4 3.8,2.5 3.8,2.1 3,2.6 2.2,3.5 2.2,4 3)) + POLYGON((5 4,4.4 4.8,3.5 4.8,3.1 4,3.6 3.2,4.5 3.2,5 4)) + POLYGON((6 5,5.4 5.8,4.5 5.8,4.1 5,4.6 4.2,5.5 4.2,6 5)) + POLYGON((7 6,6.4 6.8,5.5 6.8,5.1 6,5.6 5.2,6.5 5.2,7 6)) + POLYGON((8 7,7.4 7.8,6.5 7.8,6.1 7,6.6 6.2,7.5 6.2,8 7)) + POLYGON((9 8,8.4 8.8,7.5 8.8,7.1 8,7.6 7.2,8.5 7.2,9 8)) + POLYGON((10 9,9.4 9.8,8.5 9.8,8.1 9,8.6 8.2,9.5 8.2,10 9)) + spatial query box: + POLYGON((0 0,0 5,5 5,5 0,0 0)) + spatial query result: + POLYGON((1 0,0.4 0.8,-0.5 0.8,-0.9 0,-0.4 -0.8,0.5 -0.8,1 0)) + POLYGON((2 1,1.4 1.8,0.5 1.8,0.1 1,0.6 0.2,1.5 0.2,2 1)) + POLYGON((3 2,2.4 2.8,1.5 2.8,1.1 2,1.6 1.2,2.5 1.2,3 2)) + POLYGON((4 3,3.4 3.8,2.5 3.8,2.1 3,2.6 2.2,3.5 2.2,4 3)) + POLYGON((5 4,4.4 4.8,3.5 4.8,3.1 4,3.6 3.2,4.5 3.2,5 4)) + POLYGON((6 5,5.4 5.8,4.5 5.8,4.1 5,4.6 4.2,5.5 4.2,6 5)) + knn query point: + POINT(0 0) + knn query result: + POLYGON((5 4,4.4 4.8,3.5 4.8,3.1 4,3.6 3.2,4.5 3.2,5 4)) + POLYGON((4 3,3.4 3.8,2.5 3.8,2.1 3,2.6 2.2,3.5 2.2,4 3)) + POLYGON((3 2,2.4 2.8,1.5 2.8,1.1 2,1.6 1.2,2.5 1.2,3 2)) + POLYGON((1 0,0.4 0.8,-0.5 0.8,-0.9 0,-0.4 -0.8,0.5 -0.8,1 0)) + POLYGON((2 1,1.4 1.8,0.5 1.8,0.1 1,0.6 0.2,1.5 0.2,2 1)) + diff --git a/doc/index/src/examples/rtree/polygons_vector.cpp b/doc/index/src/examples/rtree/polygons_vector.cpp index a8984a732..7102862c6 100644 --- a/doc/index/src/examples/rtree/polygons_vector.cpp +++ b/doc/index/src/examples/rtree/polygons_vector.cpp @@ -77,6 +77,11 @@ int main(void) std::vector result_n; rtree.query(bgi::nearest(point(0, 0), 5), std::back_inserter(result_n)); + // note: in Boost.Geometry the WKT representation of a box is polygon + + // note: the values store the bounding boxes of polygons + // the polygons aren't used for querying but are printed + // display results std::cout << "spatial query box:" << std::endl; std::cout << bg::wkt(query_box) << std::endl; diff --git a/doc/index/src/examples/rtree/polygons_vector_results.qbk b/doc/index/src/examples/rtree/polygons_vector_results.qbk new file mode 100644 index 000000000..d1dbc6041 --- /dev/null +++ b/doc/index/src/examples/rtree/polygons_vector_results.qbk @@ -0,0 +1,29 @@ + generated polygons: + POLYGON((1 0,0.4 0.8,-0.5 0.8,-0.9 0,-0.4 -0.8,0.5 -0.8,1 0)) + POLYGON((2 1,1.4 1.8,0.5 1.8,0.1 1,0.6 0.2,1.5 0.2,2 1)) + POLYGON((3 2,2.4 2.8,1.5 2.8,1.1 2,1.6 1.2,2.5 1.2,3 2)) + POLYGON((4 3,3.4 3.8,2.5 3.8,2.1 3,2.6 2.2,3.5 2.2,4 3)) + POLYGON((5 4,4.4 4.8,3.5 4.8,3.1 4,3.6 3.2,4.5 3.2,5 4)) + POLYGON((6 5,5.4 5.8,4.5 5.8,4.1 5,4.6 4.2,5.5 4.2,6 5)) + POLYGON((7 6,6.4 6.8,5.5 6.8,5.1 6,5.6 5.2,6.5 5.2,7 6)) + POLYGON((8 7,7.4 7.8,6.5 7.8,6.1 7,6.6 6.2,7.5 6.2,8 7)) + POLYGON((9 8,8.4 8.8,7.5 8.8,7.1 8,7.6 7.2,8.5 7.2,9 8)) + POLYGON((10 9,9.4 9.8,8.5 9.8,8.1 9,8.6 8.2,9.5 8.2,10 9)) + spatial query box: + POLYGON((0 0,0 5,5 5,5 0,0 0)) + spatial query result: + POLYGON((1 0,0.4 0.8,-0.5 0.8,-0.9 0,-0.4 -0.8,0.5 -0.8,1 0)) + POLYGON((2 1,1.4 1.8,0.5 1.8,0.1 1,0.6 0.2,1.5 0.2,2 1)) + POLYGON((3 2,2.4 2.8,1.5 2.8,1.1 2,1.6 1.2,2.5 1.2,3 2)) + POLYGON((4 3,3.4 3.8,2.5 3.8,2.1 3,2.6 2.2,3.5 2.2,4 3)) + POLYGON((5 4,4.4 4.8,3.5 4.8,3.1 4,3.6 3.2,4.5 3.2,5 4)) + POLYGON((6 5,5.4 5.8,4.5 5.8,4.1 5,4.6 4.2,5.5 4.2,6 5)) + knn query point: + POINT(0 0) + knn query result: + POLYGON((5 4,4.4 4.8,3.5 4.8,3.1 4,3.6 3.2,4.5 3.2,5 4)) + POLYGON((4 3,3.4 3.8,2.5 3.8,2.1 3,2.6 2.2,3.5 2.2,4 3)) + POLYGON((3 2,2.4 2.8,1.5 2.8,1.1 2,1.6 1.2,2.5 1.2,3 2)) + POLYGON((1 0,0.4 0.8,-0.5 0.8,-0.9 0,-0.4 -0.8,0.5 -0.8,1 0)) + POLYGON((2 1,1.4 1.8,0.5 1.8,0.1 1,0.6 0.2,1.5 0.2,2 1)) + diff --git a/doc/index/src/examples/rtree/quick_start.cpp b/doc/index/src/examples/rtree/quick_start.cpp index 80c883122..fd3834449 100644 --- a/doc/index/src/examples/rtree/quick_start.cpp +++ b/doc/index/src/examples/rtree/quick_start.cpp @@ -66,6 +66,8 @@ int main(void) rtree.query(bgi::nearest(point(0, 0), 5), std::back_inserter(result_n)); //] + // note: in Boost.Geometry WKT representation of a box is polygon + //[rtree_quickstart_output // display results std::cout << "spatial query box:" << std::endl; diff --git a/doc/index/src/examples/rtree/quick_start_results.qbk b/doc/index/src/examples/rtree/quick_start_results.qbk new file mode 100644 index 000000000..e1defd08c --- /dev/null +++ b/doc/index/src/examples/rtree/quick_start_results.qbk @@ -0,0 +1,18 @@ + spatial query box: + POLYGON((0 0,0 5,5 5,5 0,0 0)) + spatial query result: + POLYGON((0 0,0 0.5,0.5 0.5,0.5 0,0 0)) - 0 + POLYGON((1 1,1 1.5,1.5 1.5,1.5 1,1 1)) - 1 + POLYGON((2 2,2 2.5,2.5 2.5,2.5 2,2 2)) - 2 + POLYGON((3 3,3 3.5,3.5 3.5,3.5 3,3 3)) - 3 + POLYGON((4 4,4 4.5,4.5 4.5,4.5 4,4 4)) - 4 + POLYGON((5 5,5 5.5,5.5 5.5,5.5 5,5 5)) - 5 + knn query point: + POINT(0 0) + knn query result: + POLYGON((4 4,4 4.5,4.5 4.5,4.5 4,4 4)) - 4 + POLYGON((3 3,3 3.5,3.5 3.5,3.5 3,3 3)) - 3 + POLYGON((2 2,2 2.5,2.5 2.5,2.5 2,2 2)) - 2 + POLYGON((0 0,0 0.5,0.5 0.5,0.5 0,0 0)) - 0 + POLYGON((1 1,1 1.5,1.5 1.5,1.5 1,1 1)) - 1 + diff --git a/doc/index/src/examples/rtree/value_index.cpp b/doc/index/src/examples/rtree/value_index.cpp index 32f306909..6f245403b 100644 --- a/doc/index/src/examples/rtree/value_index.cpp +++ b/doc/index/src/examples/rtree/value_index.cpp @@ -78,6 +78,8 @@ int main(void) std::vector result_n; rtree.query(bgi::nearest(point(0, 0), 5), std::back_inserter(result_n)); + // note: in Boost.Geometry the WKT representation of a box is polygon + // display results std::cout << "spatial query box:" << std::endl; std::cout << bg::wkt(query_box) << std::endl; diff --git a/doc/index/src/examples/rtree/value_index_results.qbk b/doc/index/src/examples/rtree/value_index_results.qbk new file mode 100644 index 000000000..046da1374 --- /dev/null +++ b/doc/index/src/examples/rtree/value_index_results.qbk @@ -0,0 +1,29 @@ + generated boxes: + POLYGON((0 0,0 0.5,0.5 0.5,0.5 0,0 0)) + POLYGON((1 1,1 1.5,1.5 1.5,1.5 1,1 1)) + POLYGON((2 2,2 2.5,2.5 2.5,2.5 2,2 2)) + POLYGON((3 3,3 3.5,3.5 3.5,3.5 3,3 3)) + POLYGON((4 4,4 4.5,4.5 4.5,4.5 4,4 4)) + POLYGON((5 5,5 5.5,5.5 5.5,5.5 5,5 5)) + POLYGON((6 6,6 6.5,6.5 6.5,6.5 6,6 6)) + POLYGON((7 7,7 7.5,7.5 7.5,7.5 7,7 7)) + POLYGON((8 8,8 8.5,8.5 8.5,8.5 8,8 8)) + POLYGON((9 9,9 9.5,9.5 9.5,9.5 9,9 9)) + spatial query box: + POLYGON((0 0,0 5,5 5,5 0,0 0)) + spatial query result: + POLYGON((0 0,0 0.5,0.5 0.5,0.5 0,0 0)) + POLYGON((1 1,1 1.5,1.5 1.5,1.5 1,1 1)) + POLYGON((2 2,2 2.5,2.5 2.5,2.5 2,2 2)) + POLYGON((3 3,3 3.5,3.5 3.5,3.5 3,3 3)) + POLYGON((4 4,4 4.5,4.5 4.5,4.5 4,4 4)) + POLYGON((5 5,5 5.5,5.5 5.5,5.5 5,5 5)) + knn query point: + POINT(0 0) + knn query result: + POLYGON((4 4,4 4.5,4.5 4.5,4.5 4,4 4)) + POLYGON((3 3,3 3.5,3.5 3.5,3.5 3,3 3)) + POLYGON((2 2,2 2.5,2.5 2.5,2.5 2,2 2)) + POLYGON((0 0,0 0.5,0.5 0.5,0.5 0,0 0)) + POLYGON((1 1,1 1.5,1.5 1.5,1.5 1,1 1)) + diff --git a/doc/index/src/examples/rtree/value_shared_ptr.cpp b/doc/index/src/examples/rtree/value_shared_ptr.cpp index e2ff55737..bf20646fe 100644 --- a/doc/index/src/examples/rtree/value_shared_ptr.cpp +++ b/doc/index/src/examples/rtree/value_shared_ptr.cpp @@ -72,6 +72,8 @@ int main(void) std::vector result_n; rtree.query(bgi::nearest(point(0, 0), 5), std::back_inserter(result_n)); + // note: in Boost.Geometry the WKT representation of a box is polygon + // display results std::cout << "spatial query box:" << std::endl; std::cout << bg::wkt(query_box) << std::endl; diff --git a/doc/index/src/examples/rtree/value_shared_ptr_results.qbk b/doc/index/src/examples/rtree/value_shared_ptr_results.qbk new file mode 100644 index 000000000..e745d7a84 --- /dev/null +++ b/doc/index/src/examples/rtree/value_shared_ptr_results.qbk @@ -0,0 +1,29 @@ + filling index with boxes shared pointers: + POLYGON((0 0,0 0.5,0.5 0.5,0.5 0,0 0)) + POLYGON((1 1,1 1.5,1.5 1.5,1.5 1,1 1)) + POLYGON((2 2,2 2.5,2.5 2.5,2.5 2,2 2)) + POLYGON((3 3,3 3.5,3.5 3.5,3.5 3,3 3)) + POLYGON((4 4,4 4.5,4.5 4.5,4.5 4,4 4)) + POLYGON((5 5,5 5.5,5.5 5.5,5.5 5,5 5)) + POLYGON((6 6,6 6.5,6.5 6.5,6.5 6,6 6)) + POLYGON((7 7,7 7.5,7.5 7.5,7.5 7,7 7)) + POLYGON((8 8,8 8.5,8.5 8.5,8.5 8,8 8)) + POLYGON((9 9,9 9.5,9.5 9.5,9.5 9,9 9)) + spatial query box: + POLYGON((0 0,0 5,5 5,5 0,0 0)) + spatial query result: + POLYGON((0 0,0 0.5,0.5 0.5,0.5 0,0 0)) + POLYGON((1 1,1 1.5,1.5 1.5,1.5 1,1 1)) + POLYGON((2 2,2 2.5,2.5 2.5,2.5 2,2 2)) + POLYGON((3 3,3 3.5,3.5 3.5,3.5 3,3 3)) + POLYGON((4 4,4 4.5,4.5 4.5,4.5 4,4 4)) + POLYGON((5 5,5 5.5,5.5 5.5,5.5 5,5 5)) + knn query point: + POINT(0 0) + knn query result: + POLYGON((4 4,4 4.5,4.5 4.5,4.5 4,4 4)) + POLYGON((3 3,3 3.5,3.5 3.5,3.5 3,3 3)) + POLYGON((2 2,2 2.5,2.5 2.5,2.5 2,2 2)) + POLYGON((0 0,0 0.5,0.5 0.5,0.5 0,0 0)) + POLYGON((1 1,1 1.5,1.5 1.5,1.5 1,1 1)) + diff --git a/doc/index/src/examples/rtree/variants_map.cpp b/doc/index/src/examples/rtree/variants_map.cpp index 462795002..efc043e3d 100644 --- a/doc/index/src/examples/rtree/variants_map.cpp +++ b/doc/index/src/examples/rtree/variants_map.cpp @@ -124,6 +124,11 @@ int main(void) std::vector result_n; rtree.query(bgi::nearest(point(0, 0), 5), std::back_inserter(result_n)); + // note: in Boost.Geometry the WKT representation of a box is polygon + + // note: the values store the bounding boxes of geometries + // the geometries aren't used for querying but are printed + // display results std::cout << "spatial query box:" << std::endl; std::cout << bg::wkt(query_box) << std::endl; diff --git a/doc/index/src/examples/rtree/variants_map_results.qbk b/doc/index/src/examples/rtree/variants_map_results.qbk new file mode 100644 index 000000000..e6e76b347 --- /dev/null +++ b/doc/index/src/examples/rtree/variants_map_results.qbk @@ -0,0 +1,29 @@ + generated geometries: + POLYGON((1 0,0.4 0.8,-0.5 0.8,-0.9 0,-0.4 -0.8,0.5 -0.8)) + POLYGON((2 1,1.4 1.8,0.5 1.8,0.1 1,0.6 0.2,1.5 0.2)) + POLYGON((3 2,2.4 2.8,1.5 2.8,1.1 2,1.6 1.2,2.5 1.2,3 2)) + POLYGON((4 3,3.4 3.8,2.5 3.8,2.1 3,2.6 2.2,3.5 2.2)) + LINESTRING(5 4,4.4 4.8,3.5 4.8,3.1 4,3.6 3.2,4.5 3.2) + POLYGON((6 5,5.4 5.8,4.5 5.8,4.1 5,4.6 4.2,5.5 4.2)) + POLYGON((7 6,6.4 6.8,5.5 6.8,5.1 6,5.6 5.2,6.5 5.2)) + POLYGON((8 7,7.4 7.8,6.5 7.8,6.1 7,6.6 6.2,7.5 6.2,8 7)) + POLYGON((9 8,8.4 8.8,7.5 8.8,7.1 8,7.6 7.2,8.5 7.2,9 8)) + POLYGON((10 9,9.4 9.8,8.5 9.8,8.1 9,8.6 8.2,9.5 8.2)) + spatial query box: + POLYGON((0 0,0 5,5 5,5 0,0 0)) + spatial query result: + POLYGON((1 0,0.4 0.8,-0.5 0.8,-0.9 0,-0.4 -0.8,0.5 -0.8)) + POLYGON((2 1,1.4 1.8,0.5 1.8,0.1 1,0.6 0.2,1.5 0.2)) + POLYGON((3 2,2.4 2.8,1.5 2.8,1.1 2,1.6 1.2,2.5 1.2,3 2)) + POLYGON((4 3,3.4 3.8,2.5 3.8,2.1 3,2.6 2.2,3.5 2.2)) + LINESTRING(5 4,4.4 4.8,3.5 4.8,3.1 4,3.6 3.2,4.5 3.2) + POLYGON((6 5,5.4 5.8,4.5 5.8,4.1 5,4.6 4.2,5.5 4.2)) + knn query point: + POINT(0 0) + knn query result: + LINESTRING(5 4,4.4 4.8,3.5 4.8,3.1 4,3.6 3.2,4.5 3.2) + POLYGON((4 3,3.4 3.8,2.5 3.8,2.1 3,2.6 2.2,3.5 2.2)) + POLYGON((3 2,2.4 2.8,1.5 2.8,1.1 2,1.6 1.2,2.5 1.2,3 2)) + POLYGON((1 0,0.4 0.8,-0.5 0.8,-0.9 0,-0.4 -0.8,0.5 -0.8)) + POLYGON((2 1,1.4 1.8,0.5 1.8,0.1 1,0.6 0.2,1.5 0.2)) + diff --git a/doc/introduction.qbk b/doc/introduction.qbk index dfac965d5..1762a169d 100644 --- a/doc/introduction.qbk +++ b/doc/introduction.qbk @@ -45,18 +45,14 @@ officially part of the Boost C++ Libraries. Latest stable version of the source code is included in the [@http://www.boost.org/users/download/ Boost packaged releases]. -It can also be downloaded from the current -[@http://svn.boost.org/svn/boost/branches/release Boost release branch] -in the Boost Subversion repository. +It can also be downloaded from the [@http://github.com/boostorg/boost Boost GitHub repository] (master branch). The library development upstream is available from the -[@http://svn.boost.org/svn/boost/trunk Boost trunk] in the Boost Subversion -repository. +[@https://github.com/boostorg/geometry/tree/develop Boost.Geometry (develop branch)]. Note that the library [*extensions] are not distributed in the official Boost -releases, but only available -in the [@http://svn.boost.org/svn/boost/trunk/boost/geometry/extensions/ Boost -trunk] +releases, but only available +in the [@https://github.com/boostorg/geometry/tree/develop Boost.Geometry (develop branch)] and that they are subject to change. __boost_geometry__ was accepted by Boost at November 28, 2009 @@ -65,6 +61,8 @@ __boost_geometry__ was accepted by Boost at November 28, 2009 There is a __boost_geometry__ [@http://lists.boost.org/mailman/listinfo.cgi/geometry mailing list]. The mailing list and its messages are also accessible from [@http://boost-geometry.203548.n3.nabble.com/ Nabble] as Boost Geometry. Also on -the Boost Developers list and on the Boost Users list __boost_geometry__ is discussed. +the [@http://lists.boost.org/mailman/listinfo.cgi/boost +Boost Developers list] and on the [@http://lists.boost.org/mailman/listinfo.cgi/boost-users +Boost Users list] __boost_geometry__ is discussed. [endsect] diff --git a/doc/make_qbk.py b/doc/make_qbk.py index ae8adfda7..7bf5db409 100755 --- a/doc/make_qbk.py +++ b/doc/make_qbk.py @@ -12,7 +12,9 @@ import os, sys -os.chdir(os.path.dirname(sys.argv[0])) +script_dir = os.path.dirname(__file__) +os.chdir(os.path.abspath(script_dir)) +print("Boost.Geometry is making .qbk files in %s" % os.getcwd()) if 'DOXYGEN' in os.environ: doxygen_cmd = os.environ['DOXYGEN'] @@ -34,44 +36,53 @@ cmd = cmd + " --copyright src/copyright_block.qbk" cmd = cmd + " --output_member_variables false" cmd = cmd + " > generated/%s.qbk" +def run_command(command): + if os.system(command) != 0: + raise Exception("Error running %s" % command) + +def remove_all_files(dir): + if os.path.exists(dir): + for f in os.listdir(dir): + os.remove(dir+f) + def call_doxygen(): - os.chdir("doxy"); - os.system("rm -f doxygen_output/xml/*.xml") - os.system(doxygen_cmd) + os.chdir("doxy") + remove_all_files("doxygen_output/xml/") + run_command(doxygen_cmd) os.chdir("..") def group_to_quickbook(section): - os.system(cmd % ("group__" + section.replace("_", "__"), section)) + run_command(cmd % ("group__" + section.replace("_", "__"), section)) def model_to_quickbook(section): - os.system(cmd % ("classboost_1_1geometry_1_1model_1_1" + section.replace("_", "__"), section)) + run_command(cmd % ("classboost_1_1geometry_1_1model_1_1" + section.replace("_", "__"), section)) def model_to_quickbook2(classname, section): - os.system(cmd % ("classboost_1_1geometry_1_1model_1_1" + classname, section)) + run_command(cmd % ("classboost_1_1geometry_1_1model_1_1" + classname, section)) def struct_to_quickbook(section): - os.system(cmd % ("structboost_1_1geometry_1_1" + section.replace("_", "__"), section)) + run_command(cmd % ("structboost_1_1geometry_1_1" + section.replace("_", "__"), section)) def class_to_quickbook(section): - os.system(cmd % ("classboost_1_1geometry_1_1" + section.replace("_", "__"), section)) + run_command(cmd % ("classboost_1_1geometry_1_1" + section.replace("_", "__"), section)) def strategy_to_quickbook(section): p = section.find("::") ns = section[:p] strategy = section[p+2:] - os.system(cmd % ("classboost_1_1geometry_1_1strategy_1_1" + run_command(cmd % ("classboost_1_1geometry_1_1strategy_1_1" + ns.replace("_", "__") + "_1_1" + strategy.replace("_", "__"), ns + "_" + strategy)) def cs_to_quickbook(section): - os.system(cmd % ("structboost_1_1geometry_1_1cs_1_1" + section.replace("_", "__"), section)) + run_command(cmd % ("structboost_1_1geometry_1_1cs_1_1" + section.replace("_", "__"), section)) call_doxygen() algorithms = ["append", "assign", "make", "clear" , "area", "buffer", "centroid", "convert", "correct", "covered_by" - , "convex_hull", "difference", "disjoint", "distance" + , "convex_hull", "crosses", "difference", "disjoint", "distance" , "envelope", "equals", "expand", "for_each", "intersection", "intersects" , "length", "num_geometries", "num_interior_rings", "num_points" , "overlaps", "perimeter", "reverse", "simplify", "sym_difference" @@ -158,4 +169,4 @@ execfile("make_qbk.py") os.chdir("..") # Use either bjam or b2 or ../../../b2 (the last should be done on Release branch) -os.system("bjam") +run_command("b2") diff --git a/doc/quickbook/quickref.xml b/doc/quickbook/quickref.xml new file mode 100644 index 000000000..8179d7a87 --- /dev/null +++ b/doc/quickbook/quickref.xml @@ -0,0 +1,739 @@ + + + + + + + + + + + + + + + + + + Geometry Concepts + + + + + + + 0-dimensional + + boost::geometry::concept::Point + boost::geometry::concept::ConstPoint + + + + 1-dimensional + + boost::geometry::concept::Segment + boost::geometry::concept::ConstSegment + boost::geometry::concept::Linestring + boost::geometry::concept::ConstLinestring + + + + 2-dimensional + + boost::geometry::concept::Box + boost::geometry::concept::ConstBox + boost::geometry::concept::Ring + boost::geometry::concept::ConstRing + boost::geometry::concept::Polygon + boost::geometry::concept::ConstPolygon + + + + + + Functions + + boost::geometry::concept::check + boost::geometry::concept::check_concepts_and_equal_dimensions + + + + + + + + + + + + + + + Geometry Models + + + + + + + 0-dimensional + + boost::geometry::point + boost::geometry::point_xy + boost::geometry::point_2d + boost::geometry::point_3d + + + + 1-dimensional + + boost::geometry::segment + boost::geometry::segment_2d + boost::geometry::linestring + boost::geometry::linestring_2d + boost::geometry::linestring_3d + + + + 2-dimensional + + boost::geometry::box + boost::geometry::box_2d + boost::geometry::box_3d + boost::geometry::box + boost::geometry::linear_ring + boost::geometry::ring_2d + boost::geometry::ring_3d + boost::geometry::polygon + boost::geometry::polygon_2d + boost::geometry::polygon_3d + + + + + + Functions + + + Macros + + + + + + + + + + + + + + + + Core + + + + + + + Metafunctions + + boost::geometry::cs_tag + boost::geometry::coordinate_type + boost::geometry::coordinate_system + boost::geometry::dimension + boost::geometry::geometry_id + boost::geometry::interior_type + boost::geometry::is_linear + boost::geometry::is_multi + boost::geometry::is_radian + boost::geometry::point_order + boost::geometry::point_type + boost::geometry::ring_type + boost::geometry::replace_point_type + boost::geometry::reverse_dispatch + boost::geometry::tag + boost::geometry::topological_dimension + + + + Access Functions + + boost::geometry::exterior_ring + boost::geometry::get + boost::geometry::get_as_radian + boost::geometry::interior_rings + boost::geometry::num_interior_rings + boost::geometry::num_points + boost::geometry::set + boost::geometry::set_from_radian + + Classes + + boost::geometry::exception + boost::geometry::centroid_exception + + + + + + + + + + + + + + Constants + + + + + + + Numeric + + boost::geometry::max_corner + boost::geometry::min_corner + boost::geometry::order_selector + boost::geometry::math::pi + boost::geometry::math::two_pi + boost::geometry::math::d2r + boost::geometry::math::r2d + + + + Types + + boost::geometry::degree + boost::geometry::radian + + + + + + + + + + + + + + + Coordinate Systems + + + Iterators + + + + + + + Classes + + boost::geometry::cs::cartesian + boost::geometry::cs::geographic + boost::geometry::cs::polar + boost::geometry::cs::spherical + + + + Metafunctions + + boost::geometry::range_type + + Classes + + boost::geometry::circular_iterator + boost::geometry::ever_circling_iterator + boost::geometry::one_section_segment_iterator + boost::geometry::section_iterator + boost::geometry::segment_iterator + + + + Functions + + boost::geometry::make_segment_iterator + boost::geometry::operator== + boost::geometry::operator!= + + + + + + + + + + + + + + + Algorithms + + + + + + + Geometry Constructors + + boost::geometry::make + boost::geometry::make_inverse + boost::geometry::make_zero + + Predicates + + boost::geometry::crosses + boost::geometry::disjoint + boost::geometry::equals + boost::geometry::intersects + boost::geometry::overlaps + boost::geometry::selected + boost::geometry::within + + + + Append + + boost::geometry::append + + Area + + boost::geometry::area + + Assign + + boost::geometry::assign + boost::geometry::assign_box_corners + boost::geometry::assign_inverse + boost::geometry::assign_point_from_index + boost::geometry::assign_point_to_index + boost::geometry::assign_zero + + Buffer + + boost::geometry::buffer + boost::geometry::make_buffer + + + + Centroid + + boost::geometry::centroid + boost::geometry::make_centroid + + Clear + + boost::geometry::clear + + Combine + + boost::geometry::combine + + Convert + + boost::geometry::convert + + Convex Hull + + boost::geometry::convex_hull + boost::geometry::convex_hull_inserter + + Correct + + boost::geometry::correct + + + + + + Distance + + boost::geometry::distance + + Difference + + boost::geometry::difference + boost::geometry::sym_difference + + Dissolve + + boost::geometry::dissolve + + Envelope + + boost::geometry::envelope + boost::geometry::make_envelope + + for_each + + boost::geometry::for_each_point + boost::geometry::for_each_segment + + Intersection + + boost::geometry::intersection_inserter + + + + Length + + boost::geometry::length + + Overlay + + boost::geometry::copy_segments + boost::geometry::copy_segment_point + boost::geometry::copy_segment_points + boost::geometry::enrich_intersection_points + boost::geometry::get_turns + boost::geometry::traverse + + Perimeter + + boost::geometry::perimeter + + Reverse + + boost::geometry::reverse + + + + Section + + boost::geometry::get_section + boost::geometry::sectionalize + + Simplify + + boost::geometry::simplify + boost::geometry::simplify_inserter + + Transform + + boost::geometry::transform + + Union + + boost::geometry::union_inserter + + Unique + + boost::geometry::unique + + Miscellaneous Utilities + + boost::geometry::parse + + + + + + + + + + + + + + Policies + + + + + + + Compare + + boost::geometry::equal_to + boost::geometry::greater + boost::geometry::less + + + + Relate + + boost::geometry::policies::relate::direction_type + boost::geometry::policies::relate::segments_de9im + boost::geometry::policies::relate::segments_direction + boost::geometry::policies::relate::segments_intersection_points + boost::geometry::policies::relate::segments_tupled + + + + + + + + + + + + + + Strategy Concepts + + + + + + + + boost::geometry::concept::AreaStrategy + boost::geometry::concept::CentroidStrategy + boost::geometry::concept::ConvexHullStrategy + boost::geometry::concept::PointDistanceStrategy + boost::geometry::concept::PointSegmentDistanceStrategy + + + + + boost::geometry::concept::SegmentIntersectStrategy + boost::geometry::concept::SimplifyStrategy + boost::geometry::concept::WithinStrategy + + + + + + + + + + + + + + + Strategies + + + + + + + Area + + boost::geometry::strategy_area + boost::geometry::area_result + boost::geometry::strategy::area::by_triangles + boost::geometry::strategy::area::huiller + + + + Buffer + + boost::geometry::strategy::buffer::join_miter + boost::geometry::strategy::buffer::join_bevel + boost::geometry::strategy::buffer::join_round + + + + Centroid + + boost::geometry::strategy_centroid + boost::geometry::strategy::centroid_::bashein_detmer + boost::geometry::strategy::centroid_::centroid_average + + + + + + Compare + + boost::geometry::strategy_compare + boost::geometry::strategy::compare::default_strategy + boost::geometry::strategy::compare::circular_comparator + + + + Convex Hull + + boost::geometry::strategy_convex_hull + boost::geometry::strategy::convex_hull::graham_andrew + + + + Distance + + boost::geometry::strategy_distance + boost::geometry::strategy_distance_segment + boost::geometry::cartesian_distance + boost::geometry::distance_result + boost::geometry::make_distance_result + boost::geometry::close_to_zero + boost::geometry::fuzzy_equals + boost::geometry::strategy::distance::projected_point + boost::geometry::strategy::distance::pythagoras + boost::geometry::strategy::distance::cross_track + boost::geometry::strategy::distance::haversine + + + + + + Intersection + + boost::geometry::de9im + boost::geometry::de9im_segment + boost::geometry::segment_intersection_points + boost::geometry::strategy_intersection + boost::geometry::strategy::intersection::liang_barsky + boost::geometry::strategy::intersection::relate_cartesian_segments + boost::geometry::strategy::intersection::relate_cartesian_segments + + + + Side + + boost::geometry::strategy_side + boost::geometry::side_info + boost::geometry::strategy::side::course + boost::geometry::strategy::side::side_by_triangle + boost::geometry::strategy::side::side_by_cross_track + + + + Simplify + + boost::geometry::strategy::simplify::douglas_peucker + + + + + + Transform + + boost::geometry::strategy_transform + boost::geometry::strategy::copy_direct + boost::geometry::strategy::copy_per_coordinate + boost::geometry::strategy::degree_radian_vv + boost::geometry::strategy::degree_radian_vv_3 + boost::geometry::strategy::from_spherical_2_to_cartesian_3 + boost::geometry::strategy::from_spherical_3_to_cartesian_3 + boost::geometry::strategy::from_cartesian_3_to_spherical_2 + boost::geometry::strategy::from_cartesian_3_to_spherical_3 + boost::geometry::strategy::inverse_transformer + boost::geometry::strategy::map_transformer + boost::geometry::strategy::ublas_transformer + boost::geometry::strategy::translate_transformer + boost::geometry::strategy::scale_transformer + boost::geometry::strategy::rotate_transformer + + + + Within + + boost::geometry::strategy::winding + boost::geometry::strategy::crossings_multiply + boost::geometry::strategy::franklin + + + + Miscellaneous Utilities + + boost::geometry::strategy::not_implemented + + + + + + + + + + + + + + + + Arithmetic + + + + + + + Add + + boost::geometry::add_point + boost::geometry::add_value + + + + Subtract + + boost::geometry::subtract_point + boost::geometry::subtract_value + + + + Multiply + + boost::geometry::multiply_point + boost::geometry::multiply_value + + + + Divide + + boost::geometry::divide_point + boost::geometry::divide_value + + + + + + Products + + boost::geometry::cross_product + boost::geometry::dot_product + + + + + + + + + + + + + + + Extensions + + + + + + + TODO + + ... + + + + + + + diff --git a/doc/quickref.xml b/doc/quickref.xml index 987cad233..9e20847d3 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -318,6 +318,7 @@ Predicates + crosses covered_by disjoint equals @@ -734,13 +735,13 @@ contains(Geometry const &) covered_by(Geometry const &) - covers(Geometry const &) + covers(Geometry const &) disjoint(Geometry const &) intersects(Geometry const &) overlaps(Geometry const &) within(Geometry const &) satisfies(UnaryPredicate const &) - nearest(Point const &, unsigned) + nearest(Geometry const &, unsigned) 2 0 0 0 0 F i/i x/x +// |--------> +// +// |--------> 2 0 0 0 0 T i/x x/i +// <--------| +// +// |-----> 1 0 0 0 0 T x/x +// <-----| +// + +// |---------> 2 0 0 0 1 T i/x x/i +// <----| +// +// |---------> 2 0 0 0 0 F i/i x/x +// |----> +// +// |---------> 2 0 0 -1 1 F i/i u/x +// |----> +// +// |---------> 2 0 0 -1 0 T i/x u/i +// <----| + +// |-------> 2 0 0 1 -1 F and i/i x/u +// |-------> 2 0 0 -1 1 F symetric i/i u/x +// |-------> +// +// |-------> 2 0 0 -1 -1 T i/u u/i +// <-------| +// +// |-------> 2 0 0 1 1 T i/x x/i +// <-------| +// +// |--------> 2 0 0 -1 1 F i/i u/x +// |----> +// +// |--------> 2 0 0 -1 1 T i/x u/i +// <----| + +// |-----> 1 -1 -1 -1 -1 T u/u +// <-----| +// +// |-----> 1 -1 0 -1 0 F and u/x +// |-----> 1 0 -1 0 -1 F symetric x/u +// |-----> + +// D0 or D1 != 0 + +// ^ +// | +// + 1 -1 1 -1 1 F and u/x (P is vertical) +// |--------> 1 1 -1 1 -1 F symetric x/u (P is horizontal) +// ^ +// | +// + +// +// + +// | +// v +// |--------> 1 1 1 1 1 F x/x (P is vertical) +// +// ^ +// | +// + +// |--------> 1 -1 -1 -1 -1 F u/u (P is vertical) +// +// ^ +// | +// + +// |--------> 1 0 -1 0 -1 F u/u (P is vertical) +// +// + +// | +// v +// |--------> 1 0 1 0 1 F u/x (P is vertical) +// + +class linear_intersections +{ +public: + template + linear_intersections(Point1 const& pi, + Point2 const& qi, + IntersectionResult const& result, + bool is_p_last, bool is_q_last) + { + int arrival_a = result.template get<1>().arrival[0]; + int arrival_b = result.template get<1>().arrival[1]; + bool same_dirs = result.template get<1>().dir_a == 0 + && result.template get<1>().dir_b == 0; + + if ( same_dirs ) + { + if ( result.template get<0>().count == 2 ) + { + if ( ! result.template get<1>().opposite ) + { + ips[0].p_operation = operation_intersection; + ips[0].q_operation = operation_intersection; + ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last); + ips[1].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last); + + ips[0].is_pi + = equals::equals_point_point( + pi, result.template get<0>().intersections[0]); + ips[0].is_qi + = equals::equals_point_point( + qi, result.template get<0>().intersections[0]); + ips[1].is_pj = arrival_a != -1; + ips[1].is_qj = arrival_b != -1; + } + else + { + ips[0].p_operation = operation_intersection; + ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last); + ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last); + ips[1].q_operation = operation_intersection; + + ips[0].is_pi = arrival_b != 1; + ips[0].is_qj = arrival_b != -1; + ips[1].is_pj = arrival_a != -1; + ips[1].is_qi = arrival_a != 1; + } + } + else + { + BOOST_ASSERT(result.template get<0>().count == 1); + ips[0].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last); + ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last); + + ips[0].is_pi = arrival_a == -1; + ips[0].is_qi = arrival_b == -1; + ips[0].is_pj = arrival_a == 0; + ips[0].is_qj = arrival_b == 0; + } + } + else + { + ips[0].p_operation = union_or_blocked_different_dirs(arrival_a, is_p_last); + ips[0].q_operation = union_or_blocked_different_dirs(arrival_b, is_q_last); + + ips[0].is_pi = arrival_a == -1; + ips[0].is_qi = arrival_b == -1; + ips[0].is_pj = arrival_a == 1; + ips[0].is_qj = arrival_b == 1; + } + } + + struct ip_info + { + inline ip_info() + : p_operation(operation_none), q_operation(operation_none) + , is_pi(false), is_pj(false), is_qi(false), is_qj(false) + {} + + operation_type p_operation, q_operation; + bool is_pi, is_pj, is_qi, is_qj; + }; + + template + ip_info const& get() const + { + BOOST_STATIC_ASSERT(I < 2); + return ips[I]; + } + +private: + + // only if collinear (same_dirs) + static inline operation_type union_or_blocked_same_dirs(int arrival, bool is_last) + { + if ( arrival == 1 ) + return operation_blocked; + else if ( arrival == -1 ) + return operation_union; + else + return is_last ? operation_blocked : operation_union; + //return operation_blocked; + } + + // only if not collinear (!same_dirs) + static inline operation_type union_or_blocked_different_dirs(int arrival, bool is_last) + { + if ( arrival == 1 ) + //return operation_blocked; + return is_last ? operation_blocked : operation_union; + else + return operation_union; + } + + ip_info ips[2]; +}; + +template +struct get_turn_info_for_endpoint +{ + BOOST_STATIC_ASSERT(EnableFirst || EnableLast); + + template + static inline bool apply(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + TurnInfo const& tp_model, + IntersectionInfo const& inters, + method_type /*method*/, + OutputIterator out) + { + std::size_t ip_count = inters.i_info().count; + // no intersection points + if ( ip_count == 0 ) + return false; + + int segment_index0 = tp_model.operations[0].seg_id.segment_index; + int segment_index1 = tp_model.operations[1].seg_id.segment_index; + BOOST_ASSERT(segment_index0 >= 0 && segment_index1 >= 0); + + if ( !is_p_first && !is_p_last && !is_q_first && !is_q_last ) + return false; + + linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last); + + bool append0_last + = analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + intersections.template get<0>(), + tp_model, inters, 0, out); + + // NOTE: opposite && ip_count == 1 may be true! + bool opposite = inters.d_info().opposite; + + // don't ignore only for collinear opposite + bool result_ignore_ip0 = append0_last && ( ip_count == 1 || !opposite ); + + if ( intersections.template get<1>().p_operation == operation_none ) + return result_ignore_ip0; + + bool append1_last + = analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + intersections.template get<1>(), + tp_model, inters, 1, out); + + // don't ignore only for collinear opposite + bool result_ignore_ip1 = append1_last && !opposite /*&& ip_count == 2*/; + + return result_ignore_ip0 || result_ignore_ip1; + } + + template + static inline + bool analyse_segment_and_assign_ip(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + linear_intersections::ip_info const& ip_info, + TurnInfo const& tp_model, + IntersectionInfo const& inters, + int ip_index, + OutputIterator out) + { +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR + // may this give false positives for INTs? + typename IntersectionResult::point_type const& + inters_pt = result.template get<0>().intersections[ip_index]; + BOOST_ASSERT(ip_info.is_pi == equals::equals_point_point(pi, inters_pt)); + BOOST_ASSERT(ip_info.is_qi == equals::equals_point_point(qi, inters_pt)); + BOOST_ASSERT(ip_info.is_pj == equals::equals_point_point(pj, inters_pt)); + BOOST_ASSERT(ip_info.is_qj == equals::equals_point_point(qj, inters_pt)); +#endif + + // TODO - calculate first/last only if needed + bool is_p_first_ip = is_p_first && ip_info.is_pi; + bool is_p_last_ip = is_p_last && ip_info.is_pj; + bool is_q_first_ip = is_q_first && ip_info.is_qi; + bool is_q_last_ip = is_q_last && ip_info.is_qj; + bool append_first = EnableFirst && (is_p_first_ip || is_q_first_ip); + bool append_last = EnableLast && (is_p_last_ip || is_q_last_ip); + + operation_type p_operation = ip_info.p_operation; + operation_type q_operation = ip_info.q_operation; + + if ( append_first || append_last ) + { + bool handled = handle_internal<0>(pi, pj, pk, qi, qj, qk, + inters.rpi(), inters.rpj(), inters.rpk(), + inters.rqi(), inters.rqj(), inters.rqk(), + is_p_first_ip, is_p_last_ip, + is_q_first_ip, is_q_last_ip, + ip_info.is_qi, ip_info.is_qj, + tp_model, inters, ip_index, + p_operation, q_operation); + if ( !handled ) + { + handle_internal<1>(qi, qj, qk, pi, pj, pk, + inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk(), + is_q_first_ip, is_q_last_ip, + is_p_first_ip, is_p_last_ip, + ip_info.is_pi, ip_info.is_pj, + tp_model, inters, ip_index, + q_operation, p_operation); + } + + if ( p_operation != operation_none ) + { + method_type method = endpoint_ip_method(ip_info.is_pi, ip_info.is_pj, + ip_info.is_qi, ip_info.is_qj); + turn_position p_pos = ip_position(is_p_first_ip, is_p_last_ip); + turn_position q_pos = ip_position(is_q_first_ip, is_q_last_ip); + + // handle spikes + + // P is spike and should be handled + if ( !is_p_last + && ip_info.is_pj // this check is redundant (also in is_spike_p) but faster + && inters.i_info().count == 2 + && inters.is_spike_p() ) + { + assign(pi, qi, inters.result(), ip_index, method, operation_blocked, q_operation, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out); + assign(pi, qi, inters.result(), ip_index, method, operation_intersection, q_operation, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out); + } + // Q is spike and should be handled + else if ( !is_q_last + && ip_info.is_qj // this check is redundant (also in is_spike_q) but faster + && inters.i_info().count == 2 + && inters.is_spike_q() ) + { + assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_blocked, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out); + assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_intersection, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out); + } + // no spikes + else + { + assign(pi, qi, inters.result(), ip_index, method, p_operation, q_operation, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, false, tp_model, out); + } + } + } + + return append_last; + } + + // TODO: IT'S ALSO PROBABLE THAT ALL THIS FUNCTION COULD BE INTEGRATED WITH handle_segment + // however now it's lazily calculated and then it would be always calculated + + template + static inline bool handle_internal(Point1 const& /*i1*/, Point1 const& /*j1*/, Point1 const& /*k1*/, + Point2 const& i2, Point2 const& j2, Point2 const& /*k2*/, + RobustPoint1 const& ri1, RobustPoint1 const& rj1, RobustPoint1 const& /*rk1*/, + RobustPoint2 const& ri2, RobustPoint2 const& rj2, RobustPoint2 const& rk2, + bool first1, bool last1, bool first2, bool last2, + bool ip_i2, bool ip_j2, TurnInfo const& tp_model, + IntersectionInfo const& inters, int ip_index, + operation_type & op1, operation_type & op2) + { + boost::ignore_unused_variable_warning(i2); + boost::ignore_unused_variable_warning(j2); + boost::ignore_unused_variable_warning(ip_index); + boost::ignore_unused_variable_warning(tp_model); + + if ( !first2 && !last2 ) + { + if ( first1 ) + { +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR + // may this give false positives for INTs? + typename IntersectionResult::point_type const& + inters_pt = inters.i_info().intersections[ip_index]; + BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt)); + BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt)); +#endif + if ( ip_i2 ) + { + // don't output this IP - for the first point of other geometry segment + op1 = operation_none; + op2 = operation_none; + return true; + } + else if ( ip_j2 ) + { + side_calculator + side_calc(ri2, ri1, rj1, ri2, rj2, rk2); + + std::pair + operations = operations_of_equal(side_calc); + +// TODO: must the above be calculated? +// wouldn't it be enough to check if segments are collinear? + + if ( operations_both(operations, operation_continue) ) + { + if ( op1 != operation_union + || op2 != operation_union + || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) ) + { + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! + bool opposite = inters.d_info().opposite; + + op1 = operation_intersection; + op2 = opposite ? operation_union : operation_intersection; + } + } + else + { + BOOST_ASSERT(operations_combination(operations, operation_intersection, operation_union)); + //op1 = operation_union; + //op2 = operation_union; + } + + return true; + } + // else do nothing - shouldn't be handled this way + } + else if ( last1 ) + { +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR + // may this give false positives for INTs? + typename IntersectionResult::point_type const& + inters_pt = inters.i_info().intersections[ip_index]; + BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt)); + BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt)); +#endif + if ( ip_i2 ) + { + // don't output this IP - for the first point of other geometry segment + op1 = operation_none; + op2 = operation_none; + return true; + } + else if ( ip_j2 ) + { + side_calculator + side_calc(ri2, rj1, ri1, ri2, rj2, rk2); + + std::pair + operations = operations_of_equal(side_calc); + +// TODO: must the above be calculated? +// wouldn't it be enough to check if segments are collinear? + + if ( operations_both(operations, operation_continue) ) + { + if ( op1 != operation_blocked + || op2 != operation_union + || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) ) + { + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! + bool second_going_out = inters.i_info().count > 1; + + op1 = operation_blocked; + op2 = second_going_out ? operation_union : operation_intersection; + } + } + else + { + BOOST_ASSERT(operations_combination(operations, operation_intersection, operation_union)); + //op1 = operation_blocked; + //op2 = operation_union; + } + + return true; + } + // else do nothing - shouldn't be handled this way + } + // else do nothing - shouldn't be handled this way + } + + return false; + } + + static inline method_type endpoint_ip_method(bool ip_pi, bool ip_pj, bool ip_qi, bool ip_qj) + { + if ( (ip_pi || ip_pj) && (ip_qi || ip_qj) ) + return method_touch; + else + return method_touch_interior; + } + + static inline turn_position ip_position(bool is_ip_first_i, bool is_ip_last_j) + { + return is_ip_first_i ? position_front : + ( is_ip_last_j ? position_back : position_middle ); + } + + template + static inline void assign(Point1 const& pi, Point2 const& qi, + IntersectionResult const& result, + int ip_index, + method_type method, + operation_type op0, operation_type op1, + turn_position pos0, turn_position pos1, + bool is_p_first_ip, bool is_q_first_ip, + bool is_p_spike, bool is_q_spike, + TurnInfo const& tp_model, + OutputIterator out) + { + TurnInfo tp = tp_model; + + //geometry::convert(ip, tp.point); + //tp.method = method; + base_turn_handler::assign_point(tp, method, result.template get<0>(), ip_index); + + tp.operations[0].operation = op0; + tp.operations[1].operation = op1; + tp.operations[0].position = pos0; + tp.operations[1].position = pos1; + + if ( result.template get<0>().count > 1 ) + { + // NOTE: is_collinear is NOT set for the first endpoint + // for which there is no preceding segment + + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); + if ( ! is_p_first_ip ) + { + tp.operations[0].is_collinear = op0 != operation_intersection + || is_p_spike; + } + + if ( ! is_q_first_ip ) + { + tp.operations[1].is_collinear = op1 != operation_intersection + || is_q_spike; + } + } + else //if ( result.template get<0>().count == 1 ) + { + if ( op0 == operation_blocked && op1 == operation_intersection ) + { + tp.operations[0].is_collinear = true; + } + else if ( op0 == operation_intersection && op1 == operation_blocked ) + { + tp.operations[1].is_collinear = true; + } + } + + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + *out++ = tp; + } + + template + static inline std::pair operations_of_equal(SidePolicy const& side) + { + int const side_pk_q2 = side.pk_wrt_q2(); + int const side_pk_p = side.pk_wrt_p1(); + int const side_qk_p = side.qk_wrt_p1(); + + // If pk is collinear with qj-qk, they continue collinearly. + // This can be on either side of p1 (== q1), or collinear + // The second condition checks if they do not continue + // oppositely + if ( side_pk_q2 == 0 && side_pk_p == side_qk_p ) + { + return std::make_pair(operation_continue, operation_continue); + } + + // If they turn to same side (not opposite sides) + if ( ! base_turn_handler::opposite(side_pk_p, side_qk_p) ) + { + int const side_pk_q2 = side.pk_wrt_q2(); + // If pk is left of q2 or collinear: p: union, q: intersection + if ( side_pk_q2 != -1 ) + { + return std::make_pair(operation_union, operation_intersection); + } + else + { + return std::make_pair(operation_intersection, operation_union); + } + } + else + { + // They turn opposite sides. If p turns left (or collinear), + // p: union, q: intersection + if ( side_pk_p != -1 ) + { + return std::make_pair(operation_union, operation_intersection); + } + else + { + return std::make_pair(operation_intersection, operation_union); + } + } + } + + static inline bool operations_both( + std::pair const& operations, + operation_type const op) + { + return operations.first == op && operations.second == op; + } + + static inline bool operations_combination( + std::pair const& operations, + operation_type const op1, operation_type const op2) + { + return ( operations.first == op1 && operations.second == op2 ) + || ( operations.first == op2 && operations.second == op1 ); + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp new file mode 100644 index 000000000..3f0d5da1f --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -0,0 +1,336 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP + +#include + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +enum turn_position { position_middle, position_front, position_back }; + +template +struct turn_operation_linear + : public turn_operation +{ + turn_operation_linear() + : position(position_middle) + , is_collinear(false) + {} + + turn_position position; + bool is_collinear; // valid only for Linear geometry +}; + +template +struct side_calculator +{ + typedef boost::geometry::strategy::side::side_by_triangle<> side; // todo: get from coordinate system + + inline side_calculator(Pi const& pi, Pj const& pj, Pk const& pk, + Qi const& qi, Qj const& qj, Qk const& qk) + : m_pi(pi), m_pj(pj), m_pk(pk) + , m_qi(qi), m_qj(qj), m_qk(qk) + {} + + inline int pk_wrt_p1() const { return side::apply(m_pi, m_pj, m_pk); } + inline int pk_wrt_q1() const { return side::apply(m_qi, m_qj, m_pk); } + inline int qk_wrt_p1() const { return side::apply(m_pi, m_pj, m_qk); } + inline int qk_wrt_q1() const { return side::apply(m_qi, m_qj, m_qk); } + + inline int pk_wrt_q2() const { return side::apply(m_qj, m_qk, m_pk); } + inline int qk_wrt_p2() const { return side::apply(m_pj, m_pk, m_qk); } + + Pi const& m_pi; + Pj const& m_pj; + Pk const& m_pk; + Qi const& m_qi; + Qj const& m_qj; + Qk const& m_qk; +}; + +template +struct robust_points +{ + typedef typename geometry::robust_point_type + < + Point1, RobustPolicy + >::type robust_point1_type; + // TODO: define robust_point2_type using Point2? + typedef robust_point1_type robust_point2_type; + + inline robust_points(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + { + geometry::recalculate(m_rpi, pi, robust_policy); + geometry::recalculate(m_rpj, pj, robust_policy); + geometry::recalculate(m_rpk, pk, robust_policy); + geometry::recalculate(m_rqi, qi, robust_policy); + geometry::recalculate(m_rqj, qj, robust_policy); + geometry::recalculate(m_rqk, qk, robust_policy); + } + + robust_point1_type m_rpi, m_rpj, m_rpk; + robust_point2_type m_rqi, m_rqj, m_rqk; +}; + +template +class intersection_info_base + : private robust_points +{ + typedef robust_points base_t; + +public: + typedef Point1 point1_type; + typedef Point2 point2_type; + + typedef typename base_t::robust_point1_type robust_point1_type; + typedef typename base_t::robust_point2_type robust_point2_type; + + typedef side_calculator side_calculator_type; + + intersection_info_base(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + : base_t(pi, pj, pk, qi, qj, qk, robust_policy) + , m_side_calc(base_t::m_rpi, base_t::m_rpj, base_t::m_rpk, + base_t::m_rqi, base_t::m_rqj, base_t::m_rqk) + , m_pi(pi), m_pj(pj), m_pk(pk) + , m_qi(qi), m_qj(qj), m_qk(qk) + {} + + inline Point1 const& pi() const { return m_pi; } + inline Point1 const& pj() const { return m_pj; } + inline Point1 const& pk() const { return m_pk; } + + inline Point2 const& qi() const { return m_qi; } + inline Point2 const& qj() const { return m_qj; } + inline Point2 const& qk() const { return m_qk; } + + inline robust_point1_type const& rpi() const { return base_t::m_rpi; } + inline robust_point1_type const& rpj() const { return base_t::m_rpj; } + inline robust_point1_type const& rpk() const { return base_t::m_rpk; } + + inline robust_point2_type const& rqi() const { return base_t::m_rqi; } + inline robust_point2_type const& rqj() const { return base_t::m_rqj; } + inline robust_point2_type const& rqk() const { return base_t::m_rqk; } + + inline side_calculator_type const& sides() const { return m_side_calc; } + +private: + side_calculator_type m_side_calc; + + point1_type const& m_pi; + point1_type const& m_pj; + point1_type const& m_pk; + point2_type const& m_qi; + point2_type const& m_qj; + point2_type const& m_qk; +}; + +template +class intersection_info_base +{ +public: + typedef Point1 point1_type; + typedef Point2 point2_type; + + typedef Point1 robust_point1_type; + typedef Point2 robust_point2_type; + + typedef side_calculator side_calculator_type; + + intersection_info_base(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + no_rescale_policy const& /*robust_policy*/) + : m_side_calc(pi, pj, pk, qi, qj, qk) + {} + + inline Point1 const& pi() const { return m_side_calc.m_pi; } + inline Point1 const& pj() const { return m_side_calc.m_pj; } + inline Point1 const& pk() const { return m_side_calc.m_pk; } + + inline Point2 const& qi() const { return m_side_calc.m_qi; } + inline Point2 const& qj() const { return m_side_calc.m_qj; } + inline Point2 const& qk() const { return m_side_calc.m_qk; } + + inline Point1 const& rpi() const { return pi(); } + inline Point1 const& rpj() const { return pj(); } + inline Point1 const& rpk() const { return pk(); } + + inline Point2 const& rqi() const { return qi(); } + inline Point2 const& rqj() const { return qj(); } + inline Point2 const& rqk() const { return qk(); } + + inline side_calculator_type const& sides() const { return m_side_calc; } + +private: + side_calculator_type m_side_calc; +}; + + +template +class intersection_info + : public intersection_info_base +{ + typedef intersection_info_base base_t; + + typedef typename strategy_intersection + < + typename cs_tag::type, + Point1, + Point2, + TurnPoint, + RobustPolicy + >::segment_intersection_strategy_type strategy; + +public: + typedef model::referring_segment segment_type1; + typedef model::referring_segment segment_type2; + typedef typename base_t::side_calculator_type side_calculator_type; + + typedef typename strategy::return_type result_type; + typedef typename boost::tuples::element<0, result_type>::type i_info_type; // intersection_info + typedef typename boost::tuples::element<1, result_type>::type d_info_type; // dir_info + + intersection_info(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + : base_t(pi, pj, pk, qi, qj, qk, robust_policy) + , m_result(strategy::apply(segment_type1(pi, pj), + segment_type2(qi, qj), + robust_policy)) + , m_robust_policy(robust_policy) + {} + + inline result_type const& result() const { return m_result; } + inline i_info_type const& i_info() const { return m_result.template get<0>(); } + inline d_info_type const& d_info() const { return m_result.template get<1>(); } + + // TODO: it's more like is_spike_ip_p + inline bool is_spike_p() const + { + if ( base_t::sides().pk_wrt_p1() == 0 ) + { + if ( ! is_ip_j<0>() ) + return false; + + int const qk_p1 = base_t::sides().qk_wrt_p1(); + int const qk_p2 = base_t::sides().qk_wrt_p2(); + + if ( qk_p1 == -qk_p2 ) + { + if ( qk_p1 == 0 ) + { + return is_spike_of_collinear(base_t::pi(), base_t::pj(), base_t::pk()); + } + + return true; + } + } + + return false; + } + + // TODO: it's more like is_spike_ip_q + inline bool is_spike_q() const + { + if ( base_t::sides().qk_wrt_q1() == 0 ) + { + if ( ! is_ip_j<1>() ) + return false; + + int const pk_q1 = base_t::sides().pk_wrt_q1(); + int const pk_q2 = base_t::sides().pk_wrt_q2(); + + if ( pk_q1 == -pk_q2 ) + { + if ( pk_q1 == 0 ) + { + return is_spike_of_collinear(base_t::qi(), base_t::qj(), base_t::qk()); + } + + return true; + } + } + + return false; + } + +private: + template + inline bool is_spike_of_collinear(Point const& i, Point const& j, Point const& k) const + { + typedef model::referring_segment seg_t; + + typedef strategy_intersection + < + typename cs_tag::type, Point, Point, Point, RobustPolicy + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + + typename strategy::return_type result + = strategy::apply(seg_t(i, j), seg_t(j, k), m_robust_policy); + + return result.template get<0>().count == 2; + } + + template + bool is_ip_j() const + { + int arrival = d_info().arrival[OpId]; + bool same_dirs = d_info().dir_a == 0 && d_info().dir_b == 0; + + if ( same_dirs ) + { + if ( i_info().count == 2 ) + { + if ( ! d_info().opposite ) + { + return arrival != -1; + } + else + { + return arrival != -1; + } + } + else + { + return arrival == 0; + } + } + else + { + return arrival == 1; + } + } + + result_type m_result; + RobustPolicy const& m_robust_policy; +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp new file mode 100644 index 000000000..20340a608 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp @@ -0,0 +1,809 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP + +#include +#include + +// TEMP, for spikes detector +//#include + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +template +struct get_turn_info_linear_areal +{ + static const bool handle_spikes = true; + + template + < + typename Point1, + typename Point2, + typename TurnInfo, + typename RobustPolicy, + typename OutputIterator + > + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + TurnInfo const& tp_model, + RobustPolicy const& robust_policy, + OutputIterator out) + { + typedef intersection_info + inters_info; + + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); + + char const method = inters.d_info().how; + + // Copy, to copy possibly extended fields + TurnInfo tp = tp_model; + + // Select method and apply + switch(method) + { + case 'a' : // collinear, "at" + case 'f' : // collinear, "from" + case 's' : // starts from the middle + get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_none, out); + break; + + case 'd' : // disjoint: never do anything + break; + + case 'm' : + { + if ( get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch_interior, out) ) + { + // do nothing + } + else + { + typedef touch_interior + < + TurnInfo + > policy; + + // If Q (1) arrives (1) + if ( inters.d_info().arrival[1] == 1 ) + { + policy::template apply<0>(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), + inters.sides()); + } + else + { + // Swap p/q + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); + policy::template apply<1>(qi, qj, qk, pi, pj, pk, + tp, inters.i_info(), inters.d_info(), + swapped_side_calc); + } + + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + + // this function assumes that 'u' must be set for a spike + calculate_spike_operation(tp.operations[0].operation, + inters, is_p_last); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + *out++ = tp; + } + } + break; + case 'i' : + { + crosses::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info()); + + replace_operations_i(tp.operations[0].operation, tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + break; + case 't' : + { + // Both touch (both arrive there) + if ( get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch, out) ) + { + // do nothing + } + else + { + touch::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + + // workarounds for touch<> not taking spikes into account starts here + // those was discovered empirically + // touch<> is not symmetrical! + // P spikes and Q spikes may produce various operations! + // Only P spikes are valid for L/A + // TODO: this is not optimal solution - think about rewriting touch<> + + if ( tp.operations[0].operation == operation_blocked ) + { + // a spike on P on the same line with Q1 + if ( inters.is_spike_p() ) + { + if ( inters.sides().qk_wrt_p1() == 0 ) + { + tp.operations[0].is_collinear = true; + } + else + { + tp.operations[0].operation = operation_union; + } + } + } + else if ( tp.operations[0].operation == operation_continue + && tp.operations[1].operation == operation_continue ) + { + // P spike on the same line with Q2 (opposite) + if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1() + && inters.is_spike_p() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + } + else if ( tp.operations[0].operation == operation_none + && tp.operations[1].operation == operation_none ) + { + // spike not handled by touch<> + if ( inters.is_spike_p() ) + { + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_union; + + if ( inters.sides().pk_wrt_q2() == 0 ) + { + tp.operations[0].operation = operation_continue; // will be converted to i + tp.operations[0].is_collinear = true; + } + } + } + + // workarounds for touch<> not taking spikes into account ends here + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + + bool ignore_spike + = calculate_spike_operation(tp.operations[0].operation, + inters, is_p_last); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + if ( ! handle_spikes + || ignore_spike + || ! append_opposite_spikes( // for 'i' or 'c' i??? + tp, inters, is_p_last, is_q_last, out) ) + { + *out++ = tp; + } + } + } + break; + case 'e': + { + if ( get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_equal, out) ) + { + // do nothing + } + else + { + tp.operations[0].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + // Both equal + // or collinear-and-ending at intersection point + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + turn_transformer_ec transformer(method_touch); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, + method_touch, append_equal, out) ) + { + *out++ = tp; // no spikes + } + } + else + { + equal_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, qi, + tp, out, inters.i_info(), inters.d_info()); + } + } + } + break; + case 'c' : + { + // Collinear + if ( get_turn_info_for_endpoint( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_collinear, out) ) + { + // do nothing + } + else + { + tp.operations[0].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + method_type method_replace = method_touch_interior; + append_version_c version = append_collinear; + + if ( inters.d_info().arrival[0] == 0 ) + { + // Collinear, but similar thus handled as equal + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + method_replace = method_touch; + version = append_equal; + } + else + { + collinear::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + //method_replace = method_touch_interior; + //version = append_collinear; + } + + turn_transformer_ec transformer(method_replace); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, + method_replace, version, out) ) + { + // no spikes + *out++ = tp; + } + } + else + { + // Is this always 'm' ? + turn_transformer_ec transformer(method_touch_interior); + + // conditionally handle spikes + if ( handle_spikes ) + { + append_opposite_spikes( + tp, inters, is_p_last, is_q_last, out); + } + + // TODO: ignore for spikes? + // E.g. pass is_p_valid = !is_p_last && !is_pj_spike, + // the same with is_q_valid + + collinear_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, pj, pk, qi, qj, qk, + tp, out, inters.i_info(), inters.d_info(), + inters.sides(), transformer); + } + } + } + break; + case '0' : + { + // degenerate points + if (AssignPolicy::include_degenerate) + { + only_convert::apply(tp, inters.i_info()); + + if ( is_p_first + && equals::equals_point_point(pi, tp.point) ) + { + tp.operations[0].position = position_front; + } + else if ( is_p_last + && equals::equals_point_point(pj, tp.point) ) + { + tp.operations[0].position = position_back; + } + // tp.operations[1].position = position_middle; + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + } + break; + default : + { +#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) + std::cout << "TURN: Unknown method: " << method << std::endl; +#endif +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw turn_info_exception(method); +#endif + } + break; + } + + return out; + } + + template + static inline bool calculate_spike_operation(Operation & op, + IntersectionInfo const& inters, + bool is_p_last) + { + bool is_p_spike = ( op == operation_union || op == operation_intersection ) + && ! is_p_last + && inters.is_spike_p(); + + if ( is_p_spike ) + { + bool going_in = false, going_out = false; + + int const pk_q1 = inters.sides().pk_wrt_q1(); + int const pk_q2 = inters.sides().pk_wrt_q2(); + + if ( inters.sides().qk_wrt_q1() <= 0 ) // Q turning R or C + { + going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both + going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them + } + else + { + going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them + going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both + } + + if ( going_in ) + { + op = operation_intersection; + return true; + } + else if ( going_out ) + { + op = operation_union; + return true; + } + } + + return false; + } + + enum append_version_c { append_equal, append_collinear }; + + template + static inline bool append_collinear_spikes(TurnInfo & tp, + IntersectionInfo const& inters, + bool is_p_last, bool /*is_q_last*/, + method_type method, append_version_c version, + OutIt out) + { + // method == touch || touch_interior + // both position == middle + + bool is_p_spike = ( version == append_equal ? + ( tp.operations[0].operation == operation_union + || tp.operations[0].operation == operation_intersection ) : + tp.operations[0].operation == operation_continue ) + && ! is_p_last + && inters.is_spike_p(); + + // TODO: throw an exception for spike in Areal? + /*bool is_q_spike = tp.operations[1].operation == spike_op + && ! is_q_last + && inters.is_spike_q();*/ + + if ( is_p_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_union; + *out++ = tp; + tp.operations[0].operation = operation_continue; // boundary + //tp.operations[1].operation = operation_union; + *out++ = tp; + + return true; + } + + return false; + } + + enum append_version_o { append_touches, append_collinear_opposite }; + + template + static inline bool append_opposite_spikes(TurnInfo & tp, + IntersectionInfo const& inters, + bool is_p_last, bool /*is_q_last*/, + OutIt out) + { + bool is_p_spike = ( Version == append_touches ? + ( tp.operations[0].operation == operation_continue + || tp.operations[0].operation == operation_intersection ) : // i ??? + true ) + && ! is_p_last + && inters.is_spike_p(); + // TODO: throw an exception for spike in Areal? + /*bool is_q_spike = ( Version == append_touches ? + ( tp.operations[1].operation == operation_continue + || tp.operations[1].operation == operation_intersection ) : + true ) + && ! is_q_last + && inters.is_spike_q();*/ + + if ( is_p_spike && ( Version == append_touches || inters.d_info().arrival[0] == 1 ) ) + { + if ( Version == append_touches ) + { + tp.operations[0].is_collinear = true; + //tp.operations[1].is_collinear = false; + tp.method = method_touch; + } + else + { + tp.operations[0].is_collinear = true; + //tp.operations[1].is_collinear = false; + + BOOST_ASSERT(inters.i_info().count > 1); + base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1); + + AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters.i_info(), inters.d_info()); + } + + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_continue; // boundary + *out++ = tp; + tp.operations[0].operation = operation_continue; // boundary + //tp.operations[1].operation = operation_continue; // boundary + *out++ = tp; + + return true; + } + + return false; + } + + static inline void replace_method_and_operations_tm(method_type & method, + operation_type & op0, + operation_type & op1) + { + if ( op0 == operation_blocked && op1 == operation_blocked ) + { + // NOTE: probably only if methods are WRT IPs, not segments! + method = (method == method_touch ? method_equal : method_collinear); + } + + // Assuming G1 is always Linear + if ( op0 == operation_blocked ) + { + op0 = operation_continue; + } + + if ( op1 == operation_blocked ) + { + op1 = operation_continue; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + + // spikes in 'm' + if ( method == method_error ) + { + method = method_touch_interior; + op0 = operation_union; + op1 = operation_union; + } + } + + template + class turn_transformer_ec + { + public: + explicit turn_transformer_ec(method_type method_t_or_m) + : m_method(method_t_or_m) + {} + + template + void operator()(Turn & turn) const + { + operation_type & op0 = turn.operations[0].operation; + operation_type & op1 = turn.operations[1].operation; + + // NOTE: probably only if methods are WRT IPs, not segments! + if ( IsFront + || op0 == operation_intersection || op0 == operation_union + || op1 == operation_intersection || op1 == operation_union ) + { + turn.method = m_method; + } + + turn.operations[0].is_collinear = op0 != operation_blocked; + + // Assuming G1 is always Linear + if ( op0 == operation_blocked ) + { + op0 = operation_continue; + } + + if ( op1 == operation_blocked ) + { + op1 = operation_continue; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + } + + private: + method_type m_method; + }; + + static inline void replace_operations_i(operation_type & /*op0*/, operation_type & op1) + { + // assuming Linear is always the first one + op1 = operation_union; + } + + // NOTE: Spikes may NOT be handled for Linear endpoints because it's not + // possible to define a spike on an endpoint. Areal geometries must + // NOT have spikes at all. One thing that could be done is to throw + // an exception when spike is detected in Areal geometry. + + template + static inline bool get_turn_info_for_endpoint( + Point1 const& pi, Point1 const& /*pj*/, Point1 const& /*pk*/, + Point2 const& qi, Point2 const& /*qj*/, Point2 const& /*qk*/, + bool is_p_first, bool is_p_last, + bool /*is_q_first*/, bool is_q_last, + TurnInfo const& tp_model, + IntersectionInfo const& inters, + method_type /*method*/, + OutputIterator out) + { + namespace ov = overlay; + typedef ov::get_turn_info_for_endpoint get_info_e; + + const std::size_t ip_count = inters.i_info().count; + // no intersection points + if ( ip_count == 0 ) + return false; + + const int segment_index0 = tp_model.operations[0].seg_id.segment_index; + const int segment_index1 = tp_model.operations[1].seg_id.segment_index; + BOOST_ASSERT(segment_index0 >= 0 && segment_index1 >= 0); + + if ( !is_p_first && !is_p_last ) + return false; + +// TODO: is_q_last could probably be replaced by false and removed from parameters + + linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last); + linear_intersections::ip_info const& ip0 = intersections.template get<0>(); + linear_intersections::ip_info const& ip1 = intersections.template get<1>(); + + const bool opposite = inters.d_info().opposite; + + // ANALYSE AND ASSIGN FIRST + + // IP on the first point of Linear Geometry + if ( EnableFirst && is_p_first && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication + { + TurnInfo tp = tp_model; + tp.operations[0].position = position_front; + tp.operations[1].position = position_middle; + + if ( opposite ) // opposite -> collinear + { + tp.operations[0].operation = operation_continue; + tp.operations[1].operation = operation_union; + tp.method = ip0.is_qj ? method_touch : method_touch_interior; + } + else + { + method_type replaced_method = method_touch_interior; + + if ( ip0.is_qj ) + { + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), + inters.rqi(), inters.rqj(), inters.rqk()); + + std::pair + operations = get_info_e::operations_of_equal(side_calc); + + tp.operations[0].operation = operations.first; + tp.operations[1].operation = operations.second; + + replaced_method = method_touch; + } + else + { + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), + inters.rqi(), inters.rpi(), inters.rqj()); + + std::pair + operations = get_info_e::operations_of_equal(side_calc); + + tp.operations[0].operation = operations.first; + tp.operations[1].operation = operations.second; + } + + turn_transformer_ec transformer(replaced_method); + transformer(tp); + } + + // equals<> or collinear<> will assign the second point, + // we'd like to assign the first one + base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0); + + // NOTE: is_collinear is not set for the first endpoint of L + // for which there is no preceding segment + // here is_p_first_ip == true + tp.operations[0].is_collinear = false; + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + + // ANALYSE AND ASSIGN LAST + + // IP on the last point of Linear Geometry + if ( EnableLast + && is_p_last + && ( ip_count > 1 ? (ip1.is_pj && !ip1.is_qi) : (ip0.is_pj && !ip0.is_qi) ) ) // prevents duplication + { + TurnInfo tp = tp_model; + + if ( inters.i_info().count > 1 ) + { + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); + tp.operations[0].is_collinear = true; + tp.operations[1].operation = opposite ? operation_continue : operation_union; + } + else //if ( result.template get<0>().count == 1 ) + { + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpj(), inters.rpi(), + inters.rqi(), inters.rqj(), inters.rqk()); + + std::pair + operations = get_info_e::operations_of_equal(side_calc); + + tp.operations[0].operation = operations.first; + tp.operations[1].operation = operations.second; + + turn_transformer_ec transformer(method_none); + transformer(tp); + + tp.operations[0].is_collinear = tp.both(operation_continue); + } + + tp.method = ( ip_count > 1 ? ip1.is_qj : ip0.is_qj ) ? method_touch : method_touch_interior; + tp.operations[0].operation = operation_blocked; + tp.operations[0].position = position_back; + tp.operations[1].position = position_middle; + + // equals<> or collinear<> will assign the second point, + // we'd like to assign the first one + int ip_index = ip_count > 1 ? 1 : 0; + base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + + return true; + } + + // don't ignore anything for now + return false; + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp new file mode 100644 index 000000000..4f0384877 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp @@ -0,0 +1,720 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP + +#include +#include + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +template +struct get_turn_info_linear_linear +{ + static const bool handle_spikes = true; + + template + < + typename Point1, + typename Point2, + typename TurnInfo, + typename RobustPolicy, + typename OutputIterator + > + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + TurnInfo const& tp_model, + RobustPolicy const& robust_policy, + OutputIterator out) + { + typedef intersection_info + inters_info; + + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); + + char const method = inters.d_info().how; + + // Copy, to copy possibly extended fields + TurnInfo tp = tp_model; + + // Select method and apply + switch(method) + { + case 'a' : // collinear, "at" + case 'f' : // collinear, "from" + case 's' : // starts from the middle + get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_none, out); + break; + + case 'd' : // disjoint: never do anything + break; + + case 'm' : + { + if ( get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch_interior, out) ) + { + // do nothing + } + else + { + typedef touch_interior + < + TurnInfo + > policy; + + // If Q (1) arrives (1) + if ( inters.d_info().arrival[1] == 1) + { + policy::template apply<0>(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), + inters.sides()); + } + else + { + // Swap p/q + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); + + policy::template apply<1>(qi, qj, qk, pi, pj, pk, + tp, inters.i_info(), inters.d_info(), + swapped_side_calc); + } + + if ( tp.operations[0].operation == operation_blocked ) + { + tp.operations[1].is_collinear = true; + } + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + } + break; + case 'i' : + { + crosses::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info()); + + replace_operations_i(tp.operations[0].operation, tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + break; + case 't' : + { + // Both touch (both arrive there) + if ( get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch, out) ) + { + // do nothing + } + else + { + touch::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + // workarounds for touch<> not taking spikes into account starts here + // those was discovered empirically + // touch<> is not symmetrical! + // P spikes and Q spikes may produce various operations! + // TODO: this is not optimal solution - think about rewriting touch<> + + if ( tp.operations[0].operation == operation_blocked + && tp.operations[1].operation == operation_blocked ) + { + // two touching spikes on the same line + if ( inters.is_spike_p() && inters.is_spike_q() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + else + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + } + } + else if ( tp.operations[0].operation == operation_blocked ) + { + // a spike on P on the same line with Q1 + if ( inters.is_spike_p() ) + { + if ( inters.sides().qk_wrt_p1() == 0 ) + { + tp.operations[0].is_collinear = true; + } + else + { + tp.operations[0].operation = operation_union; + } + } + else + { + tp.operations[1].is_collinear = true; + } + } + else if ( tp.operations[1].operation == operation_blocked ) + { + // a spike on Q on the same line with P1 + if ( inters.is_spike_q() ) + { + if ( inters.sides().pk_wrt_q1() == 0 ) + { + tp.operations[1].is_collinear = true; + } + else + { + tp.operations[1].operation = operation_union; + } + } + else + { + tp.operations[0].is_collinear = true; + } + } + else if ( tp.operations[0].operation == operation_continue + && tp.operations[1].operation == operation_continue ) + { + // P spike on the same line with Q2 (opposite) + if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1() + && inters.is_spike_p() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + } + else if ( tp.operations[0].operation == operation_none + && tp.operations[1].operation == operation_none ) + { + // spike not handled by touch<> + bool const is_p = inters.is_spike_p(); + bool const is_q = inters.is_spike_q(); + + if ( is_p || is_q ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + + if ( inters.sides().pk_wrt_q2() == 0 ) + { + tp.operations[0].operation = operation_continue; // will be converted to i + if ( is_p ) + { + tp.operations[0].is_collinear = true; + } + } + + if ( inters.sides().qk_wrt_p2() == 0 ) + { + tp.operations[1].operation = operation_continue; // will be converted to i + if ( is_q ) + { + tp.operations[1].is_collinear = true; + } + } + } + } + + // workarounds for touch<> not taking spikes into account ends here + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + if ( ! handle_spikes + || ! append_opposite_spikes(tp, inters, + is_p_last, is_q_last, + out) ) + { + *out++ = tp; + } + } + } + break; + case 'e': + { + if ( get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_equal, out) ) + { + // do nothing + } + else + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + // Both equal + // or collinear-and-ending at intersection point + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + // transform turn + turn_transformer_ec transformer(method_touch); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, + is_p_last, is_q_last, + method_touch, operation_union, + out) ) + { + *out++ = tp; // no spikes + } + } + else + { + // TODO: ignore for spikes or generate something else than opposite? + + equal_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, qi, tp, out, inters.i_info(), inters.d_info()); + } + } + } + break; + case 'c' : + { + // Collinear + if ( get_turn_info_for_endpoint + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_collinear, out) ) + { + // do nothing + } + else + { + // NOTE: this is for spikes since those are set in the turn_transformer_ec + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + method_type method_replace = method_touch_interior; + operation_type spike_op = operation_continue; + + if ( inters.d_info().arrival[0] == 0 ) + { + // Collinear, but similar thus handled as equal + equal::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + method_replace = method_touch; + spike_op = operation_union; + } + else + { + collinear::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + //method_replace = method_touch_interior; + //spike_op = operation_continue; + } + + // transform turn + turn_transformer_ec transformer(method_replace); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, + is_p_last, is_q_last, + method_replace, spike_op, + out) ) + { + // no spikes + *out++ = tp; + } + } + else + { + // If this always 'm' ? + turn_transformer_ec transformer(method_touch_interior); + + // conditionally handle spikes + if ( handle_spikes ) + { + append_opposite_spikes(tp, inters, + is_p_last, is_q_last, + out); + } + + // TODO: ignore for spikes? + // E.g. pass is_p_valid = !is_p_last && !is_pj_spike, + // the same with is_q_valid + + collinear_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, pj, pk, qi, qj, qk, + tp, out, inters.i_info(), inters.d_info(), inters.sides(), + transformer, !is_p_last, !is_q_last); + } + } + } + break; + case '0' : + { + // degenerate points + if (AssignPolicy::include_degenerate) + { + only_convert::apply(tp, inters.i_info()); + + // if any, only one of those should be true + if ( is_p_first + && equals::equals_point_point(pi, tp.point) ) + { + tp.operations[0].position = position_front; + } + else if ( is_p_last + && equals::equals_point_point(pj, tp.point) ) + { + tp.operations[0].position = position_back; + } + else if ( is_q_first + && equals::equals_point_point(qi, tp.point) ) + { + tp.operations[1].position = position_front; + } + else if ( is_q_last + && equals::equals_point_point(qj, tp.point) ) + { + tp.operations[1].position = position_back; + } + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + } + break; + default : + { +#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) + std::cout << "TURN: Unknown method: " << method << std::endl; +#endif +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw turn_info_exception(method); +#endif + } + break; + } + + return out; + } + + template + static inline bool append_collinear_spikes(TurnInfo & tp, + IntersectionInfo const& inters_info, + bool is_p_last, bool is_q_last, + method_type method, operation_type spike_op, + OutIt out) + { + // method == touch || touch_interior + // both position == middle + + bool is_p_spike = tp.operations[0].operation == spike_op + && ! is_p_last + && inters_info.is_spike_p(); + bool is_q_spike = tp.operations[1].operation == spike_op + && ! is_q_last + && inters_info.is_spike_q(); + + if ( is_p_spike && is_q_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + return true; + } + else if ( is_p_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_union; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + //tp.operations[1].operation = operation_union; + *out++ = tp; + + return true; + } + else if ( is_q_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + //tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + return true; + } + + return false; + } + + enum append_version { append_touches, append_collinear_opposite }; + + template + static inline bool append_opposite_spikes(TurnInfo & tp, + IntersectionInfo const& inters, + bool is_p_last, bool is_q_last, + OutIt out) + { + bool is_p_spike = ( Version == append_touches ? + ( tp.operations[0].operation == operation_continue + || tp.operations[0].operation == operation_intersection ) : + true ) + && ! is_p_last + && inters.is_spike_p(); + bool is_q_spike = ( Version == append_touches ? + ( tp.operations[1].operation == operation_continue + || tp.operations[1].operation == operation_intersection ) : + true ) + && ! is_q_last + && inters.is_spike_q(); + + bool res = false; + + if ( is_p_spike && ( Version == append_touches || inters.d_info().arrival[0] == 1 ) ) + { + if ( Version == append_touches ) + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = false; + tp.method = method_touch; + } + else // Version == append_collinear_opposite + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = false; + + BOOST_ASSERT(inters.i_info().count > 1); + + base_turn_handler::assign_point(tp, method_touch_interior, + inters.i_info(), 1); + + AssignPolicy::apply(tp, inters.pi(), inters.qi(), + inters.i_info(), inters.d_info()); + } + + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + //tp.operations[1].operation = operation_intersection; + *out++ = tp; + + res = true; + } + + if ( is_q_spike && ( Version == append_touches || inters.d_info().arrival[1] == 1 ) ) + { + if ( Version == append_touches ) + { + tp.operations[0].is_collinear = false; + tp.operations[1].is_collinear = true; + tp.method = method_touch; + } + else // Version == append_collinear_opposite + { + tp.operations[0].is_collinear = false; + tp.operations[1].is_collinear = true; + + BOOST_ASSERT(inters.i_info().count > 0); + + base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 0); + + AssignPolicy::apply(tp, inters.pi(), inters.qi(), + inters.i_info(), inters.d_info()); + } + + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + //tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + res = true; + } + + return res; + } + + static inline void replace_method_and_operations_tm(method_type & method, + operation_type & op0, + operation_type & op1) + { + if ( op0 == operation_blocked && op1 == operation_blocked ) + { + // NOTE: probably only if methods are WRT IPs, not segments! + method = (method == method_touch ? method_equal : method_collinear); + op0 = operation_continue; + op1 = operation_continue; + } + else + { + if ( op0 == operation_continue || op0 == operation_blocked ) + { + op0 = operation_intersection; + } + else if ( op0 == operation_intersection ) + { + op0 = operation_union; + } + + if ( op1 == operation_continue || op1 == operation_blocked ) + { + op1 = operation_intersection; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + + // spikes in 'm' + if ( method == method_error ) + { + method = method_touch_interior; + op0 = operation_union; + op1 = operation_union; + } + } + } + + class turn_transformer_ec + { + public: + explicit turn_transformer_ec(method_type method_t_or_m) + : m_method(method_t_or_m) + {} + + template + void operator()(Turn & turn) const + { + operation_type & op0 = turn.operations[0].operation; + operation_type & op1 = turn.operations[1].operation; + + BOOST_ASSERT(op0 != operation_blocked || op1 != operation_blocked ); + + if ( op0 == operation_blocked ) + { + op0 = operation_intersection; + } + else if ( op0 == operation_intersection ) + { + op0 = operation_union; + } + + if ( op1 == operation_blocked ) + { + op1 = operation_intersection; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + + if ( op0 == operation_intersection || op0 == operation_union + || op1 == operation_intersection || op1 == operation_union ) + { + turn.method = m_method; + } + +// TODO: is this correct? +// it's equivalent to comparing to operation_blocked at the beginning of the function + turn.operations[0].is_collinear = op0 != operation_intersection; + turn.operations[1].is_collinear = op1 != operation_intersection; + } + + private: + method_type m_method; + }; + + static inline void replace_operations_i(operation_type & op0, operation_type & op1) + { + if ( op0 == operation_intersection ) + { + op0 = operation_union; + } + + if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 23487872a..513bfd372 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -1,11 +1,17 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP @@ -17,15 +23,14 @@ #include #include #include -#include #include #include -#include - #include #include +#include #include +#include #include @@ -43,17 +48,22 @@ #include #include -#include -#include -#include +#include +#include +#include +#include +#include + +#include +#include +#include #include - #include +#include #include -#include #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION # include @@ -66,10 +76,10 @@ namespace boost { namespace geometry // Silence warning C4127: conditional expression is constant #if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4127) +#pragma warning(push) +#pragma warning(disable : 4127) #endif - + #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace get_turns @@ -93,9 +103,7 @@ template typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, typename Section1, typename Section2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > class get_turns_in_sections { @@ -152,12 +160,12 @@ class get_turns_in_sections < typename tag_cast < - typename geometry::point_type::type, + typename geometry::tag::type, areal_tag - >::type, + >::type, areal_tag >::value - && index1 == 0 + && index1 == 0 && index2 >= n - 2 ; } @@ -165,15 +173,27 @@ class get_turns_in_sections public : // Returns true if terminated, false if interrupted + template static inline bool apply( int source_id1, Geometry1 const& geometry1, Section1 const& sec1, int source_id2, Geometry2 const& geometry2, Section2 const& sec2, bool skip_larger, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { boost::ignore_unused_variable_warning(interrupt_policy); + if ((sec1.duplicate && (sec1.count + 1) < sec1.range_count) + || (sec2.duplicate && (sec2.count + 1) < sec2.range_count)) + { + // Skip sections containig only duplicates. + // They are still important (can indicate non-disjointness) + // but they will be found processing adjacent sections. + // Do NOT skip if they are the ONLY section + return true; + } + cview_type1 cview1(range_by_section(geometry1, sec1)); cview_type2 cview2(range_by_section(geometry2, sec2)); view_type1 view1(cview1); @@ -198,7 +218,7 @@ public : range1_iterator prev1, it1, end1; get_start_point_iterator(sec1, view1, prev1, it1, end1, - index1, ndi1, dir1, sec2.bounding_box); + index1, ndi1, dir1, sec2.bounding_box, robust_policy); // We need a circular iterator because it might run through the closing point. // One circle is actually enough but this one is just convenient. @@ -209,12 +229,12 @@ public : // section 2: [--------------] // section 1: |----|---|---|---|---| for (prev1 = it1++, next1++; - it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box); + it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); ++prev1, ++it1, ++index1, ++next1, ++ndi1) { ever_circling_iterator nd_next1( begin_range_1, end_range_1, next1, true); - advance_to_non_duplicate_next(nd_next1, it1, sec1); + advance_to_non_duplicate_next(nd_next1, it1, sec1, robust_policy); int index2 = sec2.begin_index; int ndi2 = sec2.non_duplicate_index; @@ -222,12 +242,12 @@ public : range2_iterator prev2, it2, end2; get_start_point_iterator(sec2, view2, prev2, it2, end2, - index2, ndi2, dir2, sec1.bounding_box); + index2, ndi2, dir2, sec1.bounding_box, robust_policy); ever_circling_iterator next2(begin_range_2, end_range_2, it2, true); next2++; for (prev2 = it2++, next2++; - it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box); + it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { bool skip = same_source; @@ -253,7 +273,7 @@ public : // Move to the "non duplicate next" ever_circling_iterator nd_next2( begin_range_2, end_range_2, next2, true); - advance_to_non_duplicate_next(nd_next2, it2, sec2); + advance_to_non_duplicate_next(nd_next2, it2, sec2, robust_policy); typedef typename boost::range_value::type turn_info; @@ -268,8 +288,14 @@ public : std::size_t const size_before = boost::size(turns); + bool const is_1_first = sec1.is_non_duplicate_first && index1 == sec1.begin_index; + bool const is_1_last = sec1.is_non_duplicate_last && index1+1 >= sec1.end_index; + bool const is_2_first = sec2.is_non_duplicate_first && index2 == sec2.begin_index; + bool const is_2_last = sec2.is_non_duplicate_last && index2+1 >= sec2.end_index; + TurnPolicy::apply(*prev1, *it1, *nd_next1, *prev2, *it2, *nd_next2, - ti, std::back_inserter(turns)); + is_1_first, is_1_last, is_2_first, is_2_last, + ti, robust_policy, std::back_inserter(turns)); if (InterruptPolicy::enabled) { @@ -294,24 +320,34 @@ private : typedef typename model::referring_segment segment2_type; - template - static inline bool preceding(int dir, Point const& point, Box const& box) + template + static inline bool preceding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) { - return (dir == 1 && get(point) < get(box)) - || (dir == -1 && get(point) > get(box)); + typename robust_point_type::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get(robust_point) < get(box)) + || (dir == -1 && get(robust_point) > get(box)); } - template - static inline bool exceeding(int dir, Point const& point, Box const& box) + template + static inline bool exceeding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) { - return (dir == 1 && get(point) > get(box)) - || (dir == -1 && get(point) < get(box)); + typename robust_point_type::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get(robust_point) > get(box)) + || (dir == -1 && get(robust_point) < get(box)); } - template + template static inline void advance_to_non_duplicate_next(Iterator& next, - RangeIterator const& it, Section const& section) + RangeIterator const& it, Section const& section, RobustPolicy const& robust_policy) { + typedef typename robust_point_type::type robust_point_type; + robust_point_type robust_point_from_it; + robust_point_type robust_point_from_next; + geometry::recalculate(robust_point_from_it, *it, robust_policy); + geometry::recalculate(robust_point_from_next, *next, robust_policy); + // To see where the next segments bend to, in case of touch/intersections // on end points, we need (in case of degenerate/duplicate points) an extra // iterator which moves to the REAL next point, so non duplicate. @@ -322,10 +358,14 @@ private : // So advance to the "non duplicate next" // (the check is defensive, to avoid endless loops) std::size_t check = 0; - while(! detail::disjoint::disjoint_point_point(*it, *next) + while(! detail::disjoint::disjoint_point_point + ( + robust_point_from_it, robust_point_from_next + ) && check++ < section.range_count) { next++; + geometry::recalculate(robust_point_from_next, *next, robust_policy); } } @@ -333,14 +373,14 @@ private : // because of the logistics of "index" (the section-iterator automatically // skips to the begin-point, we loose the index or have to recalculate it) // So we mimic it here - template + template static inline void get_start_point_iterator(Section & section, Range const& range, typename boost::range_iterator::type& it, typename boost::range_iterator::type& prev, typename boost::range_iterator::type& end, int& index, int& ndi, - int dir, Box const& other_bounding_box) + int dir, Box const& other_bounding_box, RobustPolicy const& robust_policy) { it = boost::begin(range) + section.begin_index; end = boost::begin(range) + section.end_index + 1; @@ -348,7 +388,7 @@ private : // Mimic section-iterator: // Skip to point such that section interects other box prev = it++; - for(; it != end && preceding<0>(dir, *it, other_bounding_box); + for(; it != end && preceding<0>(dir, *it, other_bounding_box, robust_policy); prev = it++, index++, ndi++) {} // Go back one step because we want to start completely preceding @@ -380,6 +420,7 @@ template bool Reverse1, bool Reverse2, typename Turns, typename TurnPolicy, + typename RobustPolicy, typename InterruptPolicy > struct section_visitor @@ -388,14 +429,17 @@ struct section_visitor Geometry1 const& m_geometry1; int m_source_id2; Geometry2 const& m_geometry2; + RobustPolicy const& m_rescale_policy; Turns& m_turns; InterruptPolicy& m_interrupt_policy; section_visitor(int id1, Geometry1 const& g1, int id2, Geometry2 const& g2, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& ip) : m_source_id1(id1), m_geometry1(g1) , m_source_id2(id2), m_geometry2(g2) + , m_rescale_policy(robust_policy) , m_turns(turns) , m_interrupt_policy(ip) {} @@ -411,13 +455,12 @@ struct section_visitor Geometry2, Reverse1, Reverse2, Section, Section, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy >::apply( m_source_id1, m_geometry1, sec1, m_source_id2, m_geometry2, sec2, false, + m_rescale_policy, m_turns, m_interrupt_policy); } return true; @@ -429,37 +472,45 @@ template < typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > class get_turns_generic { public: + template static inline void apply( int source_id1, Geometry1 const& geometry1, int source_id2, Geometry2 const& geometry2, - Turns& turns, InterruptPolicy& interrupt_policy) + RobustPolicy const& robust_policy, + Turns& turns, + InterruptPolicy& interrupt_policy) { // First create monotonic sections... typedef typename boost::range_value::type ip_type; typedef typename ip_type::point_type point_type; - typedef model::box box_type; + + typedef model::box + < + typename geometry::robust_point_type + < + point_type, RobustPolicy + >::type + > box_type; typedef typename geometry::sections sections_type; sections_type sec1, sec2; - geometry::sectionalize(geometry1, sec1, 0); - geometry::sectionalize(geometry2, sec2, 1); + geometry::sectionalize(geometry1, robust_policy, true, sec1, 0); + geometry::sectionalize(geometry2, robust_policy, true, sec2, 1); // ... and then partition them, intersecting overlapping sections in visitor method section_visitor < Geometry1, Geometry2, Reverse1, Reverse2, - Turns, TurnPolicy, InterruptPolicy - > visitor(source_id1, geometry1, source_id2, geometry2, turns, interrupt_policy); + Turns, TurnPolicy, RobustPolicy, InterruptPolicy + > visitor(source_id1, geometry1, source_id2, geometry2, robust_policy, turns, interrupt_policy); geometry::partition < @@ -474,13 +525,10 @@ template < typename Range, typename Box, bool ReverseRange, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns_cs { - typedef typename boost::range_value::type turn_info; typedef typename geometry::point_type::type point_type; typedef typename geometry::point_type::type box_point_type; @@ -502,14 +550,16 @@ struct get_turns_cs >::type iterator_type; + template static inline void apply( int source_id1, Range const& range, int source_id2, Box const& box, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, int multi_index = -1, int ring_index = -1) { - if (boost::size(range) <= 1) + if ( boost::size(range) <= 1) { return; } @@ -520,6 +570,8 @@ struct get_turns_cs cview_type cview(range); view_type view(cview); + typename boost::range_size::type segments_count1 = boost::size(view) - 1; + iterator_type it = boost::begin(view); ever_circling_iterator next( @@ -568,9 +620,13 @@ struct get_turns_cs get_turns_with_box(seg_id, source_id2, *prev, *it, *next, bp[0], bp[1], bp[2], bp[3], + // NOTE: some dummy values could be passed below since this would be called only for Polygons and Boxes + index == 0, + unsigned(index) == segments_count1, + robust_policy, turns, interrupt_policy); - // Future performance enhancement: - // return if told by the interrupt policy + // Future performance enhancement: + // return if told by the interrupt policy } } } @@ -596,6 +652,7 @@ private: else return 0; } + template static inline void get_turns_with_box(segment_identifier const& seg_id, int source_id2, // Points from a range: point_type const& rp0, @@ -606,6 +663,9 @@ private: box_point_type const& bp1, box_point_type const& bp2, box_point_type const& bp3, + bool const is_range_first, + bool const is_range_last, + RobustPolicy const& robust_policy, // Output Turns& turns, InterruptPolicy& interrupt_policy) @@ -623,19 +683,27 @@ private: ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 0); TurnPolicy::apply(rp0, rp1, rp2, bp0, bp1, bp2, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + true, false, + ti, robust_policy, std::back_inserter(turns)); ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 1); TurnPolicy::apply(rp0, rp1, rp2, bp1, bp2, bp3, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + false, false, + ti, robust_policy, std::back_inserter(turns)); ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 2); TurnPolicy::apply(rp0, rp1, rp2, bp2, bp3, bp0, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + false, false, + ti, robust_policy, std::back_inserter(turns)); ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 3); TurnPolicy::apply(rp0, rp1, rp2, bp3, bp0, bp1, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + false, true, + ti, robust_policy, std::back_inserter(turns)); if (InterruptPolicy::enabled) { @@ -651,15 +719,15 @@ template < typename Polygon, typename Box, bool Reverse, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns_polygon_cs { + template static inline void apply( int source_id1, Polygon const& polygon, int source_id2, Box const& box, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, int multi_index = -1) { @@ -669,32 +737,118 @@ struct get_turns_polygon_cs < ring_type, Box, Reverse, ReverseBox, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > intersector_type; intersector_type::apply( source_id1, geometry::exterior_ring(polygon), - source_id2, box, turns, interrupt_policy, + source_id2, box, + robust_policy, + turns, interrupt_policy, multi_index, -1); int i = 0; - typename interior_return_type::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); - ++it, ++i) + typename interior_return_type::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it, ++i) { intersector_type::apply( source_id1, *it, - source_id2, box, turns, interrupt_policy, + source_id2, box, + robust_policy, + turns, interrupt_policy, multi_index, i); } } }; + +template +< + typename Multi, typename Box, + bool Reverse, bool ReverseBox, + typename TurnPolicy +> +struct get_turns_multi_polygon_cs +{ + template + static inline void apply( + int source_id1, Multi const& multi, + int source_id2, Box const& box, + RobustPolicy const& robust_policy, + Turns& turns, InterruptPolicy& interrupt_policy) + { + typedef typename boost::range_iterator + < + Multi const + >::type iterator_type; + + int i = 0; + for (iterator_type it = boost::begin(multi); + it != boost::end(multi); + ++it, ++i) + { + // Call its single version + get_turns_polygon_cs + < + typename boost::range_value::type, Box, + Reverse, ReverseBox, + TurnPolicy + >::apply(source_id1, *it, source_id2, box, + robust_policy, turns, interrupt_policy, i); + } + } +}; + + +// GET_TURN_INFO_TYPE + +template +struct topological_tag_base +{ + typedef typename tag_cast::type, pointlike_tag, linear_tag, areal_tag>::type type; +}; + +template ::type, typename Tag2 = typename tag::type, + typename TagBase1 = typename topological_tag_base::type, typename TagBase2 = typename topological_tag_base::type> +struct get_turn_info_type + : overlay::get_turn_info +{}; + +template +struct get_turn_info_type + : overlay::get_turn_info_linear_linear +{}; + +template +struct get_turn_info_type + : overlay::get_turn_info_linear_areal +{}; + +template ::type, typename Tag2 = typename tag::type, + typename TagBase1 = typename topological_tag_base::type, typename TagBase2 = typename topological_tag_base::type> +struct turn_operation_type +{ + typedef overlay::turn_operation type; +}; + +template +struct turn_operation_type +{ + typedef overlay::turn_operation_linear type; +}; + +template +struct turn_operation_type +{ + typedef overlay::turn_operation_linear type; +}; + }} // namespace detail::get_turns #endif // DOXYGEN_NO_DETAIL @@ -710,18 +864,14 @@ template typename GeometryTag1, typename GeometryTag2, typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns : detail::get_turns::get_turns_generic < Geometry1, Geometry2, Reverse1, Reverse2, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > {}; @@ -730,23 +880,19 @@ template < typename Polygon, typename Box, bool ReversePolygon, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns < polygon_tag, box_tag, Polygon, Box, ReversePolygon, ReverseBox, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > : detail::get_turns::get_turns_polygon_cs < Polygon, Box, ReversePolygon, ReverseBox, - Turns, TurnPolicy, InterruptPolicy + TurnPolicy > {}; @@ -755,51 +901,71 @@ template < typename Ring, typename Box, bool ReverseRing, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns < ring_tag, box_tag, Ring, Box, ReverseRing, ReverseBox, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > : detail::get_turns::get_turns_cs < Ring, Box, ReverseRing, ReverseBox, - Turns, TurnPolicy, InterruptPolicy + TurnPolicy > {}; +template +< + typename MultiPolygon, + typename Box, + bool ReverseMultiPolygon, bool ReverseBox, + typename TurnPolicy +> +struct get_turns + < + multi_polygon_tag, box_tag, + MultiPolygon, Box, + ReverseMultiPolygon, ReverseBox, + TurnPolicy + > + : detail::get_turns::get_turns_multi_polygon_cs + < + MultiPolygon, Box, + ReverseMultiPolygon, ReverseBox, + TurnPolicy + > +{}; + + template < typename GeometryTag1, typename GeometryTag2, typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns_reversed { + template static inline void apply( int source_id1, Geometry1 const& g1, int source_id2, Geometry2 const& g2, - Turns& turns, InterruptPolicy& interrupt_policy) + RobustPolicy const& robust_policy, + Turns& turns, + InterruptPolicy& interrupt_policy) { get_turns < GeometryTag2, GeometryTag1, Geometry2, Geometry1, Reverse2, Reverse1, - Turns, TurnPolicy, - InterruptPolicy - >::apply(source_id2, g2, source_id1, g1, turns, interrupt_policy); + TurnPolicy + >::apply(source_id2, g2, source_id1, g1, robust_policy, + turns, interrupt_policy); } }; @@ -817,6 +983,7 @@ struct get_turns_reversed \tparam Turns type of turn-container (e.g. vector of "intersection/turn point"'s) \param geometry1 \param_geometry \param geometry2 \param_geometry +\param robust_policy policy to handle robustness issues \param turns container which will contain turn points \param interrupt_policy policy determining if process is stopped when intersection is found @@ -827,31 +994,20 @@ template typename AssignPolicy, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Turns, typename InterruptPolicy > inline void get_turns(Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { concept::check_concepts_and_equal_dimensions(); - //typedef typename strategy_intersection - // < - // typename cs_tag::type, - // Geometry1, - // Geometry2, - // typename boost::range_value::type - // >::segment_intersection_strategy_type segment_intersection_strategy_type; - - typedef detail::overlay::get_turn_info - < - typename point_type::type, - typename point_type::type, - typename boost::range_value::type, - AssignPolicy - > TurnPolicy; + typedef detail::overlay::get_turn_info TurnPolicy; + //typedef detail::get_turns::get_turn_info_type TurnPolicy; boost::mpl::if_c < @@ -862,8 +1018,7 @@ inline void get_turns(Geometry1 const& geometry1, typename tag::type, Geometry1, Geometry2, Reverse1, Reverse2, - Turns, TurnPolicy, - InterruptPolicy + TurnPolicy >, dispatch::get_turns < @@ -871,17 +1026,17 @@ inline void get_turns(Geometry1 const& geometry1, typename tag::type, Geometry1, Geometry2, Reverse1, Reverse2, - Turns, TurnPolicy, - InterruptPolicy + TurnPolicy > >::type::apply( 0, geometry1, 1, geometry2, + robust_policy, turns, interrupt_policy); } #if defined(_MSC_VER) -#pragma warning(pop) +#pragma warning(pop) #endif }} // namespace boost::geometry diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp index 84ec16f23..92d162032 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp @@ -14,7 +14,18 @@ #include #include #include +#include +#include +#include +#include +#include + +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) +#include +#endif + +#include #include @@ -31,6 +42,7 @@ template typename TurnPoints, typename Indexed, typename Geometry1, typename Geometry2, + typename RobustPolicy, bool Reverse1, bool Reverse2, typename Strategy > @@ -39,10 +51,12 @@ struct sort_in_cluster inline sort_in_cluster(TurnPoints const& turn_points , Geometry1 const& geometry1 , Geometry2 const& geometry2 + , RobustPolicy const& robust_policy , Strategy const& strategy) : m_turn_points(turn_points) , m_geometry1(geometry1) , m_geometry2(geometry2) + , m_rescale_policy(robust_policy) , m_strategy(strategy) {} @@ -51,58 +65,32 @@ private : TurnPoints const& m_turn_points; Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; + RobustPolicy const& m_rescale_policy; Strategy const& m_strategy; typedef typename Indexed::type turn_operation_type; typedef typename geometry::point_type::type point_type; - typedef model::referring_segment segment_type; - // Determine how p/r and p/s are located. - template - static inline void overlap_info(P const& pi, P const& pj, - P const& ri, P const& rj, - P const& si, P const& sj, - bool& pr_overlap, bool& ps_overlap, bool& rs_overlap) + typedef model::point + < + typename detail::robust_type + < + typename select_coordinate_type::type + >::type, + geometry::dimension::value, + typename geometry::coordinate_system::type + > robust_point_type; + + // Still called by #case_102_multi, #case_107_multi + // #case_recursive_boxes_3 + inline void get_situation_map(Indexed const& left, Indexed const& right, + robust_point_type& pi_rob, robust_point_type& pj_rob, + robust_point_type& ri_rob, robust_point_type& rj_rob, + robust_point_type& si_rob, robust_point_type& sj_rob) const { - // Determine how p/r and p/s are located. - // One of them is coming from opposite direction. - - typedef strategy::intersection::relate_cartesian_segments - < - policies::relate::segments_intersection_points - < - segment_type, - segment_type, - segment_intersection_points - > - > policy; - - segment_type p(pi, pj); - segment_type r(ri, rj); - segment_type s(si, sj); - - // Get the intersection point (or two points) - segment_intersection_points pr = policy::apply(p, r); - segment_intersection_points ps = policy::apply(p, s); - segment_intersection_points rs = policy::apply(r, s); - - // Check on overlap - pr_overlap = pr.count == 2; - ps_overlap = ps.count == 2; - rs_overlap = rs.count == 2; - } - - -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH - inline void debug_consider(int order, Indexed const& left, - Indexed const& right, std::string const& header, - bool skip = true, - std::string const& extra = "", bool ret = false - ) const - { - if (skip) return; - + typedef typename geometry::point_type::type point_type; point_type pi, pj, ri, rj, si, sj; + geometry::copy_segment_points(m_geometry1, m_geometry2, left.subject.seg_id, pi, pj); @@ -113,8 +101,86 @@ private : right.subject.other_id, si, sj); + geometry::recalculate(pi_rob, pi, m_rescale_policy); + geometry::recalculate(pj_rob, pj, m_rescale_policy); + geometry::recalculate(ri_rob, ri, m_rescale_policy); + geometry::recalculate(rj_rob, rj, m_rescale_policy); + geometry::recalculate(si_rob, si, m_rescale_policy); + geometry::recalculate(sj_rob, sj, m_rescale_policy); + } + +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO + // This method was still called but did no effect whatsoever on the results, + // with or without robustness-rescaling. + // Probable cause: we rescale in this file ourselves, ignoring passed policy + // TODO: check this more. + // Besides this, it currently does not compile for yet unknown reasons + // (does not find specialization for segment_ratio_type) + // It is currently only called from the Unit Test "multi_intersection.cpp" + + // Determine how p/r and p/s are located. + inline void overlap_info( + robust_point_type const& pi, robust_point_type const& pj, + robust_point_type const& ri, robust_point_type const& rj, + robust_point_type const& si, robust_point_type const& sj, + bool& pr_overlap, bool& ps_overlap, bool& rs_overlap) const + { + // Determine how p/r and p/s are located. + // One of them is coming from opposite direction. + + typedef segment_intersection_points + < + point_type, + typename segment_ratio_type + < + point_type, RobustPolicy + >::type + > intersection_return_type; + + typedef strategy::intersection::relate_cartesian_segments + < + policies::relate::segments_intersection_points + < + intersection_return_type + > + > policy; + + typedef model::referring_segment segment_type; + segment_type p(pi, pj); + segment_type r(ri, rj); + segment_type s(si, sj); + + // Get the intersection point (or two points) + intersection_return_type pr = policy::apply(p, r, m_rescale_policy, pi, pj, ri, rj); + intersection_return_type ps = policy::apply(p, s, m_rescale_policy, pi, pj, si, sj); + intersection_return_type rs = policy::apply(r, s, m_rescale_policy, ri, rj, si, sj); + + // Check on overlap + pr_overlap = pr.count == 2; + ps_overlap = ps.count == 2; + rs_overlap = rs.count == 2; + } +#endif + + +#ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES + inline void debug_consider(int order, Indexed const& left, + Indexed const& right, std::string const& header, + bool skip = true, + std::string const& extra = "", bool ret = false + ) const + { + if (skip) return; + + std::cout << "Case: " << header << " for " << left.index << " / " << right.index << std::endl; + + robust_point_type pi, pj, ri, rj, si, sj; + get_situation_map(left, right, pi, pj, ri, rj, si, sj); + +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO bool prc = false, psc = false, rsc = false; overlap_info(pi, pj, ri, rj, si, sj, prc, psc, rsc); +#endif int const side_ri_p = m_strategy.apply(pi, pj, ri); int const side_rj_p = m_strategy.apply(pi, pj, rj); @@ -123,8 +189,7 @@ private : int const side_si_r = m_strategy.apply(ri, rj, si); int const side_sj_r = m_strategy.apply(ri, rj, sj); - std::cout << "Case: " << header << " for " << left.index << " / " << right.index << std::endl; -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH_MORE +#ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES_MORE std::cout << " Segment p:" << geometry::wkt(pi) << " .. " << geometry::wkt(pj) << std::endl; std::cout << " Segment r:" << geometry::wkt(ri) << " .. " << geometry::wkt(rj) << std::endl; std::cout << " Segment s:" << geometry::wkt(si) << " .. " << geometry::wkt(sj) << std::endl; @@ -141,7 +206,9 @@ private : << " ri//p: " << side_ri_p << " si//p: " << side_si_p << " si//r: " << side_si_r +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO << " cnts: " << int(prc) << "," << int(psc) << "," << int(rsc) +#endif //<< " idx: " << left.index << "/" << right.index ; @@ -183,7 +250,7 @@ private : } else { -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << "ux/ux unhandled" << std::endl; #endif } @@ -226,7 +293,7 @@ private : } else { -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) // this still happens in the traverse.cpp test std::cout << " iu/ux unhandled" << std::endl; #endif @@ -270,7 +337,7 @@ private : // Default case, should not occur -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << "ix/ix unhandled" << std::endl; #endif //debug_consider(0, left, right, header, false, "-> return", ret); @@ -280,7 +347,7 @@ private : inline bool consider_iu_iu(Indexed const& left, Indexed const& right, - std::string const& header) const + std::string const& header, bool redo = false) const { //debug_consider(0, left, right, header); @@ -298,16 +365,8 @@ private : return true; } - point_type pi, pj, ri, rj, si, sj; - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.seg_id, - pi, pj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.other_id, - ri, rj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - right.subject.other_id, - si, sj); + robust_point_type pi, pj, ri, rj, si, sj; + get_situation_map(left, right, pi, pj, ri, rj, si, sj); int const side_ri_p = m_strategy.apply(pi, pj, ri); int const side_si_p = m_strategy.apply(pi, pj, si); @@ -337,14 +396,18 @@ private : debug_consider(0, left, right, header, false, "opp.", ret); return ret; } -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << " iu/iu coming from opposite unhandled" << std::endl; #endif } +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO // We need EXTRA information here: are p/r/s overlapping? bool pr_ov = false, ps_ov = false, rs_ov = false; overlap_info(pi, pj, ri, rj, si, sj, pr_ov, ps_ov, rs_ov); +#else + // std::cout << "Boost.Geometry: skipping overlap_info" << std::endl; +#endif // One coming from right (#83,#90) // One coming from left (#90, #94, #95) @@ -352,12 +415,14 @@ private : { bool ret = false; +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO if (pr_ov || ps_ov) { int r = side_ri_p != 0 ? side_ri_p : side_si_p; ret = r * side_si_r == 1; } else +#endif { ret = side_si_r == 1; } @@ -374,6 +439,7 @@ private : // Take the one NOT overlapping bool ret = false; bool found = false; +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO if (pr_ov && ! ps_ov) { ret = true; @@ -384,6 +450,7 @@ private : ret = false; found = true; } +#endif debug_consider(0, left, right, header, false, "aligned", ret); if (found) @@ -392,10 +459,17 @@ private : } } -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << " iu/iu unhandled" << std::endl; debug_consider(0, left, right, header, false, "unhandled", left.index < right.index); #endif + if (! redo) + { + // In some cases behaviour is not symmetrical. TODO: fix this properly + // OR: alternatively we might consider calling all these functions one-way anyway + return ! consider_iu_iu(right, left, header, true); + } + return left.index < right.index; } @@ -404,16 +478,8 @@ private : { debug_consider(0, left, right, header); - point_type pi, pj, ri, rj, si, sj; - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.seg_id, - pi, pj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - left.subject.other_id, - ri, rj); - geometry::copy_segment_points(m_geometry1, m_geometry2, - right.subject.other_id, - si, sj); + robust_point_type pi, pj, ri, rj, si, sj; + get_situation_map(left, right, pi, pj, ri, rj, si, sj); int const side_ri_p = m_strategy.apply(pi, pj, ri); int const side_si_p = m_strategy.apply(pi, pj, si); @@ -518,7 +584,7 @@ public : // Now we have no clue how to sort. -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << " Consider: " << operation_char(m_turn_points[left.index].operations[0].operation) << operation_char(m_turn_points[left.index].operations[1].operation) << "/" << operation_char(m_turn_points[right.index].operations[0].operation) @@ -633,12 +699,14 @@ template typename TurnPoints, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Strategy > inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, TurnPoints& turn_points, operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy& robust_policy, Strategy const& strategy) { // First inspect and (possibly) discard rows @@ -653,17 +721,17 @@ inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, TurnPoints, IndexType, Geometry1, Geometry2, + RobustPolicy, Reverse1, Reverse2, Strategy - >(turn_points, geometry1, geometry2, strategy)); + >(turn_points, geometry1, geometry2, robust_policy, strategy)); - -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) typedef typename IndexType::type operations_type; operations_type const& op = turn_points[begin_cluster->index].operations[begin_cluster->operation_index]; - std::cout << "Clustered points on equal distance " << op.enriched.distance << std::endl; - std::cout << "->Indexes "; + std::cout << std::endl << "Clustered points on equal distance " << op.fraction << std::endl; + std::cout << "->Indexes "; for (Iterator it = begin_cluster; it != end_cluster; ++it) { std::cout << " " << it->index; diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp new file mode 100644 index 000000000..dd041b0d7 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp @@ -0,0 +1,84 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP + + +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + +template +struct intersection_box_box +{ + template + < + typename Box1, typename Box2, + typename RobustPolicy, + typename BoxOut, + typename Strategy + > + static inline bool apply(Box1 const& box1, + Box2 const& box2, + RobustPolicy const& robust_policy, + BoxOut& box_out, + Strategy const& strategy) + { + typedef typename coordinate_type::type ct; + + ct min1 = get(box1); + ct min2 = get(box2); + ct max1 = get(box1); + ct max2 = get(box2); + + if (max1 < min2 || max2 < min1) + { + return false; + } + // Set dimensions of output coordinate + set(box_out, min1 < min2 ? min2 : min1); + set(box_out, max1 > max2 ? max2 : max1); + + return intersection_box_box + ::apply(box1, box2, robust_policy, box_out, strategy); + } +}; + +template +struct intersection_box_box +{ + template + < + typename Box1, typename Box2, + typename RobustPolicy, + typename BoxOut, + typename Strategy + > + static inline bool apply(Box1 const&, Box2 const&, + RobustPolicy const&, BoxOut&, Strategy const&) + { + return true; + } +}; + + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index f0307eaf8..a13a62745 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -1,6 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -28,8 +33,17 @@ #include #include #include + +#include +#include +#include + #include +#include +#include + + #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) #include #endif @@ -47,25 +61,58 @@ struct intersection_segment_segment_point template < typename Segment1, typename Segment2, + typename RobustPolicy, typename OutputIterator, typename Strategy > static inline OutputIterator apply(Segment1 const& segment1, - Segment2 const& segment2, OutputIterator out, + Segment2 const& segment2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) { typedef typename point_type::type point_type; + typedef typename geometry::robust_point_type + < + typename geometry::point_type::type, + RobustPolicy + >::type robust_point_type; + + // TODO: rescale segment -> robust points + robust_point_type pi_rob, pj_rob, qi_rob, qj_rob; + { + // Workaround: + point_type pi, pj, qi, qj; + assign_point_from_index<0>(segment1, pi); + assign_point_from_index<1>(segment1, pj); + assign_point_from_index<0>(segment2, qi); + assign_point_from_index<1>(segment2, qj); + geometry::recalculate(pi_rob, pi, robust_policy); + geometry::recalculate(pj_rob, pj, robust_policy); + geometry::recalculate(qi_rob, qi, robust_policy); + geometry::recalculate(qj_rob, qj, robust_policy); + } + // Get the intersection point (or two points) - segment_intersection_points is - = strategy::intersection::relate_cartesian_segments + typedef segment_intersection_points + < + point_type, + typename segment_ratio_type + < + point_type, RobustPolicy + >::type + > intersection_return_type; + + typedef strategy::intersection::relate_cartesian_segments < policies::relate::segments_intersection_points < - Segment1, - Segment2, - segment_intersection_points + intersection_return_type > - >::apply(segment1, segment2); + > policy; + + intersection_return_type is = policy::apply(segment1, segment2, + robust_policy, pi_rob, pj_rob, qi_rob, qj_rob); for (std::size_t i = 0; i < is.count; i++) { @@ -83,18 +130,25 @@ struct intersection_linestring_linestring_point template < typename Linestring1, typename Linestring2, + typename RobustPolicy, typename OutputIterator, typename Strategy > static inline OutputIterator apply(Linestring1 const& linestring1, - Linestring2 const& linestring2, OutputIterator out, + Linestring2 const& linestring2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) { typedef typename point_type::type point_type; - typedef detail::overlay::turn_info turn_info; + typedef detail::overlay::turn_info + < + point_type, + typename segment_ratio_type::type + > turn_info; std::deque turns; - geometry::get_intersection_points(linestring1, linestring2, turns); + geometry::get_intersection_points(linestring1, linestring2, robust_policy, turns); for (typename boost::range_iterator const>::type it = boost::begin(turns); it != boost::end(turns); ++it) @@ -120,7 +174,7 @@ struct intersection_of_linestring_with_areal { #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) template - static inline void debug_follow(Turn const& turn, Operation op, + static inline void debug_follow(Turn const& turn, Operation op, int index) { std::cout << index @@ -138,9 +192,11 @@ struct intersection_of_linestring_with_areal template < typename LineString, typename Areal, + typename RobustPolicy, typename OutputIterator, typename Strategy > static inline OutputIterator apply(LineString const& linestring, Areal const& areal, + RobustPolicy const& robust_policy, OutputIterator out, Strategy const& ) { @@ -158,8 +214,11 @@ struct intersection_of_linestring_with_areal > follower; typedef typename point_type::type point_type; - - typedef detail::overlay::traversal_turn_info turn_info; + typedef detail::overlay::traversal_turn_info + < + point_type, + typename geometry::segment_ratio_type::type + > turn_info; std::deque turns; detail::get_turns::no_interrupt_policy policy; @@ -167,12 +226,12 @@ struct intersection_of_linestring_with_areal < false, (OverlayType == overlay_intersection ? ReverseAreal : !ReverseAreal), - detail::overlay::calculate_distance_policy - >(linestring, areal, turns, policy); + detail::overlay::assign_null_policy + >(linestring, areal, robust_policy, turns, policy); if (turns.empty()) { - // No intersection points, it is either completely + // No intersection points, it is either completely // inside (interior + borders) // or completely outside @@ -184,8 +243,7 @@ struct intersection_of_linestring_with_areal return out; } - - if (follower::included(border_point, areal)) + if (follower::included(border_point, areal, robust_policy)) { LineStringOut copy; geometry::convert(linestring, copy); @@ -206,7 +264,7 @@ struct intersection_of_linestring_with_areal ( linestring, areal, geometry::detail::overlay::operation_intersection, - turns, out + turns, robust_policy, out ); } }; @@ -335,22 +393,23 @@ template < typename Linestring, typename Box, typename GeometryOut, - overlay_type OverlayType, bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert < Linestring, Box, GeometryOut, - OverlayType, + overlay_intersection, Reverse1, Reverse2, ReverseOut, linestring_tag, box_tag, linestring_tag, false, true, false > { - template + template static inline OutputIterator apply(Linestring const& linestring, - Box const& box, OutputIterator out, Strategy const& ) + Box const& box, + RobustPolicy const& , + OutputIterator out, Strategy const& ) { typedef typename point_type::type point_type; strategy::intersection::liang_barsky lb_strategy; @@ -424,9 +483,11 @@ struct intersection_insert false, true, false > { - template + template static inline OutputIterator apply(Segment const& segment, - Box const& box, OutputIterator out, Strategy const& ) + Box const& box, + RobustPolicy const& ,// TODO: propagate to clip_range_with_box + OutputIterator out, Strategy const& ) { geometry::segment_view range(segment); @@ -456,19 +517,25 @@ struct intersection_insert Areal1, Areal2, false > { - template + template static inline OutputIterator apply(Geometry1 const& geometry1, - Geometry2 const& geometry2, OutputIterator out, Strategy const& ) + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) { - typedef detail::overlay::turn_info turn_info; + typedef detail::overlay::turn_info + < + PointOut, + typename segment_ratio_type::type + > turn_info; std::vector turns; detail::get_turns::no_interrupt_policy policy; geometry::get_turns < false, false, detail::overlay::assign_null_policy - >(geometry1, geometry2, turns, policy); + >(geometry1, geometry2, robust_policy, turns, policy); for (typename std::vector::const_iterator it = turns.begin(); it != turns.end(); ++it) { @@ -488,9 +555,11 @@ template > struct intersection_insert_reversed { - template + template static inline OutputIterator apply(Geometry1 const& g1, - Geometry2 const& g2, OutputIterator out, + Geometry2 const& g2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& strategy) { return intersection_insert @@ -498,12 +567,138 @@ struct intersection_insert_reversed Geometry2, Geometry1, GeometryOut, OverlayType, Reverse2, Reverse1, ReverseOut - >::apply(g2, g1, out, strategy); + >::apply(g2, g1, robust_policy, out, strategy); } }; +// dispatch for non-areal geometries +template +< + typename Geometry1, typename Geometry2, typename GeometryOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename TagIn1, typename TagIn2 +> +struct intersection_insert + < + Geometry1, Geometry2, GeometryOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + TagIn1, TagIn2, linestring_tag, + false, false, false + > : intersection_insert + < + Geometry1, Geometry2, GeometryOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + typename tag_cast::type, + typename tag_cast::type, + linestring_tag, + false, false, false + > +{}; + + +// dispatch for difference/intersection of linear geometries +template +< + typename Linear1, typename Linear2, typename LineStringOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Linear1, Linear2, LineStringOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + linear_tag, linear_tag, linestring_tag, + false, false, false + > : detail::overlay::linear_linear_linestring + < + Linear1, Linear2, LineStringOut, OverlayType + > +{}; + + +// dispatch for difference/intersection of point-like geometries + +template +< + typename Point1, typename Point2, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Point1, Point2, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + point_tag, point_tag, point_tag, + false, false, false + > : detail::overlay::point_point_point + < + Point1, Point2, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint, typename Point, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + MultiPoint, Point, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + multi_point_tag, point_tag, point_tag, + false, false, false + > : detail::overlay::multipoint_point_point + < + MultiPoint, Point, PointOut, OverlayType + > +{}; + + +template +< + typename Point, typename MultiPoint, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Point, MultiPoint, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + point_tag, multi_point_tag, point_tag, + false, false, false + > : detail::overlay::point_multipoint_point + < + Point, MultiPoint, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint1, typename MultiPoint2, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + MultiPoint1, MultiPoint2, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + multi_point_tag, multi_point_tag, point_tag, + false, false, false + > : detail::overlay::multipoint_multipoint_point + < + MultiPoint1, MultiPoint2, PointOut, OverlayType + > +{}; + + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH @@ -519,35 +714,37 @@ template bool ReverseSecond, overlay_type OverlayType, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename OutputIterator, typename Strategy > inline OutputIterator insert(Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy robust_policy, OutputIterator out, Strategy const& strategy) { return boost::mpl::if_c + < + geometry::reverse_dispatch::type::value, + geometry::dispatch::intersection_insert_reversed < - geometry::reverse_dispatch::type::value, - geometry::dispatch::intersection_insert_reversed - < - Geometry1, Geometry2, - GeometryOut, - OverlayType, - overlay::do_reverse::value>::value, - overlay::do_reverse::value, ReverseSecond>::value, - overlay::do_reverse::value>::value - >, - geometry::dispatch::intersection_insert - < - Geometry1, Geometry2, - GeometryOut, - OverlayType, - geometry::detail::overlay::do_reverse::value>::value, - geometry::detail::overlay::do_reverse::value, ReverseSecond>::value - > - >::type::apply(geometry1, geometry2, out, strategy); + Geometry1, Geometry2, + GeometryOut, + OverlayType, + overlay::do_reverse::value>::value, + overlay::do_reverse::value, ReverseSecond>::value, + overlay::do_reverse::value>::value + >, + geometry::dispatch::intersection_insert + < + Geometry1, Geometry2, + GeometryOut, + OverlayType, + geometry::detail::overlay::do_reverse::value>::value, + geometry::detail::overlay::do_reverse::value, ReverseSecond>::value + > + >::type::apply(geometry1, geometry2, robust_policy, out, strategy); } @@ -586,10 +783,14 @@ inline OutputIterator intersection_insert(Geometry1 const& geometry1, concept::check(); concept::check(); + typedef typename Strategy::rescale_policy_type rescale_policy_type; + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, geometry2); + return detail::intersection::insert < GeometryOut, false, overlay_intersection - >(geometry1, geometry2, out, strategy); + >(geometry1, geometry2, robust_policy, out, strategy); } @@ -623,12 +824,18 @@ inline OutputIterator intersection_insert(Geometry1 const& geometry1, concept::check(); concept::check(); + typedef typename geometry::rescale_policy_type + < + typename geometry::point_type::type // TODO from both + >::type rescale_policy_type; + typedef strategy_intersection < typename cs_tag::type, Geometry1, Geometry2, - typename geometry::point_type::type + typename geometry::point_type::type, + rescale_policy_type > strategy; return intersection_insert(geometry1, geometry2, out, diff --git a/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp b/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp new file mode 100644 index 000000000..944c8fd56 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp @@ -0,0 +1,327 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +< + typename LineStringOut, + overlay_type OverlayType, + typename Geometry, + typename GeometryTag +> +struct linear_linear_no_intersections; + + +template +struct linear_linear_no_intersections + < + LineStringOut, overlay_difference, LineString, linestring_tag + > +{ + template + static inline OutputIterator apply(LineString const& linestring, + OutputIterator oit) + { + LineStringOut ls_out; + geometry::convert(linestring, ls_out); + *oit++ = ls_out; + return oit; + } +}; + + +template +struct linear_linear_no_intersections + < + LineStringOut, + overlay_difference, + MultiLineString, + multi_linestring_tag + > +{ + template + static inline OutputIterator apply(MultiLineString const& multilinestring, + OutputIterator oit) + { + for (typename boost::range_iterator::type + it = boost::begin(multilinestring); + it != boost::end(multilinestring); ++it) + { + LineStringOut ls_out; + geometry::convert(*it, ls_out); + *oit++ = ls_out; + } + return oit; + } +}; + + +template +struct linear_linear_no_intersections + < + LineStringOut, overlay_intersection, Geometry, GeometryTag + > +{ + template + static inline OutputIterator apply(Geometry const&, + OutputIterator oit) + { + return oit; + } +}; + + + + + + + +template +< + typename Linear1, + typename Linear2, + typename LinestringOut, + overlay_type OverlayType, + bool EnableFilterContinueTurns = false, + bool EnableRemoveDuplicateTurns = false, + bool EnableDegenerateTurns = true, +#ifdef BOOST_GEOMETRY_INTERSECTION_DO_NOT_INCLUDE_ISOLATED_POINTS + bool EnableFollowIsolatedPoints = false +#else + bool EnableFollowIsolatedPoints = true +#endif +> +class linear_linear_linestring +{ +protected: + struct assign_policy + { + static bool const include_no_turn = false; + static bool const include_degenerate = EnableDegenerateTurns; + static bool const include_opposite = false; + + template + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply(Info& , Point1 const& , Point2 const& , + IntersectionInfo const& , DirInfo const& ) + { + } + }; + + + template + < + typename Turns, + typename LinearGeometry1, + typename LinearGeometry2 + > + static inline void compute_turns(Turns& turns, + LinearGeometry1 const& linear1, + LinearGeometry2 const& linear2) + { + turns.clear(); + geometry::detail::relate::turns::get_turns + < + LinearGeometry1, + LinearGeometry2, + detail::get_turns::get_turn_info_type + < + LinearGeometry1, + LinearGeometry2, + assign_policy + > + >::apply(turns, linear1, linear2); + } + + + template + < + overlay_type OverlayTypeForFollow, + bool FollowIsolatedPoints, + typename Turns, + typename LinearGeometry1, + typename LinearGeometry2, + typename OutputIterator + > + static inline OutputIterator + sort_and_follow_turns(Turns& turns, + LinearGeometry1 const& linear1, + LinearGeometry2 const& linear2, + OutputIterator oit) + { + // remove turns that have no added value + turns::filter_continue_turns + < + Turns, + EnableFilterContinueTurns && OverlayType != overlay_intersection + >::apply(turns); + + // sort by seg_id, distance, and operation + std::sort(boost::begin(turns), boost::end(turns), + detail::turns::less_seg_fraction_other_op<>()); + + // remove duplicate turns + turns::remove_duplicate_turns + < + Turns, EnableRemoveDuplicateTurns + >::apply(turns); + + return detail::overlay::following::linear::follow + < + LinestringOut, + LinearGeometry1, + LinearGeometry2, + OverlayTypeForFollow, + FollowIsolatedPoints, + !EnableFilterContinueTurns || OverlayType == overlay_intersection + >::apply(linear1, linear2, boost::begin(turns), boost::end(turns), + oit); + } + +public: + template + < + typename RobustPolicy, typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(Linear1 const& linear1, + Linear2 const& linear2, + RobustPolicy const&, + OutputIterator oit, + Strategy const& ) + { + typedef typename detail::relate::turns::get_turns + < + Linear1, Linear2 + >::turn_info turn_info; + + typedef std::vector turns_container; + + turns_container turns; + compute_turns(turns, linear1, linear2); + + if ( turns.empty() ) + { + // the two linear geometries are disjoint + return linear_linear_no_intersections + < + LinestringOut, + OverlayType, + Linear1, + typename tag::type + >::apply(linear1, oit); + } + + return sort_and_follow_turns + < + OverlayType, + EnableFollowIsolatedPoints + && OverlayType == overlay_intersection + >(turns, linear1, linear2, oit); + } +}; + + + + +template +< + typename Linear1, + typename Linear2, + typename LinestringOut, + bool EnableFilterContinueTurns, + bool EnableRemoveDuplicateTurns, + bool EnableDegenerateTurns, + bool EnableFollowIsolatedPoints +> +struct linear_linear_linestring + < + Linear1, Linear2, LinestringOut, overlay_union, + EnableFilterContinueTurns, EnableRemoveDuplicateTurns, + EnableDegenerateTurns, EnableFollowIsolatedPoints + > +{ + template + < + typename RobustPolicy, typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(Linear1 const& linear1, + Linear2 const& linear2, + RobustPolicy const& robust_policy, + OutputIterator oit, + Strategy const& strategy) + { + oit = linear_linear_no_intersections + < + LinestringOut, + overlay_difference, + Linear1, + typename tag::type + >::apply(linear1, oit); + + return linear_linear_linestring + < + Linear2, Linear1, LinestringOut, overlay_difference, + EnableFilterContinueTurns, EnableRemoveDuplicateTurns, + EnableDegenerateTurns, EnableFollowIsolatedPoints + >::apply(linear2, linear1, robust_policy, oit, strategy); + } +}; + + + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 39432dc50..29e0dad0c 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -17,7 +18,6 @@ #include -#include #include #include #include @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -34,6 +35,9 @@ #include #include #include +#include + +#include #ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE @@ -116,8 +120,8 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, // Silence warning C4127: conditional expression is constant #if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4127) +#pragma warning(push) +#pragma warning(disable : 4127) #endif // Union: return either of them @@ -131,7 +135,7 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, } #if defined(_MSC_VER) -#pragma warning(pop) +#pragma warning(pop) #endif @@ -154,20 +158,21 @@ template > struct overlay { - template + template static inline OutputIterator apply( Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, OutputIterator out, Strategy const& ) { - if (geometry::num_points(geometry1) == 0 - && geometry::num_points(geometry2) == 0) + if ( geometry::num_points(geometry1) == 0 + && geometry::num_points(geometry2) == 0 ) { return out; } - if (geometry::num_points(geometry1) == 0 - || geometry::num_points(geometry2) == 0) + if ( geometry::num_points(geometry1) == 0 + || geometry::num_points(geometry2) == 0 ) { return return_if_one_input_is_empty < @@ -176,7 +181,11 @@ struct overlay } typedef typename geometry::point_type::type point_type; - typedef detail::overlay::traversal_turn_info turn_info; + typedef detail::overlay::traversal_turn_info + < + point_type, + typename geometry::segment_ratio_type::type + > turn_info; typedef std::deque container_type; typedef std::deque @@ -197,8 +206,8 @@ std::cout << "get turns" << std::endl; geometry::get_turns < Reverse1, Reverse2, - detail::overlay::calculate_distance_policy - >(geometry1, geometry2, turn_points, policy); + detail::overlay::assign_null_policy + >(geometry1, geometry2, robust_policy, turn_points, policy); #ifdef BOOST_GEOMETRY_TIME_OVERLAY std::cout << "get_turns: " << timer.elapsed() << std::endl; @@ -213,6 +222,7 @@ std::cout << "enrich" << std::endl; ? geometry::detail::overlay::operation_union : geometry::detail::overlay::operation_intersection, geometry1, geometry2, + robust_policy, side_strategy); #ifdef BOOST_GEOMETRY_TIME_OVERLAY @@ -233,6 +243,7 @@ std::cout << "traverse" << std::endl; Direction == overlay_union ? geometry::detail::overlay::operation_union : geometry::detail::overlay::operation_intersection, + robust_policy, turn_points, rings ); @@ -263,8 +274,8 @@ std::cout << "traverse" << std::endl; ring_identifier id(2, 0, -1); for (typename boost::range_iterator::type it = boost::begin(rings); - it != boost::end(rings); - ++it) + it != boost::end(rings); + ++it) { selected[id] = properties(*it, true); selected[id].reversed = ReverseOut; @@ -288,24 +299,6 @@ std::cout << "traverse" << std::endl; }; -// Metafunction helper for intersection and union -template -struct do_reverse {}; - -template <> -struct do_reverse : boost::false_type {}; - -template <> -struct do_reverse : boost::true_type {}; - -template <> -struct do_reverse : boost::true_type {}; - -template <> -struct do_reverse : boost::false_type {}; - - - }} // namespace detail::overlay #endif // DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp b/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp new file mode 100644 index 000000000..0af062d27 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp @@ -0,0 +1,435 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +// struct for copying points of the pointlike geometries to output +template +< + typename PointOut, + typename GeometryIn, + typename TagIn = typename tag::type +> +struct copy_points + : not_implemented +{}; + +template +struct copy_points +{ + template + static inline void apply(PointIn const& point_in, + OutputIterator& oit) + { + PointOut point_out; + geometry::convert(point_in, point_out); + *oit++ = point_out; + } +}; + + +template +struct copy_points +{ + template + static inline void apply(MultiPointIn const& multi_point_in, + OutputIterator& oit) + { + for (typename boost::range_iterator::type + it = boost::begin(multi_point_in); + it != boost::end(multi_point_in); ++it) + { + PointOut point_out; + geometry::convert(*it, point_out); + *oit++ = point_out; + } + } +}; + + + +// action struct for difference/intersection +template +struct action_selector_pl_pl +{}; + +template +struct action_selector_pl_pl +{ + template + < + typename Point, + typename OutputIterator + > + static inline void apply(Point const& point, + bool is_common, + OutputIterator& oit) + { + if ( is_common ) + { + copy_points::apply(point, oit); + } + } +}; + + + +template +struct action_selector_pl_pl +{ + template + < + typename Point, + typename OutputIterator + > + static inline void apply(Point const& point, + bool is_common, + OutputIterator& oit) + { + if ( !is_common ) + { + copy_points::apply(point, oit); + } + } +}; + + +//=========================================================================== + +// difference/intersection of point-point +template +< + typename Point1, + typename Point2, + typename PointOut, + overlay_type OverlayType +> +struct point_point_point +{ + template + static inline OutputIterator apply(Point1 const& point1, + Point2 const& point2, + RobustPolicy const& , + OutputIterator oit, + Strategy const&) + { + action_selector_pl_pl + < + PointOut, OverlayType + >::apply(point1, + detail::equals::equals_point_point(point1, point2), + oit); + + return oit; + } +}; + + + +// difference of multipoint-point +// +// the apply method in the following struct is called only for +// difference; for intersection the reversal will +// always call the point-multipoint version +template +< + typename MultiPoint, + typename Point, + typename PointOut, + overlay_type OverlayType +> +struct multipoint_point_point +{ + template + static inline OutputIterator apply(MultiPoint const& multipoint, + Point const& point, + RobustPolicy const& , + OutputIterator oit, + Strategy const&) + { + BOOST_ASSERT( OverlayType == overlay_difference ); + + for (typename boost::range_iterator::type + it = boost::begin(multipoint); + it != boost::end(multipoint); ++it) + { + action_selector_pl_pl + < + PointOut, OverlayType + >::apply(*it, + detail::equals::equals_point_point(*it, point), + oit); + } + + return oit; + } +}; + + +// difference/intersection of point-multipoint +template +< + typename Point, + typename MultiPoint, + typename PointOut, + overlay_type OverlayType +> +struct point_multipoint_point +{ + template + static inline OutputIterator apply(Point const& point, + MultiPoint const& multipoint, + RobustPolicy const& , + OutputIterator oit, + Strategy const&) + { + typedef action_selector_pl_pl action; + + for (typename boost::range_iterator::type + it = boost::begin(multipoint); + it != boost::end(multipoint); ++it) + { + if ( detail::equals::equals_point_point(*it, point) ) + { + action::apply(point, true, oit); + return oit; + } + } + + action::apply(point, false, oit); + return oit; + } +}; + + + +// difference/intersection of multipoint-multipoint +template +< + typename MultiPoint1, + typename MultiPoint2, + typename PointOut, + overlay_type OverlayType +> +struct multipoint_multipoint_point +{ + template + static inline OutputIterator apply(MultiPoint1 const& multipoint1, + MultiPoint2 const& multipoint2, + RobustPolicy const& robust_policy, + OutputIterator oit, + Strategy const& strategy) + { + if ( OverlayType != overlay_difference + && boost::size(multipoint1) > boost::size(multipoint2) ) + { + return multipoint_multipoint_point + < + MultiPoint2, MultiPoint1, PointOut, OverlayType + >::apply(multipoint2, multipoint1, robust_policy, oit, strategy); + } + + std::vector::type> + points2(boost::begin(multipoint2), boost::end(multipoint2)); + + std::sort(points2.begin(), points2.end(), detail::relate::less()); + + for (typename boost::range_iterator::type + it1 = boost::begin(multipoint1); + it1 != boost::end(multipoint1); ++it1) + { + bool found = std::binary_search(points2.begin(), points2.end(), + *it1, detail::relate::less()); + + action_selector_pl_pl + < + PointOut, OverlayType + >::apply(*it1, found, oit); + } + return oit; + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +//=========================================================================== + + +#ifndef DOXYGEN_NO_DISPATCH +namespace detail_dispatch { namespace overlay +{ + +// dispatch struct for pointlike-pointlike difference/intersection +// computation +template +< + typename PointLike1, + typename PointLike2, + typename PointOut, + overlay_type OverlayType, + typename Tag1, + typename Tag2 +> +struct pointlike_pointlike_point + : not_implemented +{}; + + +template +< + typename Point1, + typename Point2, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + Point1, Point2, PointOut, OverlayType, + point_tag, point_tag + > : detail::overlay::point_point_point + < + Point1, Point2, PointOut, OverlayType + > +{}; + + +template +< + typename Point, + typename MultiPoint, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + Point, MultiPoint, PointOut, OverlayType, + point_tag, multi_point_tag + > : detail::overlay::point_multipoint_point + < + Point, MultiPoint, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint, + typename Point, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + MultiPoint, Point, PointOut, OverlayType, + multi_point_tag, point_tag + > : detail::overlay::multipoint_point_point + < + MultiPoint, Point, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint1, + typename MultiPoint2, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + MultiPoint1, MultiPoint2, PointOut, OverlayType, + multi_point_tag, multi_point_tag + > : detail::overlay::multipoint_multipoint_point + < + MultiPoint1, MultiPoint2, PointOut, OverlayType + > +{}; + + +}} // namespace detail_dispatch::overlay +#endif // DOXYGEN_NO_DISPATCH + + +//=========================================================================== + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +// generic pointlike-pointlike union implementation +template +< + typename PointLike1, + typename PointLike2, + typename PointOut +> +struct union_pointlike_pointlike_point +{ + template + static inline OutputIterator apply(PointLike1 const& pointlike1, + PointLike2 const& pointlike2, + RobustPolicy const& robust_policy, + OutputIterator oit, + Strategy const& strategy) + { + copy_points::apply(pointlike1, oit); + + return detail_dispatch::overlay::pointlike_pointlike_point + < + PointLike2, PointLike1, PointOut, overlay_difference, + typename tag::type, + typename tag::type + >::apply(pointlike2, pointlike1, robust_policy, oit, strategy); + } + +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp index f664b1951..385658a19 100644 --- a/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,11 +10,16 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP + #include +#include + +#include #include #include +#include #include #include #include @@ -40,14 +46,14 @@ namespace dispatch struct select_rings { template - static inline void apply(Box const& box, Geometry const& , + static inline void apply(Box const& box, Geometry const& , ring_identifier const& id, Map& map, bool midpoint) { map[id] = typename Map::mapped_type(box, midpoint); } template - static inline void apply(Box const& box, + static inline void apply(Box const& box, ring_identifier const& id, Map& map, bool midpoint) { map[id] = typename Map::mapped_type(box, midpoint); @@ -68,7 +74,7 @@ namespace dispatch } template - static inline void apply(Ring const& ring, + static inline void apply(Ring const& ring, ring_identifier const& id, Map& map, bool midpoint) { if (boost::size(ring) > 0) @@ -91,9 +97,10 @@ namespace dispatch per_ring::apply(exterior_ring(polygon), geometry, id, map, midpoint); - typename interior_return_type::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + typename interior_return_type::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; per_ring::apply(*it, geometry, id, map, midpoint); @@ -109,16 +116,42 @@ namespace dispatch per_ring::apply(exterior_ring(polygon), id, map, midpoint); - typename interior_return_type::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + typename interior_return_type::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; per_ring::apply(*it, id, map, midpoint); } } }; -} + + template + struct select_rings + { + template + static inline void apply(Multi const& multi, Geometry const& geometry, + ring_identifier id, Map& map, bool midpoint) + { + typedef typename boost::range_iterator + < + Multi const + >::type iterator_type; + + typedef select_rings::type> per_polygon; + + id.multi_index = 0; + for (iterator_type it = boost::begin(multi); it != boost::end(multi); ++it) + { + id.ring_index = -1; + per_polygon::apply(*it, geometry, id, map, midpoint); + id.multi_index++; + } + } + }; + +} // namespace dispatch template @@ -213,7 +246,7 @@ inline void update_selection_map(Geometry1 const& geometry1, typename SelectionMap::mapped_type properties = it->second; // Copy by value // Calculate the "within code" (previously this was done earlier but is - // must efficienter here - it can be even more efficient doing it all at once, + // much efficienter here - it can be even more efficient doing it all at once, // using partition, TODO) // So though this is less elegant than before, it avoids many unused point-in-poly calculations switch(id.source_index) @@ -248,7 +281,7 @@ template typename IntersectionMap, typename SelectionMap > inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2, - IntersectionMap const& intersection_map, + IntersectionMap const& intersection_map, SelectionMap& selection_map, bool midpoint) { typedef typename geometry::tag::type tag1; @@ -271,16 +304,16 @@ template typename IntersectionMap, typename SelectionMap > inline void select_rings(Geometry const& geometry, - IntersectionMap const& intersection_map, + IntersectionMap const& intersection_map, SelectionMap& selection_map, bool midpoint) { typedef typename geometry::tag::type tag; SelectionMap map_with_all; - dispatch::select_rings::apply(geometry, + dispatch::select_rings::apply(geometry, ring_identifier(0, -1, -1), map_with_all, midpoint); - update_selection_map(geometry, geometry, intersection_map, + update_selection_map(geometry, geometry, intersection_map, map_with_all, selection_map); } diff --git a/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp index e5f32d993..8dffeae28 100644 --- a/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -9,16 +9,18 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP + #include #include #include #include +#include #include -#include +#include #include #include @@ -55,17 +57,21 @@ template typename Geometry, typename Turns, typename TurnPolicy, + typename RobustPolicy, typename InterruptPolicy > struct self_section_visitor { Geometry const& m_geometry; + RobustPolicy const& m_rescale_policy; Turns& m_turns; InterruptPolicy& m_interrupt_policy; inline self_section_visitor(Geometry const& g, + RobustPolicy const& rp, Turns& turns, InterruptPolicy& ip) : m_geometry(g) + , m_rescale_policy(rp) , m_turns(turns) , m_interrupt_policy(ip) {} @@ -82,12 +88,12 @@ struct self_section_visitor Geometry, Geometry, false, false, Section, Section, - Turns, TurnPolicy, - InterruptPolicy + TurnPolicy >::apply( 0, m_geometry, sec1, 0, m_geometry, sec2, false, + m_rescale_policy, m_turns, m_interrupt_policy); } if (m_interrupt_policy.has_intersections) @@ -103,17 +109,13 @@ struct self_section_visitor -template -< - typename Geometry, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy -> +template struct get_turns { + template static inline bool apply( Geometry const& geometry, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { @@ -127,20 +129,20 @@ struct get_turns > sections_type; sections_type sec; - geometry::sectionalize(geometry, sec); + geometry::sectionalize(geometry, robust_policy, false, sec); self_section_visitor < Geometry, - Turns, TurnPolicy, InterruptPolicy - > visitor(geometry, turns, interrupt_policy); + Turns, TurnPolicy, RobustPolicy, InterruptPolicy + > visitor(geometry, robust_policy, turns, interrupt_policy); try { geometry::partition < - box_type, - detail::get_turns::get_section_box, + box_type, + detail::get_turns::get_section_box, detail::get_turns::ovelaps_section_box >::apply(sec, visitor); } @@ -166,9 +168,7 @@ template < typename GeometryTag, typename Geometry, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points { @@ -178,44 +178,32 @@ struct self_get_turn_points template < typename Ring, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points < ring_tag, Ring, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > - : detail::self_get_turn_points::get_turns - < - Ring, - Turns, - TurnPolicy, - InterruptPolicy - > + : detail::self_get_turn_points::get_turns {}; template < typename Box, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points < box_tag, Box, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > { + template static inline bool apply( Box const& , + RobustPolicy const& , Turns& , InterruptPolicy& ) { @@ -227,24 +215,28 @@ struct self_get_turn_points template < typename Polygon, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points < polygon_tag, Polygon, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > - : detail::self_get_turn_points::get_turns - < - Polygon, - Turns, - TurnPolicy, - InterruptPolicy - > + : detail::self_get_turn_points::get_turns +{}; + + +template +< + typename MultiPolygon, + typename TurnPolicy +> +struct self_get_turn_points + < + multi_polygon_tag, MultiPolygon, + TurnPolicy + > + : detail::self_get_turn_points::get_turns {}; @@ -259,6 +251,7 @@ struct self_get_turn_points \tparam Turns type of intersection container (e.g. vector of "intersection/turn point"'s) \param geometry geometry + \param robust_policy policy to handle robustness issues \param turns container which will contain intersection points \param interrupt_policy policy determining if process is stopped when intersection is found @@ -267,30 +260,24 @@ template < typename AssignPolicy, typename Geometry, + typename RobustPolicy, typename Turns, typename InterruptPolicy > inline void self_turns(Geometry const& geometry, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { concept::check(); - typedef detail::overlay::get_turn_info - < - typename point_type::type, - typename point_type::type, - typename boost::range_value::type, - detail::overlay::assign_null_policy - > TurnPolicy; + typedef detail::overlay::get_turn_info turn_policy; dispatch::self_get_turn_points < typename tag::type, Geometry, - Turns, - TurnPolicy, - InterruptPolicy - >::apply(geometry, turns, interrupt_policy); + turn_policy + >::apply(geometry, robust_policy, turns, interrupt_policy); } diff --git a/include/boost/geometry/algorithms/detail/overlay/stream_info.hpp b/include/boost/geometry/algorithms/detail/overlay/stream_info.hpp index eebe38194..51fd1b3dc 100644 --- a/include/boost/geometry/algorithms/detail/overlay/stream_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/stream_info.hpp @@ -35,7 +35,6 @@ namespace detail { namespace overlay template std::ostream& operator<<(std::ostream &os, turn_info

const& info) { - typename geometry::coordinate_type

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

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