psimpl 7
psimpl.h
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is
00015  * 'psimpl - generic n-dimensional polyline simplification'.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Elmar de Koning.
00019  * Portions created by the Initial Developer are Copyright (C) 2010-2011
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * ***** END LICENSE BLOCK ***** */
00025 
00026 /*
00027     psimpl - generic n-dimensional polyline simplification
00028     Copyright (C) 2010-2011 Elmar de Koning, edekoning@gmail.com
00029 
00030     This file is part of psimpl, and is hosted at SourceForge:
00031     http://sourceforge.net/projects/psimpl/
00032 */
00033 
00092 #ifndef PSIMPL_GENERIC
00093 #define PSIMPL_GENERIC
00094 
00095 
00096 #include <queue>
00097 #include <stack>
00098 #include <numeric>
00099 #include <algorithm>
00100 #include <cmath>
00101 
00102 
00106 namespace psimpl
00107 {
00111     namespace util
00112     {
00118         template <typename T>
00119         class scoped_array
00120         {
00121         public:
00122             scoped_array (unsigned n) {
00123                 array = new T [n];
00124             }
00125 
00126             ~scoped_array () {
00127                 delete [] array;
00128             }
00129 
00130             T& operator [] (int offset) {
00131                 return array [offset];
00132             }
00133 
00134             const T& operator [] (int offset) const {
00135                 return array [offset];
00136             }
00137 
00138             T* get () const {
00139                 return array;
00140             }
00141 
00142             void swap (scoped_array& b) {
00143                 T* tmp = b.array;
00144                 b.array = array;
00145                 array = tmp;
00146             }
00147 
00148         private:
00149             scoped_array (const scoped_array&);
00150             scoped_array& operator= (const scoped_array&);
00151 
00152         private:
00153             T* array;
00154         };
00155 
00156         template <typename T> inline void swap (scoped_array <T>& a, scoped_array <T>& b) {
00157             a.swap (b);
00158         }
00159     }
00160 
00164     namespace math
00165     {
00169         struct Statistics
00170         {
00171             Statistics () :
00172                 max (0),
00173                 sum (0),
00174                 mean (0),
00175                 std (0)
00176             {}
00177 
00178             double max;
00179             double sum;
00180             double mean;
00181             double std;     
00182         };
00183 
00191         template <unsigned DIM, class InputIterator>
00192         inline bool equal (
00193             InputIterator p1,
00194             InputIterator p2)
00195         {
00196             for (unsigned d = 0; d < DIM; ++d) {
00197                 if (*p1 != *p2) {
00198                     return false;
00199                 }
00200                 ++p1;
00201                 ++p2;
00202             }
00203             return true;
00204         }
00205 
00214         template <unsigned DIM, class InputIterator, class OutputIterator>
00215         inline OutputIterator make_vector (
00216             InputIterator p1,
00217             InputIterator p2,
00218             OutputIterator result)
00219         {
00220             for (unsigned d = 0; d < DIM; ++d) {
00221                 *result = *p2 - *p1;
00222                 ++result;
00223                 ++p1;
00224                 ++p2;
00225             }
00226             return result;
00227         }
00228 
00236         template <unsigned DIM, class InputIterator>
00237         inline typename std::iterator_traits <InputIterator>::value_type dot (
00238             InputIterator v1,
00239             InputIterator v2)
00240         {
00241             typename std::iterator_traits <InputIterator>::value_type result = 0;
00242             for (unsigned d = 0; d < DIM; ++d) {
00243                 result += (*v1) * (*v2);
00244                 ++v1;
00245                 ++v2;
00246             }
00247             return result;
00248         }
00249 
00259         template <unsigned DIM, class InputIterator, class OutputIterator>
00260         inline OutputIterator interpolate (
00261             InputIterator p1,
00262             InputIterator p2,
00263             float fraction,
00264             OutputIterator result)
00265         {
00266             typedef typename std::iterator_traits <InputIterator>::value_type value_type;
00267 
00268             for (unsigned d = 0; d < DIM; ++d) {
00269                 *result = *p1 + static_cast <value_type> (fraction * (*p2 - *p1));
00270                 ++result;
00271                 ++p1;
00272                 ++p2;
00273             }
00274             return result;
00275         }
00276 
00284         template <unsigned DIM, class InputIterator1, class InputIterator2>
00285         inline typename std::iterator_traits <InputIterator1>::value_type point_distance2 (
00286             InputIterator1 p1,
00287             InputIterator2 p2)
00288         {
00289             typename std::iterator_traits <InputIterator1>::value_type result = 0;
00290             for (unsigned d = 0; d < DIM; ++d) {
00291                 result += (*p1 - *p2) * (*p1 - *p2);
00292                 ++p1;
00293                 ++p2;
00294             }
00295             return result;
00296         }
00297 
00306         template <unsigned DIM, class InputIterator>
00307         inline typename std::iterator_traits <InputIterator>::value_type line_distance2 (
00308             InputIterator l1,
00309             InputIterator l2,
00310             InputIterator p)
00311         {
00312             typedef typename std::iterator_traits <InputIterator>::value_type value_type;
00313 
00314             value_type v [DIM];                 // vector l1 --> l2
00315             value_type w [DIM];                 // vector l1 --> p
00316 
00317             make_vector <DIM> (l1, l2, v);
00318             make_vector <DIM> (l1, p,  w);
00319 
00320             value_type cv = dot <DIM> (v, v);   // squared length of v
00321             value_type cw = dot <DIM> (w, v);   // project w onto v
00322 
00323             // avoid problems with divisions when value_type is an integer type
00324             float fraction = cv == 0 ? 0 : static_cast <float> (cw) / static_cast <float> (cv);
00325 
00326             value_type proj [DIM];              // p projected onto line (l1, l2)
00327             interpolate <DIM> (l1, l2, fraction, proj);
00328 
00329             return point_distance2 <DIM> (p, proj);
00330         }
00331 
00340         template <unsigned DIM, class InputIterator>
00341         inline typename std::iterator_traits <InputIterator>::value_type segment_distance2 (
00342             InputIterator s1,
00343             InputIterator s2,
00344             InputIterator p)
00345         {
00346             typedef typename std::iterator_traits <InputIterator>::value_type value_type;
00347 
00348             value_type v [DIM];        // vector s1 --> s2
00349             value_type w [DIM];        // vector s1 --> p
00350 
00351             make_vector <DIM> (s1, s2, v);
00352             make_vector <DIM> (s1, p,  w);
00353 
00354             value_type cw = dot <DIM> (w, v);   // project w onto v
00355             if (cw <= 0) {
00356                 // projection of w lies to the left of s1
00357                 return point_distance2 <DIM> (p, s1);
00358             }
00359 
00360             value_type cv = dot <DIM> (v, v);   // squared length of v
00361             if (cv <= cw) {
00362                 // projection of w lies to the right of s2
00363                 return point_distance2 <DIM> (p, s2);
00364             }
00365 
00366             // avoid problems with divisions when value_type is an integer type
00367             float fraction = cv == 0 ? 0 : static_cast <float> (cw) / static_cast <float> (cv);
00368 
00369             value_type proj [DIM];    // p projected onto segement (s1, s2)
00370             interpolate <DIM> (s1, s2, fraction, proj);
00371 
00372             return point_distance2 <DIM> (p, proj);
00373         }
00374 
00383         template <unsigned DIM, class InputIterator>
00384         inline typename std::iterator_traits <InputIterator>::value_type ray_distance2 (
00385             InputIterator r1,
00386             InputIterator r2,
00387             InputIterator p)
00388         {
00389             typedef typename std::iterator_traits <InputIterator>::value_type value_type;
00390 
00391             value_type v [DIM];        // vector r1 --> r2
00392             value_type w [DIM];        // vector r1 --> p
00393 
00394             make_vector <DIM> (r1, r2, v);
00395             make_vector <DIM> (r1, p,  w);
00396 
00397             value_type cv = dot <DIM> (v, v);    // squared length of v
00398             value_type cw = dot <DIM> (w, v);    // project w onto v
00399 
00400             if (cw <= 0) {
00401                 // projection of w lies to the left of r1 (not on the ray)
00402                 return point_distance2 <DIM> (p, r1);
00403             }
00404 
00405             // avoid problems with divisions when value_type is an integer type
00406             float fraction = cv == 0 ? 0 : static_cast <float> (cw) / static_cast <float> (cv);
00407 
00408             value_type proj [DIM];    // p projected onto ray (r1, r2)
00409             interpolate <DIM> (r1, r2, fraction, proj);
00410 
00411             return point_distance2 <DIM> (p, proj);
00412         }
00413 
00421         template <class InputIterator>
00422         inline Statistics compute_statistics (
00423             InputIterator first,
00424             InputIterator last)
00425         {
00426             typedef typename std::iterator_traits <InputIterator>::value_type value_type;
00427             typedef typename std::iterator_traits <InputIterator>::difference_type diff_type;
00428 
00429             Statistics stats;
00430 
00431             diff_type count = std::distance (first, last);
00432             if (count == 0) {
00433                 return stats;
00434             }
00435 
00436             value_type init = 0;
00437             stats.max = static_cast <double> (*std::max_element (first, last));
00438             stats.sum = static_cast <double> (std::accumulate (first, last, init));
00439             stats.mean = stats.sum / count;
00440             std::transform (first, last, first, std::bind2nd (std::minus <value_type> (), stats.mean));
00441             stats.std = std::sqrt (static_cast <double> (std::inner_product (first, last, first, init)) / count);
00442             return stats;
00443         }
00444     }
00445 
00453     template <unsigned DIM, class InputIterator, class OutputIterator>
00454     class PolylineSimplification
00455     {
00456         typedef typename std::iterator_traits <InputIterator>::difference_type diff_type;
00457         typedef typename std::iterator_traits <InputIterator>::value_type value_type;
00458         typedef typename std::iterator_traits <const value_type*>::difference_type ptr_diff_type;
00459 
00460     public:
00492         OutputIterator NthPoint (
00493             InputIterator first,
00494             InputIterator last,
00495             unsigned n,
00496             OutputIterator result)
00497         {
00498             diff_type coordCount = std::distance (first, last);
00499             diff_type pointCount = DIM              // protect against zero DIM
00500                                    ? coordCount / DIM
00501                                    : 0;
00502 
00503             // validate input and check if simplification required
00504             if (coordCount % DIM || pointCount < 3 || n < 2) {
00505                 return std::copy (first, last, result);
00506             }
00507 
00508             unsigned remaining = pointCount - 1;    // the number of points remaining after key
00509             InputIterator key = first;              // indicates the current key
00510 
00511             // the first point is always part of the simplification
00512             CopyKey (key, result);
00513 
00514             // copy each nth point
00515             while (Forward (key, n, remaining)) {
00516                 CopyKey (key, result);
00517             }
00518 
00519             return result;
00520         }
00521 
00554         OutputIterator RadialDistance (
00555             InputIterator first,
00556             InputIterator last,
00557             value_type tol,
00558             OutputIterator result)
00559         {
00560             diff_type coordCount = std::distance (first, last);
00561             diff_type pointCount = DIM      // protect against zero DIM
00562                                    ? coordCount / DIM
00563                                    : 0;
00564             value_type tol2 = tol * tol;    // squared distance tolerance
00565 
00566             // validate input and check if simplification required
00567             if (coordCount % DIM || pointCount < 3 || tol2 == 0) {
00568                 return std::copy (first, last, result);
00569             }
00570 
00571             InputIterator current = first;  // indicates the current key
00572             InputIterator next = first;     // used to find the next key
00573 
00574             // the first point is always part of the simplification
00575             CopyKeyAdvance (next, result);
00576 
00577             // Skip first and last point, because they are always part of the simplification
00578             for (diff_type index = 1; index < pointCount - 1; ++index) {
00579                 if (math::point_distance2 <DIM> (current, next) < tol2) {
00580                     Advance (next);
00581                     continue;
00582                 }
00583                 current = next;
00584                 CopyKeyAdvance (next, result);
00585             }
00586             // the last point is always part of the simplification
00587             CopyKeyAdvance (next, result);
00588 
00589             return result;
00590         }
00591 
00608         OutputIterator PerpendicularDistance (
00609             InputIterator first,
00610             InputIterator last,
00611             value_type tol,
00612             unsigned repeat,
00613             OutputIterator result)
00614         {
00615             if (repeat == 1) {
00616                 // single pass
00617                 return PerpendicularDistance (first, last, tol, result);
00618             }
00619             // only validate repeat; other input is validated by simplify_perpendicular_distance
00620             if (repeat < 1) {
00621                 return std::copy (first, last, result);
00622             }
00623             diff_type coordCount = std::distance (first, last);
00624 
00625             // first pass: [first, last) --> temporary array 'tempPoly'
00626             util::scoped_array <value_type> tempPoly (coordCount);
00627             PolylineSimplification <DIM, InputIterator, value_type*> psimpl_to_array;
00628             diff_type tempCoordCount = std::distance (tempPoly.get (),
00629                 psimpl_to_array.PerpendicularDistance (first, last, tol, tempPoly.get ()));
00630 
00631             // check if simplification did not improved
00632             if (coordCount == tempCoordCount) {
00633                 return std::copy (tempPoly.get (), tempPoly.get () + coordCount, result);
00634             }
00635             std::swap (coordCount, tempCoordCount);
00636             --repeat;
00637 
00638             // intermediate passes: temporary array 'tempPoly' --> temporary array 'tempResult'
00639             if (1 < repeat) {
00640                 util::scoped_array <value_type> tempResult (coordCount);
00641                 PolylineSimplification <DIM, value_type*, value_type*> psimpl_arrays;
00642 
00643                 while (--repeat) {
00644                     tempCoordCount = std::distance (tempResult.get (),
00645                         psimpl_arrays.PerpendicularDistance (
00646                             tempPoly.get (), tempPoly.get () + coordCount, tol, tempResult.get ()));
00647 
00648                     // check if simplification did not improved
00649                     if (coordCount == tempCoordCount) {
00650                         return std::copy (tempPoly.get (), tempPoly.get () + coordCount, result);
00651                     }
00652                     util::swap (tempPoly, tempResult);
00653                     std::swap (coordCount, tempCoordCount);
00654                 }
00655             }
00656 
00657             // final pass: temporary array 'tempPoly' --> result
00658             PolylineSimplification <DIM, value_type*, OutputIterator> psimpl_from_array;
00659             return psimpl_from_array.PerpendicularDistance (
00660                 tempPoly.get (), tempPoly.get () + coordCount, tol, result);
00661         }
00662 
00697         OutputIterator PerpendicularDistance (
00698             InputIterator first,
00699             InputIterator last,
00700             value_type tol,
00701             OutputIterator result)
00702         {
00703             diff_type coordCount = std::distance (first, last);
00704             diff_type pointCount = DIM      // protect against zero DIM
00705                                    ? coordCount / DIM
00706                                    : 0;
00707             value_type tol2 = tol * tol;    // squared distance tolerance
00708 
00709             // validate input and check if simplification required
00710             if (coordCount % DIM || pointCount < 3 || tol2 == 0) {
00711                 return std::copy (first, last, result);
00712             }
00713 
00714             InputIterator p0 = first;
00715             InputIterator p1 = AdvanceCopy(p0);
00716             InputIterator p2 = AdvanceCopy(p1);
00717 
00718             // the first point is always part of the simplification
00719             CopyKey (p0, result);
00720 
00721             while (p2 != last) {
00722                 // test p1 against line segment S(p0, p2)
00723                 if (math::segment_distance2 <DIM> (p0, p2, p1) < tol2) {
00724                     CopyKey (p2, result);
00725                     // move up by two points
00726                     p0 = p2;
00727                     Advance (p1, 2);
00728                     if (p1 == last) {
00729                         // protect against advancing p2 beyond last
00730                         break;
00731                     }
00732                     Advance (p2, 2);
00733                 }
00734                 else {
00735                     CopyKey (p1, result);
00736                     // move up by one point
00737                     p0 = p1;
00738                     p1 = p2;
00739                     Advance (p2);
00740                 }
00741             }
00742             // make sure the last point is part of the simplification
00743             if (p1 != last) {
00744                 CopyKey (p1, result);
00745             }
00746             return result;
00747         }
00748 
00783         OutputIterator ReumannWitkam (
00784             InputIterator first,
00785             InputIterator last,
00786             value_type tol,
00787             OutputIterator result)
00788         {
00789             diff_type coordCount = std::distance (first, last);
00790             diff_type pointCount = DIM      // protect against zero DIM
00791                                    ? coordCount / DIM
00792                                    : 0;
00793             value_type tol2 = tol * tol;    // squared distance tolerance
00794 
00795             // validate input and check if simplification required
00796             if (coordCount % DIM || pointCount < 3 || tol2 == 0) {
00797                 return std::copy (first, last, result);
00798             }
00799 
00800             // define the line L(p0, p1)
00801             InputIterator p0 = first;               // indicates the current key
00802             InputIterator p1 = AdvanceCopy (first); // indicates the next point after p0
00803 
00804             // keep track of two test points
00805             InputIterator pi = p1;     // the previous test point
00806             InputIterator pj = p1;     // the current test point (pi+1)
00807 
00808             // the first point is always part of the simplification
00809             CopyKey (p0, result);
00810 
00811             // check each point pj against L(p0, p1)
00812             for (diff_type j = 2; j < pointCount; ++j) {
00813                 pi = pj;
00814                 Advance (pj);
00815 
00816                 if (math::line_distance2 <DIM> (p0, p1, pj) < tol2) {
00817                     continue;
00818                 }
00819                 // found the next key at pi
00820                 CopyKey (pi, result);
00821                 // define new line L(pi, pj)
00822                 p0 = pi;
00823                 p1 = pj;
00824             }
00825             // the last point is always part of the simplification
00826             CopyKey (pj, result);
00827 
00828             return result;
00829         }
00830 
00872         OutputIterator Opheim (
00873             InputIterator first,
00874             InputIterator last,
00875             value_type min_tol,
00876             value_type max_tol,
00877             OutputIterator result)
00878         {
00879             diff_type coordCount = std::distance (first, last);
00880             diff_type pointCount = DIM                    // protect against zero DIM
00881                                    ? coordCount / DIM
00882                                    : 0;
00883             value_type min_tol2 = min_tol * min_tol;    // squared minimum distance tolerance
00884             value_type max_tol2 = max_tol * max_tol;    // squared maximum distance tolerance
00885 
00886             // validate input and check if simplification required
00887             if (coordCount % DIM || pointCount < 3 || min_tol2 == 0 || max_tol2 == 0) {
00888                 return std::copy (first, last, result);
00889             }
00890 
00891             // define the ray R(r0, r1)
00892             InputIterator r0 = first;  // indicates the current key and start of the ray
00893             InputIterator r1 = first;  // indicates a point on the ray
00894             bool rayDefined = false;
00895 
00896             // keep track of two test points
00897             InputIterator pi = r0;     // the previous test point
00898             InputIterator pj =         // the current test point (pi+1)
00899                 AdvanceCopy (pi);
00900 
00901             // the first point is always part of the simplification
00902             CopyKey (r0, result);
00903 
00904             for (diff_type j = 2; j < pointCount; ++j) {
00905                 pi = pj;
00906                 Advance (pj);
00907 
00908                 if (!rayDefined) {
00909                     // discard each point within minimum tolerance
00910                     if (math::point_distance2 <DIM> (r0, pj) < min_tol2) {
00911                         continue;
00912                     }
00913                     // the last point within minimum tolerance pi defines the ray R(r0, r1)
00914                     r1 = pi;
00915                     rayDefined = true;
00916                 }
00917 
00918                 // check each point pj against R(r0, r1)
00919                 if (math::point_distance2 <DIM> (r0, pj) < max_tol2 &&
00920                     math::ray_distance2 <DIM> (r0, r1, pj) < min_tol2)
00921                 {
00922                     continue;
00923                 }
00924                 // found the next key at pi
00925                 CopyKey (pi, result);
00926                 // define new ray R(pi, pj)
00927                 r0 = pi;
00928                 rayDefined = false;
00929             }
00930             // the last point is always part of the simplification
00931             CopyKey (pj, result);
00932 
00933             return result;
00934         }
00935 
00978         OutputIterator Lang (
00979             InputIterator first,
00980             InputIterator last,
00981             value_type tol,
00982             unsigned look_ahead,
00983             OutputIterator result)
00984         {
00985             diff_type coordCount = std::distance (first, last);
00986             diff_type pointCount = DIM      // protect against zero DIM
00987                                    ? coordCount / DIM
00988                                    : 0;
00989             value_type tol2 = tol * tol;    // squared minimum distance tolerance
00990             
00991             // validate input and check if simplification required
00992             if (coordCount % DIM || pointCount < 3 || look_ahead < 2 || tol2 == 0) {
00993                 return std::copy (first, last, result);
00994             }
00995 
00996             InputIterator current = first;          // indicates the current key
00997             InputIterator next = first;             // used to find the next key
00998 
00999             unsigned remaining = pointCount - 1;    // the number of points remaining after current
01000             unsigned moved = Forward (next, look_ahead, remaining);
01001 
01002             // the first point is always part of the simplification
01003             CopyKey (current, result);
01004 
01005             while (moved) {
01006                 value_type d2 = 0;
01007                 InputIterator p = AdvanceCopy (current);
01008 
01009                 while (p != next) {
01010                     d2 = std::max (d2, math::segment_distance2 <DIM> (current, next, p));
01011                     if (tol2 < d2) {
01012                         break;
01013                     }
01014                     Advance (p);
01015                 }
01016                 if (d2 < tol2) {
01017                     current = next;
01018                     CopyKey (current, result);
01019                     moved = Forward (next, look_ahead, remaining);
01020                 }
01021                 else {
01022                     Backward (next, remaining);
01023                 }
01024             }
01025             return result;
01026         }
01027 
01070         OutputIterator DouglasPeucker (
01071             InputIterator first,
01072             InputIterator last,
01073             value_type tol,
01074             OutputIterator result)
01075         {
01076             diff_type coordCount = std::distance (first, last);
01077             diff_type pointCount = DIM      // protect against zero DIM
01078                                    ? coordCount / DIM
01079                                    : 0;
01080             // validate input and check if simplification required
01081             if (coordCount % DIM || pointCount < 3 || tol == 0) {
01082                 return std::copy (first, last, result);
01083             }
01084             // radial distance routine as preprocessing
01085             util::scoped_array <value_type> reduced (coordCount);   // radial distance results
01086             PolylineSimplification <DIM, InputIterator, value_type*> psimpl_to_array;
01087             ptr_diff_type reducedCoordCount = std::distance (reduced.get (),
01088                 psimpl_to_array.RadialDistance (first, last, tol, reduced.get ()));
01089             ptr_diff_type reducedPointCount = reducedCoordCount / DIM;
01090 
01091             // douglas-peucker approximation
01092             util::scoped_array <unsigned char> keys (pointCount);         // douglas-peucker results
01093             DPHelper::Approximate (reduced.get (), reducedCoordCount, tol, keys.get ());
01094 
01095             // copy all keys
01096             const value_type* current = reduced.get ();
01097             for (ptr_diff_type p=0; p<reducedPointCount; ++p, current += DIM) {
01098                 if (keys [p]) {
01099                     for (unsigned d = 0; d < DIM; ++d) {
01100                         *result = current [d];
01101                         ++result;
01102                     }
01103                 }
01104             }
01105             return result;
01106         }
01107 
01147         OutputIterator DouglasPeuckerN (
01148             InputIterator first,
01149             InputIterator last,
01150             unsigned count,
01151             OutputIterator result)
01152         {
01153             diff_type coordCount = std::distance (first, last);
01154             diff_type pointCount = DIM      // protect against zero DIM
01155                                    ? coordCount / DIM
01156                                    : 0;
01157             // validate input and check if simplification required
01158             if (coordCount % DIM || pointCount <= static_cast <diff_type> (count) || count < 2) {
01159                 return std::copy (first, last, result);
01160             }
01161 
01162             // copy coords
01163             util::scoped_array <value_type> coords (coordCount);
01164             for (ptr_diff_type c=0; c<coordCount; ++c) {
01165                 coords [c] = *first;
01166                 ++first;
01167             }
01168 
01169             // douglas-peucker approximation
01170             util::scoped_array <unsigned char> keys (pointCount);
01171             DPHelper::ApproximateN (coords.get (), coordCount, count, keys.get ());
01172 
01173             // copy keys
01174             const value_type* current = coords.get ();
01175             for (ptr_diff_type p=0; p<pointCount; ++p, current += DIM) {
01176                 if (keys [p]) {
01177                     for (unsigned d = 0; d < DIM; ++d) {
01178                         *result = current [d];
01179                         ++result;
01180                     }
01181                 }
01182             }
01183             return result;
01184         }
01185 
01224         OutputIterator ComputePositionalErrors2 (
01225             InputIterator original_first,
01226             InputIterator original_last,
01227             InputIterator simplified_first,
01228             InputIterator simplified_last,
01229             OutputIterator result,
01230             bool* valid=0)
01231         {
01232             diff_type original_coordCount = std::distance (original_first, original_last);
01233             diff_type original_pointCount = DIM     // protect against zero DIM
01234                                             ? original_coordCount / DIM
01235                                             : 0;
01236 
01237             diff_type simplified_coordCount = std::distance (simplified_first, simplified_last);
01238             diff_type simplified_pointCount = DIM   // protect against zero DIM
01239                                               ? simplified_coordCount / DIM
01240                                               : 0;
01241 
01242             // validate input
01243             if (original_coordCount % DIM || original_pointCount < 2 ||
01244                 simplified_coordCount % DIM || simplified_pointCount < 2 ||
01245                 original_pointCount < simplified_pointCount ||
01246                 !math::equal <DIM> (original_first, simplified_first))
01247             {
01248                 if (valid) {
01249                     *valid = false;
01250                 }
01251                 return result;
01252             }
01253 
01254             // define (simplified) line segment S(simplified_prev, simplified_first)
01255             InputIterator simplified_prev = simplified_first;
01256             std::advance (simplified_first, DIM);
01257 
01258             // process each simplified line segment
01259             while (simplified_first != simplified_last) {
01260                 // process each original point until it equals the end of the line segment
01261                 while (original_first != original_last &&
01262                        !math::equal <DIM> (original_first, simplified_first))
01263                 {
01264                     *result = math::segment_distance2 <DIM> (simplified_prev, simplified_first,
01265                                                              original_first);
01266                     ++result;
01267                     std::advance (original_first, DIM);
01268                 }
01269                 // update line segment S
01270                 simplified_prev = simplified_first;
01271                 std::advance (simplified_first, DIM);
01272             }
01273             // check if last original point matched
01274             if (original_first != original_last) {
01275                 *result = 0;
01276                 ++result;
01277             }
01278 
01279             if (valid) {
01280                 *valid = original_first != original_last;
01281             }
01282             return result;
01283         }
01284 
01317         math::Statistics ComputePositionalErrorStatistics (
01318             InputIterator original_first,
01319             InputIterator original_last,
01320             InputIterator simplified_first,
01321             InputIterator simplified_last,
01322             bool* valid=0)
01323         {
01324             diff_type pointCount = std::distance (original_first, original_last) / DIM;
01325             util::scoped_array <double> errors (pointCount);
01326             PolylineSimplification <DIM, InputIterator, double*> ps;
01327 
01328             diff_type errorCount = 
01329                 std::distance (
01330                     errors.get (), 
01331                     ps.ComputePositionalErrors2 (original_first, original_last,
01332                                                  simplified_first, simplified_last,
01333                                                  errors.get (), valid));
01334 
01335             std::transform (errors.get (), errors.get () + errorCount,
01336                             errors.get (),
01337                             std::ptr_fun <double, double> (std::sqrt));
01338 
01339             return math::compute_statistics (errors.get (), errors.get () + errorCount);
01340         }
01341 
01342     private:
01351         inline void CopyKeyAdvance (
01352             InputIterator& key,
01353             OutputIterator& result)
01354         {
01355             for (unsigned d = 0; d < DIM; ++d) {
01356                 *result = *key;
01357                 ++result;
01358                 ++key;
01359             }
01360         }
01361 
01370         inline void CopyKey (
01371             InputIterator key,
01372             OutputIterator& result)
01373         {
01374             CopyKeyAdvance (key, result);
01375         }
01376 
01385         inline void Advance (
01386             InputIterator& it,
01387             diff_type n = 1)
01388         {
01389             std::advance (it, n * static_cast <diff_type> (DIM));
01390         }
01391         
01401         inline InputIterator AdvanceCopy (
01402             InputIterator it,
01403             diff_type n = 1)
01404         {
01405             Advance (it, n);
01406             return it;
01407         }
01408 
01422         inline unsigned Forward (
01423             InputIterator& it,
01424             unsigned n,
01425             unsigned& remaining)
01426         {
01427             n = std::min (n, remaining);
01428             Advance (it, n);
01429             remaining -= n;
01430             return n;
01431         }
01432 
01441         inline void Backward (
01442             InputIterator& it,
01443             unsigned& remaining)
01444         {
01445             Advance (it, -1);
01446             ++remaining;
01447         }
01448 
01449     private:
01457         class DPHelper
01458         {
01460             struct SubPoly {
01461                 SubPoly (ptr_diff_type first=0, ptr_diff_type last=0) :
01462                     first (first), last (last) {}
01463 
01464                 ptr_diff_type first;    
01465                 ptr_diff_type last;     
01466             };
01467 
01469             struct KeyInfo {
01470                 KeyInfo (ptr_diff_type index=0, value_type dist2=0) :
01471                     index (index), dist2 (dist2) {}
01472 
01473                 ptr_diff_type index;    
01474                 value_type dist2;       
01475             };
01476 
01478             struct SubPolyAlt {
01479                 SubPolyAlt (ptr_diff_type first=0, ptr_diff_type last=0) :
01480                     first (first), last (last) {}
01481 
01482                 ptr_diff_type first;    
01483                 ptr_diff_type last;     
01484                 KeyInfo keyInfo;        
01485 
01486                 bool operator< (const SubPolyAlt& other) const {
01487                     return keyInfo.dist2 < other.keyInfo.dist2;
01488                 }
01489             };
01490 
01491         public:
01500             static void Approximate (
01501                 const value_type* coords,
01502                 ptr_diff_type coordCount,
01503                 value_type tol,
01504                 unsigned char* keys)
01505             {
01506                 value_type tol2 = tol * tol;    // squared distance tolerance
01507                 ptr_diff_type pointCount = coordCount / DIM;
01508                 // zero out keys
01509                 std::fill_n (keys, pointCount, 0);
01510                 keys [0] = 1;                   // the first point is always a key
01511                 keys [pointCount - 1] = 1;      // the last point is always a key
01512 
01513                 typedef std::stack <SubPoly> Stack;
01514                 Stack stack;                    // LIFO job-queue containing sub-polylines
01515 
01516                 SubPoly subPoly (0, coordCount-DIM);
01517                 stack.push (subPoly);           // add complete poly
01518 
01519                 while (!stack.empty ()) {
01520                     subPoly = stack.top ();     // take a sub poly
01521                     stack.pop ();               // and find its key
01522                     KeyInfo keyInfo = FindKey (coords, subPoly.first, subPoly.last);
01523                     if (keyInfo.index && tol2 < keyInfo.dist2) {
01524                         // store the key if valid
01525                         keys [keyInfo.index / DIM] = 1;
01526                         // split the polyline at the key and recurse
01527                         stack.push (SubPoly (keyInfo.index, subPoly.last));
01528                         stack.push (SubPoly (subPoly.first, keyInfo.index));
01529                     }
01530                 }
01531             }
01532 
01541             static void ApproximateN (
01542                 const value_type* coords,
01543                 ptr_diff_type coordCount,
01544                 unsigned countTol,
01545                 unsigned char* keys)
01546             {
01547                 ptr_diff_type pointCount = coordCount / DIM;
01548                 // zero out keys
01549                 std::fill_n (keys, pointCount, 0);
01550                 keys [0] = 1;                   // the first point is always a key
01551                 keys [pointCount - 1] = 1;      // the last point is always a key
01552                 unsigned keyCount = 2;
01553 
01554                 if (countTol == 2) {
01555                     return;
01556                 }
01557 
01558                 typedef std::priority_queue <SubPolyAlt> PriorityQueue;
01559                 PriorityQueue queue;    // sorted (max dist2) job queue containing sub-polylines
01560 
01561                 SubPolyAlt subPoly (0, coordCount-DIM);
01562                 subPoly.keyInfo = FindKey (coords, subPoly.first, subPoly.last);
01563                 queue.push (subPoly);           // add complete poly
01564 
01565                 while (!queue.empty ()) {
01566                     subPoly = queue.top ();     // take a sub poly
01567                     queue.pop ();
01568                     // store the key
01569                     keys [subPoly.keyInfo.index / DIM] = 1;
01570                     // check point count tolerance
01571                     keyCount++;
01572                     if (keyCount == countTol) {
01573                         break;
01574                     }
01575                     // split the polyline at the key and recurse
01576                     SubPolyAlt left (subPoly.first, subPoly.keyInfo.index);
01577                     left.keyInfo = FindKey (coords, left.first, left.last);
01578                     if (left.keyInfo.index) {
01579                         queue.push (left);
01580                     }
01581                     SubPolyAlt right (subPoly.keyInfo.index, subPoly.last);
01582                     right.keyInfo = FindKey (coords, right.first, right.last);
01583                     if (right.keyInfo.index) {
01584                         queue.push (right);
01585                     }
01586                 }
01587             }
01588 
01589         private:
01602             static KeyInfo FindKey (
01603                 const value_type* coords,
01604                 ptr_diff_type first,
01605                 ptr_diff_type last)
01606             {
01607                 KeyInfo keyInfo;
01608 
01609                 for (ptr_diff_type current = first + DIM; current < last; current += DIM) {
01610                     value_type d2 = math::segment_distance2 <DIM> (coords + first, coords + last,
01611                                                                    coords + current);
01612                     if (d2 < keyInfo.dist2) {
01613                         continue;
01614                     }
01615                     // update maximum squared distance and the point it belongs to
01616                     keyInfo.index = current;
01617                     keyInfo.dist2 = d2;
01618                 }
01619                 return keyInfo;
01620             }
01621         };
01622     };
01623 
01636     template <unsigned DIM, class ForwardIterator, class OutputIterator>
01637     OutputIterator simplify_nth_point (
01638         ForwardIterator first,
01639         ForwardIterator last,
01640         unsigned n,
01641         OutputIterator result)
01642     {
01643         PolylineSimplification <DIM, ForwardIterator, OutputIterator> ps;
01644         return ps.NthPoint (first, last, n, result);
01645     }
01646 
01659     template <unsigned DIM, class ForwardIterator, class OutputIterator>
01660     OutputIterator simplify_radial_distance (
01661         ForwardIterator first,
01662         ForwardIterator last,
01663         typename std::iterator_traits <ForwardIterator>::value_type tol,
01664         OutputIterator result)
01665     {
01666         PolylineSimplification <DIM, ForwardIterator, OutputIterator> ps;
01667         return ps.RadialDistance (first, last, tol, result);
01668     }
01669 
01683     template <unsigned DIM, class ForwardIterator, class OutputIterator>
01684     OutputIterator simplify_perpendicular_distance (
01685         ForwardIterator first,
01686         ForwardIterator last,
01687         typename std::iterator_traits <ForwardIterator>::value_type tol,
01688         unsigned repeat,
01689         OutputIterator result)
01690     {
01691         PolylineSimplification <DIM, ForwardIterator, OutputIterator> ps;
01692         return ps.PerpendicularDistance (first, last, tol, repeat, result);
01693     }
01694 
01707     template <unsigned DIM, class ForwardIterator, class OutputIterator>
01708     OutputIterator simplify_perpendicular_distance (
01709         ForwardIterator first,
01710         ForwardIterator last,
01711         typename std::iterator_traits <ForwardIterator>::value_type tol,
01712         OutputIterator result)
01713     {
01714         PolylineSimplification <DIM, ForwardIterator, OutputIterator> ps;
01715         return ps.PerpendicularDistance (first, last, tol, result);
01716     }
01717 
01730     template <unsigned DIM, class ForwardIterator, class OutputIterator>
01731     OutputIterator simplify_reumann_witkam (
01732         ForwardIterator first,
01733         ForwardIterator last,
01734         typename std::iterator_traits <ForwardIterator>::value_type tol,
01735         OutputIterator result)
01736     {
01737         PolylineSimplification <DIM, ForwardIterator, OutputIterator> ps;
01738         return ps.ReumannWitkam (first, last, tol, result);
01739     }
01740 
01754     template <unsigned DIM, class ForwardIterator, class OutputIterator>
01755     OutputIterator simplify_opheim (
01756         ForwardIterator first,
01757         ForwardIterator last,
01758         typename std::iterator_traits <ForwardIterator>::value_type min_tol,
01759         typename std::iterator_traits <ForwardIterator>::value_type max_tol,
01760         OutputIterator result)
01761     {
01762         PolylineSimplification <DIM, ForwardIterator, OutputIterator> ps;
01763         return ps.Opheim (first, last, min_tol, max_tol, result);
01764     }
01765 
01779     template <unsigned DIM, class BidirectionalIterator, class OutputIterator>
01780     OutputIterator simplify_lang (
01781         BidirectionalIterator first,
01782         BidirectionalIterator last,
01783         typename std::iterator_traits <BidirectionalIterator>::value_type tol,
01784         unsigned look_ahead,
01785         OutputIterator result)
01786     {
01787         PolylineSimplification <DIM, BidirectionalIterator, OutputIterator> ps;
01788         return ps.Lang (first, last, tol, look_ahead, result);
01789     }
01790 
01803     template <unsigned DIM, class ForwardIterator, class OutputIterator>
01804     OutputIterator simplify_douglas_peucker (
01805         ForwardIterator first,
01806         ForwardIterator last,
01807         typename std::iterator_traits <ForwardIterator>::value_type tol,
01808         OutputIterator result)
01809     {
01810         PolylineSimplification <DIM, ForwardIterator, OutputIterator> ps;
01811         return ps.DouglasPeucker (first, last, tol, result);
01812     }
01813 
01826     template <unsigned DIM, class ForwardIterator, class OutputIterator>
01827     OutputIterator simplify_douglas_peucker_n (
01828         ForwardIterator first,
01829         ForwardIterator last,
01830         unsigned count,
01831         OutputIterator result)
01832     {
01833         PolylineSimplification <DIM, ForwardIterator, OutputIterator> ps;
01834         return ps.DouglasPeuckerN (first, last, count, result);
01835     }
01836 
01851     template <unsigned DIM, class ForwardIterator, class OutputIterator>
01852     OutputIterator compute_positional_errors2 (
01853         ForwardIterator original_first,
01854         ForwardIterator original_last,
01855         ForwardIterator simplified_first,
01856         ForwardIterator simplified_last,
01857         OutputIterator result,
01858         bool* valid=0)
01859     {
01860         PolylineSimplification <DIM, ForwardIterator, OutputIterator> ps;
01861         return ps.ComputePositionalErrors2 (original_first, original_last, simplified_first, simplified_last, result, valid);
01862     }
01863 
01877     template <unsigned DIM, class ForwardIterator>
01878     math::Statistics compute_positional_error_statistics (
01879         ForwardIterator original_first,
01880         ForwardIterator original_last,
01881         ForwardIterator simplified_first,
01882         ForwardIterator simplified_last,
01883         bool* valid=0)
01884     {
01885         PolylineSimplification <DIM, ForwardIterator, ForwardIterator> ps;
01886         return ps.ComputePositionalErrorStatistics (original_first, original_last, simplified_first, simplified_last, valid);
01887     }
01888 }
01889 
01890 #endif // PSIMPL_GENERIC