psimpl.h
Go to the documentation of this file.
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is
15  * 'psimpl - generic n-dimensional polyline simplification'.
16  *
17  * The Initial Developer of the Original Code is
18  * Elmar de Koning.
19  * Portions created by the Initial Developer are Copyright (C) 2010-2011
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *
24  * ***** END LICENSE BLOCK ***** */
25 
26 /*
27  psimpl - generic n-dimensional polyline simplification
28  Copyright (C) 2010-2011 Elmar de Koning, edekoning@gmail.com
29 
30  This file is part of psimpl, and is hosted at SourceForge:
31  http://sourceforge.net/projects/psimpl/
32 */
33 
92 #ifndef PSIMPL_GENERIC
93 #define PSIMPL_GENERIC
94 
95 
96 #include <queue>
97 #include <stack>
98 #include <numeric>
99 #include <algorithm>
100 #include <cmath>
101 
102 
106 namespace psimpl
107 {
111  namespace util
112  {
118  template <typename T>
120  {
121  public:
122  scoped_array (unsigned n) {
123  array = new T [n];
124  }
125 
127  delete [] array;
128  }
129 
130  T& operator [] (int offset) {
131  return array [offset];
132  }
133 
134  const T& operator [] (int offset) const {
135  return array [offset];
136  }
137 
138  T* get () const {
139  return array;
140  }
141 
142  void swap (scoped_array& b) {
143  T* tmp = b.array;
144  b.array = array;
145  array = tmp;
146  }
147 
148  private:
149  scoped_array (const scoped_array&);
151 
152  private:
153  T* array;
154  };
155 
156  template <typename T> inline void swap (scoped_array <T>& a, scoped_array <T>& b) {
157  a.swap (b);
158  }
159  }
160 
164  namespace math
165  {
169  struct Statistics
170  {
172  max (0),
173  sum (0),
174  mean (0),
175  std (0)
176  {}
177 
178  double max;
179  double sum;
180  double mean;
181  double std;
182  };
183 
191  template <unsigned DIM, class InputIterator>
192  inline bool equal (
193  InputIterator p1,
194  InputIterator p2)
195  {
196  for (unsigned d = 0; d < DIM; ++d) {
197  if (*p1 != *p2) {
198  return false;
199  }
200  ++p1;
201  ++p2;
202  }
203  return true;
204  }
205 
214  template <unsigned DIM, class InputIterator, class OutputIterator>
215  inline OutputIterator make_vector (
216  InputIterator p1,
217  InputIterator p2,
218  OutputIterator result)
219  {
220  for (unsigned d = 0; d < DIM; ++d) {
221  *result = *p2 - *p1;
222  ++result;
223  ++p1;
224  ++p2;
225  }
226  return result;
227  }
228 
236  template <unsigned DIM, class InputIterator>
237  inline typename std::iterator_traits <InputIterator>::value_type dot (
238  InputIterator v1,
239  InputIterator v2)
240  {
241  typename std::iterator_traits <InputIterator>::value_type result = 0;
242  for (unsigned d = 0; d < DIM; ++d) {
243  result += (*v1) * (*v2);
244  ++v1;
245  ++v2;
246  }
247  return result;
248  }
249 
259  template <unsigned DIM, class InputIterator, class OutputIterator>
260  inline OutputIterator interpolate (
261  InputIterator p1,
262  InputIterator p2,
263  float fraction,
264  OutputIterator result)
265  {
266  typedef typename std::iterator_traits <InputIterator>::value_type value_type;
267 
268  for (unsigned d = 0; d < DIM; ++d) {
269  *result = *p1 + static_cast <value_type> (fraction * (*p2 - *p1));
270  ++result;
271  ++p1;
272  ++p2;
273  }
274  return result;
275  }
276 
284  template <unsigned DIM, class InputIterator1, class InputIterator2>
285  inline typename std::iterator_traits <InputIterator1>::value_type point_distance2 (
286  InputIterator1 p1,
287  InputIterator2 p2)
288  {
289  typename std::iterator_traits <InputIterator1>::value_type result = 0;
290  for (unsigned d = 0; d < DIM; ++d) {
291  result += (*p1 - *p2) * (*p1 - *p2);
292  ++p1;
293  ++p2;
294  }
295  return result;
296  }
297 
306  template <unsigned DIM, class InputIterator>
307  inline typename std::iterator_traits <InputIterator>::value_type line_distance2 (
308  InputIterator l1,
309  InputIterator l2,
310  InputIterator p)
311  {
312  typedef typename std::iterator_traits <InputIterator>::value_type value_type;
313 
314  value_type v [DIM]; // vector l1 --> l2
315  value_type w [DIM]; // vector l1 --> p
316 
317  make_vector <DIM> (l1, l2, v);
318  make_vector <DIM> (l1, p, w);
319 
320  value_type cv = dot <DIM> (v, v); // squared length of v
321  value_type cw = dot <DIM> (w, v); // project w onto v
322 
323  // avoid problems with divisions when value_type is an integer type
324  float fraction = cv == 0 ? 0 : static_cast <float> (cw) / static_cast <float> (cv);
325 
326  value_type proj [DIM]; // p projected onto line (l1, l2)
327  interpolate <DIM> (l1, l2, fraction, proj);
328 
329  return point_distance2 <DIM> (p, proj);
330  }
331 
340  template <unsigned DIM, class InputIterator>
341  inline typename std::iterator_traits <InputIterator>::value_type segment_distance2 (
342  InputIterator s1,
343  InputIterator s2,
344  InputIterator p)
345  {
346  typedef typename std::iterator_traits <InputIterator>::value_type value_type;
347 
348  value_type v [DIM]; // vector s1 --> s2
349  value_type w [DIM]; // vector s1 --> p
350 
351  make_vector <DIM> (s1, s2, v);
352  make_vector <DIM> (s1, p, w);
353 
354  value_type cw = dot <DIM> (w, v); // project w onto v
355  if (cw <= 0) {
356  // projection of w lies to the left of s1
357  return point_distance2 <DIM> (p, s1);
358  }
359 
360  value_type cv = dot <DIM> (v, v); // squared length of v
361  if (cv <= cw) {
362  // projection of w lies to the right of s2
363  return point_distance2 <DIM> (p, s2);
364  }
365 
366  // avoid problems with divisions when value_type is an integer type
367  float fraction = cv == 0 ? 0 : static_cast <float> (cw) / static_cast <float> (cv);
368 
369  value_type proj [DIM]; // p projected onto segement (s1, s2)
370  interpolate <DIM> (s1, s2, fraction, proj);
371 
372  return point_distance2 <DIM> (p, proj);
373  }
374 
383  template <unsigned DIM, class InputIterator>
384  inline typename std::iterator_traits <InputIterator>::value_type ray_distance2 (
385  InputIterator r1,
386  InputIterator r2,
387  InputIterator p)
388  {
389  typedef typename std::iterator_traits <InputIterator>::value_type value_type;
390 
391  value_type v [DIM]; // vector r1 --> r2
392  value_type w [DIM]; // vector r1 --> p
393 
394  make_vector <DIM> (r1, r2, v);
395  make_vector <DIM> (r1, p, w);
396 
397  value_type cv = dot <DIM> (v, v); // squared length of v
398  value_type cw = dot <DIM> (w, v); // project w onto v
399 
400  if (cw <= 0) {
401  // projection of w lies to the left of r1 (not on the ray)
402  return point_distance2 <DIM> (p, r1);
403  }
404 
405  // avoid problems with divisions when value_type is an integer type
406  float fraction = cv == 0 ? 0 : static_cast <float> (cw) / static_cast <float> (cv);
407 
408  value_type proj [DIM]; // p projected onto ray (r1, r2)
409  interpolate <DIM> (r1, r2, fraction, proj);
410 
411  return point_distance2 <DIM> (p, proj);
412  }
413 
421  template <class InputIterator>
423  InputIterator first,
424  InputIterator last)
425  {
426  typedef typename std::iterator_traits <InputIterator>::value_type value_type;
427  typedef typename std::iterator_traits <InputIterator>::difference_type diff_type;
428 
429  Statistics stats;
430 
431  diff_type count = std::distance (first, last);
432  if (count == 0) {
433  return stats;
434  }
435 
436  value_type init = 0;
437  stats.max = static_cast <double> (*std::max_element (first, last));
438  stats.sum = static_cast <double> (std::accumulate (first, last, init));
439  stats.mean = stats.sum / count;
440  std::transform (first, last, first, std::bind2nd (std::minus <value_type> (), stats.mean));
441  stats.std = std::sqrt (static_cast <double> (std::inner_product (first, last, first, init)) / count);
442  return stats;
443  }
444  }
445 
453  template <unsigned DIM, class InputIterator, class OutputIterator>
455  {
456  typedef typename std::iterator_traits <InputIterator>::difference_type diff_type;
457  typedef typename std::iterator_traits <InputIterator>::value_type value_type;
458  typedef typename std::iterator_traits <const value_type*>::difference_type ptr_diff_type;
459 
460  public:
492  OutputIterator NthPoint (
493  InputIterator first,
494  InputIterator last,
495  unsigned n,
496  OutputIterator result)
497  {
498  diff_type coordCount = std::distance (first, last);
499  diff_type pointCount = DIM // protect against zero DIM
500  ? coordCount / DIM
501  : 0;
502 
503  // validate input and check if simplification required
504  if (coordCount % DIM || pointCount < 3 || n < 2) {
505  return std::copy (first, last, result);
506  }
507 
508  unsigned remaining = pointCount - 1; // the number of points remaining after key
509  InputIterator key = first; // indicates the current key
510 
511  // the first point is always part of the simplification
512  CopyKey (key, result);
513 
514  // copy each nth point
515  while (Forward (key, n, remaining)) {
516  CopyKey (key, result);
517  }
518 
519  return result;
520  }
521 
554  OutputIterator RadialDistance (
555  InputIterator first,
556  InputIterator last,
557  value_type tol,
558  OutputIterator result)
559  {
560  diff_type coordCount = std::distance (first, last);
561  diff_type pointCount = DIM // protect against zero DIM
562  ? coordCount / DIM
563  : 0;
564  value_type tol2 = tol * tol; // squared distance tolerance
565 
566  // validate input and check if simplification required
567  if (coordCount % DIM || pointCount < 3 || tol2 == 0) {
568  return std::copy (first, last, result);
569  }
570 
571  InputIterator current = first; // indicates the current key
572  InputIterator next = first; // used to find the next key
573 
574  // the first point is always part of the simplification
575  CopyKeyAdvance (next, result);
576 
577  // Skip first and last point, because they are always part of the simplification
578  for (diff_type index = 1; index < pointCount - 1; ++index) {
579  if (math::point_distance2 <DIM> (current, next) < tol2) {
580  Advance (next);
581  continue;
582  }
583  current = next;
584  CopyKeyAdvance (next, result);
585  }
586  // the last point is always part of the simplification
587  CopyKeyAdvance (next, result);
588 
589  return result;
590  }
591 
608  OutputIterator PerpendicularDistance (
609  InputIterator first,
610  InputIterator last,
611  value_type tol,
612  unsigned repeat,
613  OutputIterator result)
614  {
615  if (repeat == 1) {
616  // single pass
617  return PerpendicularDistance (first, last, tol, result);
618  }
619  // only validate repeat; other input is validated by simplify_perpendicular_distance
620  if (repeat < 1) {
621  return std::copy (first, last, result);
622  }
623  diff_type coordCount = std::distance (first, last);
624 
625  // first pass: [first, last) --> temporary array 'tempPoly'
626  util::scoped_array <value_type> tempPoly (coordCount);
628  diff_type tempCoordCount = std::distance (tempPoly.get (),
629  psimpl_to_array.PerpendicularDistance (first, last, tol, tempPoly.get ()));
630 
631  // check if simplification did not improved
632  if (coordCount == tempCoordCount) {
633  return std::copy (tempPoly.get (), tempPoly.get () + coordCount, result);
634  }
635  std::swap (coordCount, tempCoordCount);
636  --repeat;
637 
638  // intermediate passes: temporary array 'tempPoly' --> temporary array 'tempResult'
639  if (1 < repeat) {
640  util::scoped_array <value_type> tempResult (coordCount);
642 
643  while (--repeat) {
644  tempCoordCount = std::distance (tempResult.get (),
645  psimpl_arrays.PerpendicularDistance (
646  tempPoly.get (), tempPoly.get () + coordCount, tol, tempResult.get ()));
647 
648  // check if simplification did not improved
649  if (coordCount == tempCoordCount) {
650  return std::copy (tempPoly.get (), tempPoly.get () + coordCount, result);
651  }
652  util::swap (tempPoly, tempResult);
653  std::swap (coordCount, tempCoordCount);
654  }
655  }
656 
657  // final pass: temporary array 'tempPoly' --> result
659  return psimpl_from_array.PerpendicularDistance (
660  tempPoly.get (), tempPoly.get () + coordCount, tol, result);
661  }
662 
697  OutputIterator PerpendicularDistance (
698  InputIterator first,
699  InputIterator last,
700  value_type tol,
701  OutputIterator result)
702  {
703  diff_type coordCount = std::distance (first, last);
704  diff_type pointCount = DIM // protect against zero DIM
705  ? coordCount / DIM
706  : 0;
707  value_type tol2 = tol * tol; // squared distance tolerance
708 
709  // validate input and check if simplification required
710  if (coordCount % DIM || pointCount < 3 || tol2 == 0) {
711  return std::copy (first, last, result);
712  }
713 
714  InputIterator p0 = first;
715  InputIterator p1 = AdvanceCopy(p0);
716  InputIterator p2 = AdvanceCopy(p1);
717 
718  // the first point is always part of the simplification
719  CopyKey (p0, result);
720 
721  while (p2 != last) {
722  // test p1 against line segment S(p0, p2)
723  if (math::segment_distance2 <DIM> (p0, p2, p1) < tol2) {
724  CopyKey (p2, result);
725  // move up by two points
726  p0 = p2;
727  Advance (p1, 2);
728  if (p1 == last) {
729  // protect against advancing p2 beyond last
730  break;
731  }
732  Advance (p2, 2);
733  }
734  else {
735  CopyKey (p1, result);
736  // move up by one point
737  p0 = p1;
738  p1 = p2;
739  Advance (p2);
740  }
741  }
742  // make sure the last point is part of the simplification
743  if (p1 != last) {
744  CopyKey (p1, result);
745  }
746  return result;
747  }
748 
783  OutputIterator ReumannWitkam (
784  InputIterator first,
785  InputIterator last,
786  value_type tol,
787  OutputIterator result)
788  {
789  diff_type coordCount = std::distance (first, last);
790  diff_type pointCount = DIM // protect against zero DIM
791  ? coordCount / DIM
792  : 0;
793  value_type tol2 = tol * tol; // squared distance tolerance
794 
795  // validate input and check if simplification required
796  if (coordCount % DIM || pointCount < 3 || tol2 == 0) {
797  return std::copy (first, last, result);
798  }
799 
800  // define the line L(p0, p1)
801  InputIterator p0 = first; // indicates the current key
802  InputIterator p1 = AdvanceCopy (first); // indicates the next point after p0
803 
804  // keep track of two test points
805  InputIterator pi = p1; // the previous test point
806  InputIterator pj = p1; // the current test point (pi+1)
807 
808  // the first point is always part of the simplification
809  CopyKey (p0, result);
810 
811  // check each point pj against L(p0, p1)
812  for (diff_type j = 2; j < pointCount; ++j) {
813  pi = pj;
814  Advance (pj);
815 
816  if (math::line_distance2 <DIM> (p0, p1, pj) < tol2) {
817  continue;
818  }
819  // found the next key at pi
820  CopyKey (pi, result);
821  // define new line L(pi, pj)
822  p0 = pi;
823  p1 = pj;
824  }
825  // the last point is always part of the simplification
826  CopyKey (pj, result);
827 
828  return result;
829  }
830 
872  OutputIterator Opheim (
873  InputIterator first,
874  InputIterator last,
875  value_type min_tol,
876  value_type max_tol,
877  OutputIterator result)
878  {
879  diff_type coordCount = std::distance (first, last);
880  diff_type pointCount = DIM // protect against zero DIM
881  ? coordCount / DIM
882  : 0;
883  value_type min_tol2 = min_tol * min_tol; // squared minimum distance tolerance
884  value_type max_tol2 = max_tol * max_tol; // squared maximum distance tolerance
885 
886  // validate input and check if simplification required
887  if (coordCount % DIM || pointCount < 3 || min_tol2 == 0 || max_tol2 == 0) {
888  return std::copy (first, last, result);
889  }
890 
891  // define the ray R(r0, r1)
892  InputIterator r0 = first; // indicates the current key and start of the ray
893  InputIterator r1 = first; // indicates a point on the ray
894  bool rayDefined = false;
895 
896  // keep track of two test points
897  InputIterator pi = r0; // the previous test point
898  InputIterator pj = // the current test point (pi+1)
899  AdvanceCopy (pi);
900 
901  // the first point is always part of the simplification
902  CopyKey (r0, result);
903 
904  for (diff_type j = 2; j < pointCount; ++j) {
905  pi = pj;
906  Advance (pj);
907 
908  if (!rayDefined) {
909  // discard each point within minimum tolerance
910  if (math::point_distance2 <DIM> (r0, pj) < min_tol2) {
911  continue;
912  }
913  // the last point within minimum tolerance pi defines the ray R(r0, r1)
914  r1 = pi;
915  rayDefined = true;
916  }
917 
918  // check each point pj against R(r0, r1)
919  if (math::point_distance2 <DIM> (r0, pj) < max_tol2 &&
920  math::ray_distance2 <DIM> (r0, r1, pj) < min_tol2)
921  {
922  continue;
923  }
924  // found the next key at pi
925  CopyKey (pi, result);
926  // define new ray R(pi, pj)
927  r0 = pi;
928  rayDefined = false;
929  }
930  // the last point is always part of the simplification
931  CopyKey (pj, result);
932 
933  return result;
934  }
935 
978  OutputIterator Lang (
979  InputIterator first,
980  InputIterator last,
981  value_type tol,
982  unsigned look_ahead,
983  OutputIterator result)
984  {
985  diff_type coordCount = std::distance (first, last);
986  diff_type pointCount = DIM // protect against zero DIM
987  ? coordCount / DIM
988  : 0;
989  value_type tol2 = tol * tol; // squared minimum distance tolerance
990 
991  // validate input and check if simplification required
992  if (coordCount % DIM || pointCount < 3 || look_ahead < 2 || tol2 == 0) {
993  return std::copy (first, last, result);
994  }
995 
996  InputIterator current = first; // indicates the current key
997  InputIterator next = first; // used to find the next key
998 
999  unsigned remaining = pointCount - 1; // the number of points remaining after current
1000  unsigned moved = Forward (next, look_ahead, remaining);
1001 
1002  // the first point is always part of the simplification
1003  CopyKey (current, result);
1004 
1005  while (moved) {
1006  value_type d2 = 0;
1007  InputIterator p = AdvanceCopy (current);
1008 
1009  while (p != next) {
1010  d2 = std::max (d2, math::segment_distance2 <DIM> (current, next, p));
1011  if (tol2 < d2) {
1012  break;
1013  }
1014  Advance (p);
1015  }
1016  if (d2 < tol2) {
1017  current = next;
1018  CopyKey (current, result);
1019  moved = Forward (next, look_ahead, remaining);
1020  }
1021  else {
1022  Backward (next, remaining);
1023  }
1024  }
1025  return result;
1026  }
1027 
1070  OutputIterator DouglasPeucker (
1071  InputIterator first,
1072  InputIterator last,
1073  value_type tol,
1074  OutputIterator result)
1075  {
1076  diff_type coordCount = std::distance (first, last);
1077  diff_type pointCount = DIM // protect against zero DIM
1078  ? coordCount / DIM
1079  : 0;
1080  // validate input and check if simplification required
1081  if (coordCount % DIM || pointCount < 3 || tol == 0) {
1082  return std::copy (first, last, result);
1083  }
1084  // radial distance routine as preprocessing
1085  util::scoped_array <value_type> reduced (coordCount); // radial distance results
1087  ptr_diff_type reducedCoordCount = std::distance (reduced.get (),
1088  psimpl_to_array.RadialDistance (first, last, tol, reduced.get ()));
1089  ptr_diff_type reducedPointCount = reducedCoordCount / DIM;
1090 
1091  // douglas-peucker approximation
1092  util::scoped_array <unsigned char> keys (pointCount); // douglas-peucker results
1093  DPHelper::Approximate (reduced.get (), reducedCoordCount, tol, keys.get ());
1094 
1095  // copy all keys
1096  const value_type* current = reduced.get ();
1097  for (ptr_diff_type p=0; p<reducedPointCount; ++p, current += DIM) {
1098  if (keys [p]) {
1099  for (unsigned d = 0; d < DIM; ++d) {
1100  *result = current [d];
1101  ++result;
1102  }
1103  }
1104  }
1105  return result;
1106  }
1107 
1147  OutputIterator DouglasPeuckerN (
1148  InputIterator first,
1149  InputIterator last,
1150  unsigned count,
1151  OutputIterator result)
1152  {
1153  diff_type coordCount = std::distance (first, last);
1154  diff_type pointCount = DIM // protect against zero DIM
1155  ? coordCount / DIM
1156  : 0;
1157  // validate input and check if simplification required
1158  if (coordCount % DIM || pointCount <= static_cast <diff_type> (count) || count < 2) {
1159  return std::copy (first, last, result);
1160  }
1161 
1162  // copy coords
1163  util::scoped_array <value_type> coords (coordCount);
1164  for (ptr_diff_type c=0; c<coordCount; ++c) {
1165  coords [c] = *first;
1166  ++first;
1167  }
1168 
1169  // douglas-peucker approximation
1170  util::scoped_array <unsigned char> keys (pointCount);
1171  DPHelper::ApproximateN (coords.get (), coordCount, count, keys.get ());
1172 
1173  // copy keys
1174  const value_type* current = coords.get ();
1175  for (ptr_diff_type p=0; p<pointCount; ++p, current += DIM) {
1176  if (keys [p]) {
1177  for (unsigned d = 0; d < DIM; ++d) {
1178  *result = current [d];
1179  ++result;
1180  }
1181  }
1182  }
1183  return result;
1184  }
1185 
1224  OutputIterator ComputePositionalErrors2 (
1225  InputIterator original_first,
1226  InputIterator original_last,
1227  InputIterator simplified_first,
1228  InputIterator simplified_last,
1229  OutputIterator result,
1230  bool* valid=0)
1231  {
1232  diff_type original_coordCount = std::distance (original_first, original_last);
1233  diff_type original_pointCount = DIM // protect against zero DIM
1234  ? original_coordCount / DIM
1235  : 0;
1236 
1237  diff_type simplified_coordCount = std::distance (simplified_first, simplified_last);
1238  diff_type simplified_pointCount = DIM // protect against zero DIM
1239  ? simplified_coordCount / DIM
1240  : 0;
1241 
1242  // validate input
1243  if (original_coordCount % DIM || original_pointCount < 2 ||
1244  simplified_coordCount % DIM || simplified_pointCount < 2 ||
1245  original_pointCount < simplified_pointCount ||
1246  !math::equal <DIM> (original_first, simplified_first))
1247  {
1248  if (valid) {
1249  *valid = false;
1250  }
1251  return result;
1252  }
1253 
1254  // define (simplified) line segment S(simplified_prev, simplified_first)
1255  InputIterator simplified_prev = simplified_first;
1256  std::advance (simplified_first, DIM);
1257 
1258  // process each simplified line segment
1259  while (simplified_first != simplified_last) {
1260  // process each original point until it equals the end of the line segment
1261  while (original_first != original_last &&
1262  !math::equal <DIM> (original_first, simplified_first))
1263  {
1264  *result = math::segment_distance2 <DIM> (simplified_prev, simplified_first,
1265  original_first);
1266  ++result;
1267  std::advance (original_first, DIM);
1268  }
1269  // update line segment S
1270  simplified_prev = simplified_first;
1271  std::advance (simplified_first, DIM);
1272  }
1273  // check if last original point matched
1274  if (original_first != original_last) {
1275  *result = 0;
1276  ++result;
1277  }
1278 
1279  if (valid) {
1280  *valid = original_first != original_last;
1281  }
1282  return result;
1283  }
1284 
1318  InputIterator original_first,
1319  InputIterator original_last,
1320  InputIterator simplified_first,
1321  InputIterator simplified_last,
1322  bool* valid=0)
1323  {
1324  diff_type pointCount = std::distance (original_first, original_last) / DIM;
1325  util::scoped_array <double> errors (pointCount);
1327 
1328  diff_type errorCount =
1329  std::distance (
1330  errors.get (),
1331  ps.ComputePositionalErrors2 (original_first, original_last,
1332  simplified_first, simplified_last,
1333  errors.get (), valid));
1334 
1335  std::transform (errors.get (), errors.get () + errorCount,
1336  errors.get (),
1337  std::ptr_fun <double, double> (std::sqrt));
1338 
1339  return math::compute_statistics (errors.get (), errors.get () + errorCount);
1340  }
1341 
1342  private:
1351  inline void CopyKeyAdvance (
1352  InputIterator& key,
1353  OutputIterator& result)
1354  {
1355  for (unsigned d = 0; d < DIM; ++d) {
1356  *result = *key;
1357  ++result;
1358  ++key;
1359  }
1360  }
1361 
1370  inline void CopyKey (
1371  InputIterator key,
1372  OutputIterator& result)
1373  {
1374  CopyKeyAdvance (key, result);
1375  }
1376 
1385  inline void Advance (
1386  InputIterator& it,
1387  diff_type n = 1)
1388  {
1389  std::advance (it, n * static_cast <diff_type> (DIM));
1390  }
1391 
1401  inline InputIterator AdvanceCopy (
1402  InputIterator it,
1403  diff_type n = 1)
1404  {
1405  Advance (it, n);
1406  return it;
1407  }
1408 
1422  inline unsigned Forward (
1423  InputIterator& it,
1424  unsigned n,
1425  unsigned& remaining)
1426  {
1427  n = std::min (n, remaining);
1428  Advance (it, n);
1429  remaining -= n;
1430  return n;
1431  }
1432 
1441  inline void Backward (
1442  InputIterator& it,
1443  unsigned& remaining)
1444  {
1445  Advance (it, -1);
1446  ++remaining;
1447  }
1448 
1449  private:
1457  class DPHelper
1458  {
1460  struct SubPoly {
1462  first (first), last (last) {}
1463 
1466  };
1467 
1469  struct KeyInfo {
1471  index (index), dist2 (dist2) {}
1472 
1475  };
1476 
1478  struct SubPolyAlt {
1480  first (first), last (last) {}
1481 
1485 
1486  bool operator< (const SubPolyAlt& other) const {
1487  return keyInfo.dist2 < other.keyInfo.dist2;
1488  }
1489  };
1490 
1491  public:
1500  static void Approximate (
1501  const value_type* coords,
1502  ptr_diff_type coordCount,
1503  value_type tol,
1504  unsigned char* keys)
1505  {
1506  value_type tol2 = tol * tol; // squared distance tolerance
1507  ptr_diff_type pointCount = coordCount / DIM;
1508  // zero out keys
1509  std::fill_n (keys, pointCount, 0);
1510  keys [0] = 1; // the first point is always a key
1511  keys [pointCount - 1] = 1; // the last point is always a key
1512 
1513  typedef std::stack <SubPoly> Stack;
1514  Stack stack; // LIFO job-queue containing sub-polylines
1515 
1516  SubPoly subPoly (0, coordCount-DIM);
1517  stack.push (subPoly); // add complete poly
1518 
1519  while (!stack.empty ()) {
1520  subPoly = stack.top (); // take a sub poly
1521  stack.pop (); // and find its key
1522  KeyInfo keyInfo = FindKey (coords, subPoly.first, subPoly.last);
1523  if (keyInfo.index && tol2 < keyInfo.dist2) {
1524  // store the key if valid
1525  keys [keyInfo.index / DIM] = 1;
1526  // split the polyline at the key and recurse
1527  stack.push (SubPoly (keyInfo.index, subPoly.last));
1528  stack.push (SubPoly (subPoly.first, keyInfo.index));
1529  }
1530  }
1531  }
1532 
1541  static void ApproximateN (
1542  const value_type* coords,
1543  ptr_diff_type coordCount,
1544  unsigned countTol,
1545  unsigned char* keys)
1546  {
1547  ptr_diff_type pointCount = coordCount / DIM;
1548  // zero out keys
1549  std::fill_n (keys, pointCount, 0);
1550  keys [0] = 1; // the first point is always a key
1551  keys [pointCount - 1] = 1; // the last point is always a key
1552  unsigned keyCount = 2;
1553 
1554  if (countTol == 2) {
1555  return;
1556  }
1557 
1558  typedef std::priority_queue <SubPolyAlt> PriorityQueue;
1559  PriorityQueue queue; // sorted (max dist2) job queue containing sub-polylines
1560 
1561  SubPolyAlt subPoly (0, coordCount-DIM);
1562  subPoly.keyInfo = FindKey (coords, subPoly.first, subPoly.last);
1563  queue.push (subPoly); // add complete poly
1564 
1565  while (!queue.empty ()) {
1566  subPoly = queue.top (); // take a sub poly
1567  queue.pop ();
1568  // store the key
1569  keys [subPoly.keyInfo.index / DIM] = 1;
1570  // check point count tolerance
1571  keyCount++;
1572  if (keyCount == countTol) {
1573  break;
1574  }
1575  // split the polyline at the key and recurse
1576  SubPolyAlt left (subPoly.first, subPoly.keyInfo.index);
1577  left.keyInfo = FindKey (coords, left.first, left.last);
1578  if (left.keyInfo.index) {
1579  queue.push (left);
1580  }
1581  SubPolyAlt right (subPoly.keyInfo.index, subPoly.last);
1582  right.keyInfo = FindKey (coords, right.first, right.last);
1583  if (right.keyInfo.index) {
1584  queue.push (right);
1585  }
1586  }
1587  }
1588 
1589  private:
1602  static KeyInfo FindKey (
1603  const value_type* coords,
1605  ptr_diff_type last)
1606  {
1607  KeyInfo keyInfo;
1608 
1609  for (ptr_diff_type current = first + DIM; current < last; current += DIM) {
1610  value_type d2 = math::segment_distance2 <DIM> (coords + first, coords + last,
1611  coords + current);
1612  if (d2 < keyInfo.dist2) {
1613  continue;
1614  }
1615  // update maximum squared distance and the point it belongs to
1616  keyInfo.index = current;
1617  keyInfo.dist2 = d2;
1618  }
1619  return keyInfo;
1620  }
1621  };
1622  };
1623 
1636  template <unsigned DIM, class ForwardIterator, class OutputIterator>
1637  OutputIterator simplify_nth_point (
1638  ForwardIterator first,
1639  ForwardIterator last,
1640  unsigned n,
1641  OutputIterator result)
1642  {
1644  return ps.NthPoint (first, last, n, result);
1645  }
1646 
1659  template <unsigned DIM, class ForwardIterator, class OutputIterator>
1660  OutputIterator simplify_radial_distance (
1661  ForwardIterator first,
1662  ForwardIterator last,
1663  typename std::iterator_traits <ForwardIterator>::value_type tol,
1664  OutputIterator result)
1665  {
1667  return ps.RadialDistance (first, last, tol, result);
1668  }
1669 
1683  template <unsigned DIM, class ForwardIterator, class OutputIterator>
1685  ForwardIterator first,
1686  ForwardIterator last,
1687  typename std::iterator_traits <ForwardIterator>::value_type tol,
1688  unsigned repeat,
1689  OutputIterator result)
1690  {
1692  return ps.PerpendicularDistance (first, last, tol, repeat, result);
1693  }
1694 
1707  template <unsigned DIM, class ForwardIterator, class OutputIterator>
1709  ForwardIterator first,
1710  ForwardIterator last,
1711  typename std::iterator_traits <ForwardIterator>::value_type tol,
1712  OutputIterator result)
1713  {
1715  return ps.PerpendicularDistance (first, last, tol, result);
1716  }
1717 
1730  template <unsigned DIM, class ForwardIterator, class OutputIterator>
1731  OutputIterator simplify_reumann_witkam (
1732  ForwardIterator first,
1733  ForwardIterator last,
1734  typename std::iterator_traits <ForwardIterator>::value_type tol,
1735  OutputIterator result)
1736  {
1738  return ps.ReumannWitkam (first, last, tol, result);
1739  }
1740 
1754  template <unsigned DIM, class ForwardIterator, class OutputIterator>
1755  OutputIterator simplify_opheim (
1756  ForwardIterator first,
1757  ForwardIterator last,
1758  typename std::iterator_traits <ForwardIterator>::value_type min_tol,
1759  typename std::iterator_traits <ForwardIterator>::value_type max_tol,
1760  OutputIterator result)
1761  {
1763  return ps.Opheim (first, last, min_tol, max_tol, result);
1764  }
1765 
1779  template <unsigned DIM, class BidirectionalIterator, class OutputIterator>
1780  OutputIterator simplify_lang (
1781  BidirectionalIterator first,
1782  BidirectionalIterator last,
1783  typename std::iterator_traits <BidirectionalIterator>::value_type tol,
1784  unsigned look_ahead,
1785  OutputIterator result)
1786  {
1788  return ps.Lang (first, last, tol, look_ahead, result);
1789  }
1790 
1803  template <unsigned DIM, class ForwardIterator, class OutputIterator>
1804  OutputIterator simplify_douglas_peucker (
1805  ForwardIterator first,
1806  ForwardIterator last,
1807  typename std::iterator_traits <ForwardIterator>::value_type tol,
1808  OutputIterator result)
1809  {
1811  return ps.DouglasPeucker (first, last, tol, result);
1812  }
1813 
1826  template <unsigned DIM, class ForwardIterator, class OutputIterator>
1828  ForwardIterator first,
1829  ForwardIterator last,
1830  unsigned count,
1831  OutputIterator result)
1832  {
1834  return ps.DouglasPeuckerN (first, last, count, result);
1835  }
1836 
1851  template <unsigned DIM, class ForwardIterator, class OutputIterator>
1853  ForwardIterator original_first,
1854  ForwardIterator original_last,
1855  ForwardIterator simplified_first,
1856  ForwardIterator simplified_last,
1857  OutputIterator result,
1858  bool* valid=0)
1859  {
1861  return ps.ComputePositionalErrors2 (original_first, original_last, simplified_first, simplified_last, result, valid);
1862  }
1863 
1877  template <unsigned DIM, class ForwardIterator>
1879  ForwardIterator original_first,
1880  ForwardIterator original_last,
1881  ForwardIterator simplified_first,
1882  ForwardIterator simplified_last,
1883  bool* valid=0)
1884  {
1886  return ps.ComputePositionalErrorStatistics (original_first, original_last, simplified_first, simplified_last, valid);
1887  }
1888 }
1889 
1890 #endif // PSIMPL_GENERIC
psimpl::math::Statistics::max
double max
Definition: psimpl.h:178
psimpl::PolylineSimplification::Forward
unsigned Forward(InputIterator &it, unsigned n, unsigned &remaining)
Increments the iterator by n points if possible.
Definition: psimpl.h:1422
psimpl::util::scoped_array::operator=
scoped_array & operator=(const scoped_array &)
psimpl::PolylineSimplification::DPHelper::SubPolyAlt::last
ptr_diff_type last
coord index of the first point
Definition: psimpl.h:1483
psimpl::PolylineSimplification::PerpendicularDistance
OutputIterator PerpendicularDistance(InputIterator first, InputIterator last, value_type tol, OutputIterator result)
Performs the perpendicular distance routine (PD).
Definition: psimpl.h:697
psimpl::PolylineSimplification::DPHelper::SubPolyAlt::operator<
bool operator<(const SubPolyAlt &other) const
key of this sub poly
Definition: psimpl.h:1486
psimpl::math::equal
bool equal(InputIterator p1, InputIterator p2)
Determines if two points have the exact same coordinates.
Definition: psimpl.h:192
psimpl::util::scoped_array::~scoped_array
~scoped_array()
Definition: psimpl.h:126
psimpl::PolylineSimplification::DPHelper::SubPoly
Defines a sub polyline.
Definition: psimpl.h:1460
psimpl::PolylineSimplification::ComputePositionalErrors2
OutputIterator ComputePositionalErrors2(InputIterator original_first, InputIterator original_last, InputIterator simplified_first, InputIterator simplified_last, OutputIterator result, bool *valid=0)
Computes the squared positional error between a polyline and its simplification.
Definition: psimpl.h:1224
psimpl::PolylineSimplification::DouglasPeucker
OutputIterator DouglasPeucker(InputIterator first, InputIterator last, value_type tol, OutputIterator result)
Performs Douglas-Peucker approximation (DP).
Definition: psimpl.h:1070
psimpl::PolylineSimplification::DouglasPeuckerN
OutputIterator DouglasPeuckerN(InputIterator first, InputIterator last, unsigned count, OutputIterator result)
Performs a Douglas-Peucker approximation variant (DPn).
Definition: psimpl.h:1147
psimpl::math::dot
std::iterator_traits< InputIterator >::value_type dot(InputIterator v1, InputIterator v2)
Computes the dot product of two vectors.
Definition: psimpl.h:237
psimpl
Root namespace of the polyline simplification library.
Definition: psimpl.h:106
psimpl::PolylineSimplification::Opheim
OutputIterator Opheim(InputIterator first, InputIterator last, value_type min_tol, value_type max_tol, OutputIterator result)
Performs Opheim approximation (OP).
Definition: psimpl.h:872
psimpl::math::Statistics::mean
double mean
Definition: psimpl.h:180
psimpl::PolylineSimplification::CopyKeyAdvance
void CopyKeyAdvance(InputIterator &key, OutputIterator &result)
Copies the key to the output destination, and increments the iterator.
Definition: psimpl.h:1351
psimpl::PolylineSimplification::DPHelper::SubPolyAlt
Defines a sub polyline including its key.
Definition: psimpl.h:1478
psimpl::simplify_douglas_peucker_n
OutputIterator simplify_douglas_peucker_n(ForwardIterator first, ForwardIterator last, unsigned count, OutputIterator result)
Performs a variant of Douglas-Peucker polyline simplification (DPn).
Definition: psimpl.h:1827
psimpl::simplify_lang
OutputIterator simplify_lang(BidirectionalIterator first, BidirectionalIterator last, typename std::iterator_traits< BidirectionalIterator >::value_type tol, unsigned look_ahead, OutputIterator result)
Performs Lang polyline simplification (LA).
Definition: psimpl.h:1780
p
SharedPointer p
Definition: ConvertShared.hpp:42
psimpl::PolylineSimplification::DPHelper::KeyInfo::KeyInfo
KeyInfo(ptr_diff_type index=0, value_type dist2=0)
Definition: psimpl.h:1470
psimpl::math::line_distance2
std::iterator_traits< InputIterator >::value_type line_distance2(InputIterator l1, InputIterator l2, InputIterator p)
Computes the squared distance between an infinite line (l1, l2) and a point p.
Definition: psimpl.h:307
psimpl::math::ray_distance2
std::iterator_traits< InputIterator >::value_type ray_distance2(InputIterator r1, InputIterator r2, InputIterator p)
Computes the squared distance between a ray (r1, r2) and a point p.
Definition: psimpl.h:384
psimpl::simplify_nth_point
OutputIterator simplify_nth_point(ForwardIterator first, ForwardIterator last, unsigned n, OutputIterator result)
Performs the nth point routine (NP).
Definition: psimpl.h:1637
psimpl::math::compute_statistics
Statistics compute_statistics(InputIterator first, InputIterator last)
Computes various statistics for the range [first, last)
Definition: psimpl.h:422
psimpl::math::Statistics::Statistics
Statistics()
Definition: psimpl.h:171
psimpl::math::interpolate
OutputIterator interpolate(InputIterator p1, InputIterator p2, float fraction, OutputIterator result)
Peforms linear interpolation between two points.
Definition: psimpl.h:260
psimpl::PolylineSimplification::DPHelper
Douglas-Peucker approximation helper class.
Definition: psimpl.h:1457
psimpl::PolylineSimplification::DPHelper::SubPolyAlt::first
ptr_diff_type first
Definition: psimpl.h:1482
psimpl::PolylineSimplification::DPHelper::KeyInfo::index
ptr_diff_type index
Definition: psimpl.h:1473
psimpl::math::Statistics::std
double std
Definition: psimpl.h:181
psimpl::math::point_distance2
std::iterator_traits< InputIterator1 >::value_type point_distance2(InputIterator1 p1, InputIterator2 p2)
Computes the squared distance of two points.
Definition: psimpl.h:285
psimpl::util::swap
void swap(scoped_array< T > &a, scoped_array< T > &b)
Definition: psimpl.h:156
psimpl::PolylineSimplification::diff_type
std::iterator_traits< InputIterator >::difference_type diff_type
Definition: psimpl.h:456
psimpl::compute_positional_errors2
OutputIterator compute_positional_errors2(ForwardIterator original_first, ForwardIterator original_last, ForwardIterator simplified_first, ForwardIterator simplified_last, OutputIterator result, bool *valid=0)
Computes the squared positional error between a polyline and its simplification.
Definition: psimpl.h:1852
psimpl::simplify_perpendicular_distance
OutputIterator simplify_perpendicular_distance(ForwardIterator first, ForwardIterator last, typename std::iterator_traits< ForwardIterator >::value_type tol, unsigned repeat, OutputIterator result)
Repeatedly performs the perpendicular distance routine (PD).
Definition: psimpl.h:1684
psimpl::PolylineSimplification::DPHelper::KeyInfo
Defines the key of a polyline.
Definition: psimpl.h:1469
psimpl::PolylineSimplification::DPHelper::Approximate
static void Approximate(const value_type *coords, ptr_diff_type coordCount, value_type tol, unsigned char *keys)
Performs Douglas-Peucker approximation.
Definition: psimpl.h:1500
psimpl::math::Statistics
POD structure for storing several statistical values.
Definition: psimpl.h:169
psimpl::PolylineSimplification::ptr_diff_type
std::iterator_traits< const value_type * >::difference_type ptr_diff_type
Definition: psimpl.h:458
psimpl::PolylineSimplification::ReumannWitkam
OutputIterator ReumannWitkam(InputIterator first, InputIterator last, value_type tol, OutputIterator result)
Performs Reumann-Witkam approximation (RW).
Definition: psimpl.h:783
psimpl::PolylineSimplification::CopyKey
void CopyKey(InputIterator key, OutputIterator &result)
Copies the key to the output destination.
Definition: psimpl.h:1370
psimpl::util::scoped_array::array
T * array
Definition: psimpl.h:153
psimpl::PolylineSimplification::ComputePositionalErrorStatistics
math::Statistics ComputePositionalErrorStatistics(InputIterator original_first, InputIterator original_last, InputIterator simplified_first, InputIterator simplified_last, bool *valid=0)
Computes statistics for the positional errors between a polyline and its simplification.
Definition: psimpl.h:1317
psimpl::util::scoped_array::scoped_array
scoped_array(unsigned n)
Definition: psimpl.h:122
psimpl::PolylineSimplification::Advance
void Advance(InputIterator &it, diff_type n=1)
Increments the iterator by n points.
Definition: psimpl.h:1385
psimpl::simplify_opheim
OutputIterator simplify_opheim(ForwardIterator first, ForwardIterator last, typename std::iterator_traits< ForwardIterator >::value_type min_tol, typename std::iterator_traits< ForwardIterator >::value_type max_tol, OutputIterator result)
Performs Opheim polyline simplification (OP).
Definition: psimpl.h:1755
psimpl::PolylineSimplification::DPHelper::SubPoly::SubPoly
SubPoly(ptr_diff_type first=0, ptr_diff_type last=0)
Definition: psimpl.h:1461
psimpl::PolylineSimplification::NthPoint
OutputIterator NthPoint(InputIterator first, InputIterator last, unsigned n, OutputIterator result)
Performs the nth point routine (NP).
Definition: psimpl.h:492
lvr2::qttf::transform
PointBufferPtr transform(PointBufferPtr pc_in, const Transformd &T)
Definition: qttf.cpp:32
psimpl::util::scoped_array::swap
void swap(scoped_array &b)
Definition: psimpl.h:142
psimpl::PolylineSimplification::DPHelper::KeyInfo::dist2
value_type dist2
coord index of the key
Definition: psimpl.h:1474
psimpl::PolylineSimplification::DPHelper::ApproximateN
static void ApproximateN(const value_type *coords, ptr_diff_type coordCount, unsigned countTol, unsigned char *keys)
Performs Douglas-Peucker approximation.
Definition: psimpl.h:1541
psimpl::PolylineSimplification::DPHelper::FindKey
static KeyInfo FindKey(const value_type *coords, ptr_diff_type first, ptr_diff_type last)
Finds the key for the given sub polyline.
Definition: psimpl.h:1602
psimpl::PolylineSimplification::RadialDistance
OutputIterator RadialDistance(InputIterator first, InputIterator last, value_type tol, OutputIterator result)
Performs the (radial) distance between points routine (RD).
Definition: psimpl.h:554
psimpl::PolylineSimplification::AdvanceCopy
InputIterator AdvanceCopy(InputIterator it, diff_type n=1)
Increments a copy of the iterator by n points.
Definition: psimpl.h:1401
psimpl::util::scoped_array
A smart pointer for holding a dynamically allocated array.
Definition: psimpl.h:119
psimpl::math::make_vector
OutputIterator make_vector(InputIterator p1, InputIterator p2, OutputIterator result)
Creates a vector from two points.
Definition: psimpl.h:215
std
Definition: HalfEdge.hpp:124
psimpl::PolylineSimplification
Provides various simplification algorithms for n-dimensional simple polylines.
Definition: psimpl.h:454
psimpl::compute_positional_error_statistics
math::Statistics compute_positional_error_statistics(ForwardIterator original_first, ForwardIterator original_last, ForwardIterator simplified_first, ForwardIterator simplified_last, bool *valid=0)
Computes statistics for the positional errors between a polyline and its simplification.
Definition: psimpl.h:1878
psimpl::util::scoped_array::get
T * get() const
Definition: psimpl.h:138
psimpl::PolylineSimplification::Lang
OutputIterator Lang(InputIterator first, InputIterator last, value_type tol, unsigned look_ahead, OutputIterator result)
Performs Lang approximation (LA).
Definition: psimpl.h:978
psimpl::PolylineSimplification::DPHelper::SubPoly::last
ptr_diff_type last
coord index of the first point
Definition: psimpl.h:1465
first
void first(int id)
Definition: example.cpp:7
psimpl::math::segment_distance2
std::iterator_traits< InputIterator >::value_type segment_distance2(InputIterator s1, InputIterator s2, InputIterator p)
Computes the squared distance between a line segment (s1, s2) and a point p.
Definition: psimpl.h:341
psimpl::PolylineSimplification::value_type
std::iterator_traits< InputIterator >::value_type value_type
Definition: psimpl.h:457
psimpl::PolylineSimplification::DPHelper::SubPolyAlt::keyInfo
KeyInfo keyInfo
coord index of the last point
Definition: psimpl.h:1484
psimpl::PolylineSimplification::DPHelper::SubPoly::first
ptr_diff_type first
Definition: psimpl.h:1464
psimpl::simplify_douglas_peucker
OutputIterator simplify_douglas_peucker(ForwardIterator first, ForwardIterator last, typename std::iterator_traits< ForwardIterator >::value_type tol, OutputIterator result)
Performs Douglas-Peucker polyline simplification (DP).
Definition: psimpl.h:1804
psimpl::PolylineSimplification::PerpendicularDistance
OutputIterator PerpendicularDistance(InputIterator first, InputIterator last, value_type tol, unsigned repeat, OutputIterator result)
Repeatedly performs the perpendicular distance routine (PD).
Definition: psimpl.h:608
psimpl::simplify_reumann_witkam
OutputIterator simplify_reumann_witkam(ForwardIterator first, ForwardIterator last, typename std::iterator_traits< ForwardIterator >::value_type tol, OutputIterator result)
Performs Reumann-Witkam polyline simplification (RW).
Definition: psimpl.h:1731
psimpl::simplify_radial_distance
OutputIterator simplify_radial_distance(ForwardIterator first, ForwardIterator last, typename std::iterator_traits< ForwardIterator >::value_type tol, OutputIterator result)
Performs the (radial) distance between points routine (RD).
Definition: psimpl.h:1660
psimpl::PolylineSimplification::Backward
void Backward(InputIterator &it, unsigned &remaining)
Decrements the iterator by 1 point.
Definition: psimpl.h:1441
psimpl::math::Statistics::sum
double sum
Definition: psimpl.h:179
psimpl::util::scoped_array::operator[]
T & operator[](int offset)
Definition: psimpl.h:130
psimpl::PolylineSimplification::DPHelper::SubPolyAlt::SubPolyAlt
SubPolyAlt(ptr_diff_type first=0, ptr_diff_type last=0)
Definition: psimpl.h:1479


lvr2
Author(s): Thomas Wiemann , Sebastian Pütz , Alexander Mock , Lars Kiesow , Lukas Kalbertodt , Tristan Igelbrink , Johan M. von Behren , Dominik Feldschnieders , Alexander Löhr
autogenerated on Wed Mar 2 2022 00:37:24