Boost C++ Libraries: Ticket #11928: surveyor area method is inaccurate https://svn.boost.org/trac10/ticket/11928 <p> The following code illustrates a problem with the "surveyor" strategy for computing planar areas. It computes the area of a square of area 2 and the same square translated by 10<sup>8</sup> in x and y. bg::area incorrectly computes the area in the second case as 0. </p> <div class="wiki-code"><div class="code"><pre><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;boost/geometry.hpp&gt;</span><span class="cp"></span> <span class="k">namespace</span> <span class="n">bg</span> <span class="o">=</span> <span class="n">boost</span><span class="o">::</span><span class="n">geometry</span><span class="p">;</span> <span class="k">typedef</span> <span class="n">bg</span><span class="o">::</span><span class="n">model</span><span class="o">::</span><span class="n">point</span><span class="o">&lt;</span><span class="kt">double</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">bg</span><span class="o">::</span><span class="n">cs</span><span class="o">::</span><span class="n">cartesian</span><span class="o">&gt;</span> <span class="n">pt</span><span class="p">;</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="n">bg</span><span class="o">::</span><span class="n">model</span><span class="o">::</span><span class="n">polygon</span><span class="o">&lt;</span><span class="n">pt</span><span class="p">,</span> <span class="nb">false</span><span class="p">,</span> <span class="nb">false</span><span class="o">&gt;</span> <span class="n">poly</span><span class="p">;</span> <span class="c1">// A simple square of area 2</span> <span class="n">bg</span><span class="o">::</span><span class="n">read_wkt</span><span class="p">(</span><span class="s">&quot;POLYGON((1 0,0 1,-1 0,0 -1))&quot;</span><span class="p">,</span> <span class="n">poly</span><span class="p">);</span> <span class="c1">// Computes the correct area (= 2)</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;area &quot;</span> <span class="o">&lt;&lt;</span> <span class="n">bg</span><span class="o">::</span><span class="n">area</span><span class="p">(</span><span class="n">poly</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">;</span> <span class="c1">// The same square shifted by (10^8 10^8)</span> <span class="n">bg</span><span class="o">::</span><span class="n">read_wkt</span><span class="p">(</span><span class="s">&quot;POLYGON((100000001 100000000, \</span> <span class="s"> 100000000 100000001, \</span> <span class="s"> 99999999 100000000, \</span> <span class="s"> 100000000 99999999))&quot;</span><span class="p">,</span> <span class="n">poly</span><span class="p">);</span> <span class="c1">// Should give 2 but instead gives 0</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;area &quot;</span> <span class="o">&lt;&lt;</span> <span class="n">bg</span><span class="o">::</span><span class="n">area</span><span class="p">(</span><span class="n">poly</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">;</span> <span class="p">}</span> </pre></div></div><p> Running this code gives </p> <pre class="wiki">area 2 area 0 </pre><p> Discussion: </p> <ul><li>Surely it's a bad idea to embed the algorithm name "surveyor" into the API. Computing 2d areas is really straightforward. Users shouldn't need to worry about the underlying method. Maybe "planar_area" is a better name. </li><li>In any case "surveyor" is a rather silly name of the type beloved of Wikipedia editors. There's no evidence that surveyors actually used this method. </li><li>It's more expensive (at 2*n multiplies and 2*n additions) than applying the trapezium rule (otherwise known as "doing the integral"), i.e., summing (x[i+1] - x[i]) * (y[i+1] + y[i]) / 2, which clocks in at n multiplies and 3*n additions. </li><li>Finally, as this example illustrates, it's subject to catastrophic loss of accuracy, while the trapezium rule will give the exact result. (Kahan contrasts evaluating x*x - y*y compared with (x - y) * (x + y) with x = 1e15+1, y = 1e15-1. The former result is wrong by 1.5% while the latter is exact.) </li></ul> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/11928 Trac 1.4.3 awulkiew Mon, 25 Jan 2016 22:40:34 GMT <link>https://svn.boost.org/trac10/ticket/11928#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11928#comment:1</guid> <description> <p> The name of the formula seems to be older than Wikipedia <a class="changeset" href="https://svn.boost.org/trac10/changeset/1" title="Import core sources for SVNmanger 0.38 ">[1]</a><a class="changeset" href="https://svn.boost.org/trac10/changeset/2" title="Add Boost Disclaimer">[2]</a>. </p> <p> I haven't measured it but I'd rather guess that nowadays the most expensive part in both cases is data access. </p> <p> Furthermore I guess that in the case of the trapezium rule the result is also not precise in general when coordinates are big. So I'd rather fight with this issue by moving all of the coordinates closer to the origin of the coordinate system before passing them into the strategy, so doing it on the fly while calculating the area with any strategy. Similar technique was used before to improve the precision of <code>centroid()</code>. This solution should improve the precision of a function no matter what strategy was used. </p> <p> With that said I'm not against changing the formula. But should trapezium rule be better in all cases or is there a reason why the old one could be left as it was? </p> <p> <a class="changeset" href="https://svn.boost.org/trac10/changeset/1" title="Import core sources for SVNmanger 0.38 ">[1]</a> Flint, Abel. "A System of Geometry and Trigonometry: together with a treatise on surveying: teaching various ways of taking the survey of a field; also to protract the same and find the area. Likewise, Rectangular Surveying; Or, an accurate method of calculating the area of any field arithmetically, without the necessity of plotting it.", 1808 </p> <p> <a class="changeset" href="https://svn.boost.org/trac10/changeset/2" title="Add Boost Disclaimer">[2]</a> Braden, Bart. "The surveyor’s area formula.", 1986 </p> </description> <category>Ticket</category> </item> <item> <author>Charles Karney <charles@…></author> <pubDate>Tue, 26 Jan 2016 15:51:27 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/11928#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11928#comment:2</guid> <description> <p> Thanks for the reference to Abel Flint (a great name!). You will see on pp. 59-63 that he describes the trapezium rule and <em>not</em> the so-called surveyor's method! Most of his contemporaries who were mathematicians would have recognized this as such and not, therefore, have seen it as some special technique invented by a surveyor. </p> <p> The analysis of why </p> <blockquote> <p> sum x<sub>i</sub> y<sub>i+1</sub> - x<sub>i+1</sub> y<sub>i</sub> </p> </blockquote> <p> is typically subject to worse round off errors than </p> <blockquote> <p> sum (x<sub>i+1</sub> - x<sub>i</sub>) (y<sub>i+1</sub> + y<sub>i</sub>) </p> </blockquote> <p> goes as follows. Each formula contains </p> <ol><li>products </li><li>differences in forming each term in the sum </li><li>differences in forming the sum itself </li></ol><p> In the surveyors formula 2 follows 1 and so the mild round off errors from the products are sometimes catastrophically magnified. In the trapezium rule 2 precedes 1 and there is <em>no</em> round off error if x<sub>i+1</sub> and x<sub>i</sub> are within a factor of 2 of each other (which is a common situation). </p> <p> Both formulas are subject to potentially dangerous round off with 3. Centering the numbers might ameliorate this problem. Alternatively, merely switching to the other trapezium rule involving (y<sub>i+1</sub> - y<sub>i</sub>) would help, e.g., in the case of UTM coordinates where often the northings are much larger than the eastings. </p> </description> <category>Ticket</category> </item> <item> <author>Charles Karney <charles@…></author> <pubDate>Tue, 26 Jan 2016 16:37:57 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/11928#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11928#comment:3</guid> <description> <p> By the way, in <a class="ext-link" href="http://geographiclib.sourceforge.net"><span class="icon">​</span>GeographicLib</a>, I use an <a class="ext-link" href="http://geographiclib.sourceforge.net/html/classGeographicLib_1_1Accumulator.html"><span class="icon">​</span>Accumulator</a> class to perform the summation. This effectively sums at double the normal precision. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>awulkiew</dc:creator> <pubDate>Thu, 28 Jan 2016 13:52:18 GMT</pubDate> <title>status, milestone changed; resolution set https://svn.boost.org/trac10/ticket/11928#comment:4 https://svn.boost.org/trac10/ticket/11928#comment:4 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">fixed</span> </li> <li><strong>milestone</strong> <span class="trac-field-old">To Be Determined</span> → <span class="trac-field-new">Boost 1.61.0</span> </li> </ul> <p> I tweaked the formula as you suggested. Thanks. </p> Ticket