xmsinterp  1.0
AnisotropicInterpolator.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
6 //------------------------------------------------------------------------------
7 
8 //----- Included files ---------------------------------------------------------
9 
10 // 1. Precompiled header
11 
12 // 2. My header
14 
15 // 3. Standard Library Headers
16 #include <cmath>
17 #include <functional>
18 #include <limits>
19 
20 // 4. External Library Headers
21 
22 // 5. Shared Headers
23 #include <xmscore/misc/XmError.h>
24 #include <xmscore/misc/xmstype.h>
25 #include <xmscore/stl/vector.h>
26 #include <xmsgrid/geometry/geoms.h>
27 
28 // 6. Non-shared Headers
29 
30 //----- Forward declarations ---------------------------------------------------
31 
32 //----- External globals -------------------------------------------------------
33 
34 //----- Namespace declaration --------------------------------------------------
35 namespace xms
36 {
38 const double kVERTICAL = std::numeric_limits<double>::quiet_NaN();
39 
40 //------------------------------------------------------------------------------
44 //------------------------------------------------------------------------------
45 LineParameters::LineParameters(double a_slope, double a_intercept)
46 : m_slope(a_slope)
47 , m_intercept(a_intercept)
48 {
49 } // LineParameters::LineParameters
50 
51 //------------------------------------------------------------------------------
54 //------------------------------------------------------------------------------
56 // Get parameters of a line perpendicular to this through point p.
57 {
58  if (std::isnan(m_slope))
59  {
60  return LineParameters(0.0, a_p.y);
61  }
62  if (m_slope == 0.0)
63  {
64  return LineParameters(kVERTICAL, a_p.x);
65  }
66  double slope = -1.0 / m_slope;
67  return LineParameters(slope, a_p.y - slope * a_p.x);
68 } // LineParameters::NormalThrough
69 
70 //------------------------------------------------------------------------------
75 //------------------------------------------------------------------------------
76 bool LineParameters::Intersection(const LineParameters& a_other, Pt3d& a_p) const
77 {
78  if (m_slope == a_other.m_slope ||
79  (std::isnan(m_slope) && std::isnan(a_other.m_slope))) // Parallel Lines
80  {
81  return false;
82  }
83 
84  double a(m_slope), b(m_intercept);
85  double c(a_other.m_slope), d(a_other.m_intercept);
86  if (std::isnan(m_slope))
87  {
88  a_p.x = b;
89  a_p.y = c * b + d;
90  return true;
91  }
92  a_p.x = std::isnan(a_other.m_slope) ? d : ((d - b) / (a - c));
93  a_p.y = a * a_p.x + b;
94  return true;
95 } // LineParameters::Intersection
96 
97 //------------------------------------------------------------------------------
99 //------------------------------------------------------------------------------
101 : m_centerLinePts()
102 , m_accLengths()
103 , m_lineParams()
104 , m_SNPoints()
105 {
106 } // AnisotropicInterpolator::AnisotropicInterpolator
107 //------------------------------------------------------------------------------
116 //------------------------------------------------------------------------------
117 void AnisotropicInterpolator::SetPoints(const VecPt3d& a_centerLinePts,
118  const VecPt3d& a_interpolationPts,
119  bool a_pickClosest)
120 {
121  double total = 0.0;
122 
123  size_t points_sz = a_centerLinePts.size();
124  m_centerLinePts.reserve(points_sz);
125  m_accLengths.reserve(points_sz);
126  m_lineParams.reserve(points_sz - 1);
127  m_centerLinePts.push_back(a_centerLinePts[0]);
128  m_accLengths.push_back(0.0);
129  for (size_t i = 1; i < points_sz; ++i)
130  {
131  const Pt3d& p0 = a_centerLinePts[i - 1];
132  const Pt3d& p1 = a_centerLinePts[i];
133  if (p0.x == p1.x && p0.y == p1.y)
134  continue;
135  total += Distance(p0, p1);
136  m_accLengths.push_back(total);
137  m_lineParams.push_back(GetLineParameters(p0, p1));
138  m_centerLinePts.push_back(p1);
139  }
140 
141  m_SNPoints = ComputeInterpolationPoints(a_interpolationPts, a_pickClosest);
142 } // AnisotropicInterpolator::SetPoints
143 //------------------------------------------------------------------------------
151 //------------------------------------------------------------------------------
153  bool a_pickClosest)
154 {
155  VecPt3d results;
156  for (auto& p : a_points)
157  {
158  VecPt3d tranformedPts = TransformPoint(p, a_pickClosest);
159  results.insert(results.end(), tranformedPts.begin(), tranformedPts.end());
160  }
161  return results;
162 } // AnisotropicInterpolator::ComputeInterpolationPoints
163 //------------------------------------------------------------------------------
166 //------------------------------------------------------------------------------
168 {
169  return m_SNPoints;
170 } // AnisotropicInterpolator::GetSNVInterpolationPoints
171 //------------------------------------------------------------------------------
186 //------------------------------------------------------------------------------
188  const VecPt3d& a_interpolationPoints,
189  double a_xScale,
190  double a_IDWPower,
191  double a_tol)
192 {
193  double result = XM_NODATA;
194  VecPt3d snvs = TransformPoint(a_target, true);
195  if (!snvs.empty())
196  {
197  result = CalculateIDW(a_xScale, a_interpolationPoints, snvs[0], a_IDWPower, a_tol);
198  }
199  return result;
200 } // AnisotropicInterpolator::InterpolatePoint
201 //------------------------------------------------------------------------------
206 //------------------------------------------------------------------------------
207 double AnisotropicInterpolator::Distance(const Pt3d& a_p0, const Pt3d& a_p1) const
208 {
209  double dx(a_p0.x - a_p1.x);
210  double dy(a_p0.y - a_p1.y);
211  double distanceSquared(dx * dx + dy * dy);
212  return sqrt(distanceSquared);
213 } // AnisotropicInterpolator::Distance
214 //------------------------------------------------------------------------------
221 //------------------------------------------------------------------------------
223  const Pt3d& a_p1,
224  double a_xScale)
225 {
226  double dx = a_xScale * (a_p0.x - a_p1.x);
227  double dy = a_p0.y - a_p1.y;
228  return (dx * dx) + (dy * dy);
229 } // AnisotropicInterpolator::AnisotropicDistanceSquared
230 //------------------------------------------------------------------------------
245 //------------------------------------------------------------------------------
247  const VecPt3d& a_points,
248  const Pt3d& a_target,
249  double a_power,
250  double a_tol)
251 {
252  double numerator(0.0);
253  double denominator(0.0);
254  double power(double(a_power) * 0.5);
255  std::function<double(double)> powerFunc;
256  if (a_power == 2)
257  powerFunc = [](double x) -> double { return x; };
258  else
259  powerFunc = [=](double x) -> double { return pow(x, power); };
260  for (auto& p : a_points)
261  {
262  double distanceSquared(AnisotropicDistanceSquared(p, a_target, a_xScale));
263  double value = p.z;
264  if (distanceSquared <= a_tol)
265  {
266  return value;
267  }
268  double weight(1.0 / powerFunc(distanceSquared));
269  numerator += value * weight;
270  denominator += weight;
271  }
272  return numerator / denominator;
273 } // AnisotropicInterpolator::CalculateIDW
274 //------------------------------------------------------------------------------
283 //------------------------------------------------------------------------------
284 double AnisotropicInterpolator::CrossProduct(size_t a_segmentIndex, const Pt3d& a_p) const
285 {
286  const Pt3d& pa = a_p;
287  const Pt3d& pb = m_centerLinePts[a_segmentIndex];
288  const Pt3d& pc = m_centerLinePts[a_segmentIndex + 1];
289  double bcx = pc.x - pb.x;
290  double bcy = pc.y - pb.y;
291  double bax = pa.x - pb.x;
292  double bay = pa.y - pb.y;
293  return gmCross2D(bcx, bcy, bax, bay);
294  // return (pb.x - pa.x) * (pc.y - pa.y) - (pb.y - pa.y) * (pc.x - pa.x);
295 } // AnisotropicInterpolator::CrossProduct
296 //------------------------------------------------------------------------------
301 //------------------------------------------------------------------------------
303 {
304  if (a_p0.x == a_p1.x) // Vertical Line
305  {
306  return LineParameters(kVERTICAL, a_p0.x);
307  }
308  double slope = (a_p1.y - a_p0.y) / (a_p1.x - a_p0.x);
309  double intercept(a_p0.y - slope * a_p0.x);
310  return LineParameters(slope, intercept);
311 } // AnisotropicInterpolator::GetLineParameters
312 //------------------------------------------------------------------------------
321 //------------------------------------------------------------------------------
322 double AnisotropicInterpolator::GetParameterInSegment(size_t a_segmentIndex, const Pt3d& a_p) const
323 {
324  const Pt3d& pi = m_centerLinePts[a_segmentIndex];
325  const Pt3d& pf = m_centerLinePts[a_segmentIndex + 1];
326  return gmPtDistanceAlongSegment(pi, pf, a_p, 1.0e-7);
327 } // AnisotropicInterpolator::GetParameterInSegment
328 //------------------------------------------------------------------------------
338 //------------------------------------------------------------------------------
340  const Pt3d& a_p,
341  Pt3d& a_intersection,
342  double& a_param) const
343 {
344  const LineParameters& line = m_lineParams[a_segmentIndex];
345  const LineParameters normal = line.NormalThrough(a_p);
346  if (line.Intersection(normal, a_intersection))
347  {
348  a_param = GetParameterInSegment(a_segmentIndex, a_intersection);
349  return true;
350  }
351  return false;
352 } // AnisotropicInterpolator::GetIntersectionOfSegmentWithPoint
353 //------------------------------------------------------------------------------
361 //------------------------------------------------------------------------------
363  const Pt3d& a_p,
364  double& a_lastParam,
365  VecSNResult& a_results) const
366 {
367  Pt3d intersection;
368  double param;
369  double cross = CrossProduct(a_segmentIndex, a_p);
370  if (GetIntersectionOfSegmentWithPoint(a_segmentIndex, a_p, intersection, param))
371  {
372  if (param >= 0.0)
373  {
374  if (param <= 1.0)
375  {
376  a_results.push_back(SNResult(a_segmentIndex, param, cross, intersection));
377  }
378  }
379  else if (a_lastParam > 1.0)
380  {
381  const Pt3d& pa = m_centerLinePts[a_segmentIndex];
382  a_results.push_back(SNResult(a_segmentIndex, 0.0, cross, pa));
383  param = 0.0;
384  }
385  a_lastParam = param;
386  }
387 } // AnisotropicInterpolator::GetNormalPointParameter
388 //------------------------------------------------------------------------------
392 //------------------------------------------------------------------------------
394 {
395  a_results.clear();
396  double lastParam = -1.0;
397  for (size_t i = 0; i < m_lineParams.size(); ++i)
398  {
399  GetNormalPointParameter(i, a_p, lastParam, a_results);
400  }
401 } // AnisotropicInterpolator::GetAllNormalPointParameters
402 //------------------------------------------------------------------------------
420 //------------------------------------------------------------------------------
421 VecPt3d AnisotropicInterpolator::TransformPoint(const Pt3d& a_p, bool a_onlyClosest)
422 {
423  double value = a_p.z;
424 
425  VecSNResult results;
426  GetAllNormalPointParameters(a_p, results);
427  VecPt3d snPoints;
428  for (auto& result : results)
429  {
430  // const Pt3d& closest = result.m_position;
431  double len0 = m_accLengths[result.m_index];
432  double len1 = m_accLengths[result.m_index + 1];
433  double s = len0 + result.m_param * (len1 - len0);
434  double n = (result.m_cross == 0.0) ? 0.0 // Collinear
435  : result.m_cross < 0.0 ? -Distance(a_p, result.m_position)
436  : Distance(a_p, result.m_position);
437  Pt3d snv(s, n, value);
438  if (a_onlyClosest)
439  {
440  if (snPoints.size() == 0)
441  {
442  snPoints.push_back(snv);
443  }
444  else if (abs(snv.y) < abs(snPoints[0].y))
445  {
446  snPoints[0] = snv;
447  }
448  }
449  else
450  {
451  snPoints.push_back(snv);
452  }
453  }
454  return snPoints;
455 } // AnisotropicInterpolator::TransformPoint
456 
457 } // namespace xms
458 
459 #ifdef CXX_TEST
460 
464 
465 // namespace xms {
466 using namespace xms;
467 
472 //------------------------------------------------------------------------------
474 //------------------------------------------------------------------------------
476 {
477  VecPt3d centerline = {{-1, 0, 0}, {0, 0, 0}, {1, 0, 0}};
478  VecPt3d crossSections = {{-0.5, -1, 0}, {-0.5, 0, -1}, {-0.5, 1, 0},
479  {0.5, -1, 0}, {0.5, 0, -1}, {0.5, 1, 0}};
480  Pt3d target(0, 0, 0);
481 
482  AnisotropicInterpolator interpolator;
483  bool pickClosest = true;
484  interpolator.SetPoints(centerline, crossSections, pickClosest);
485  Pt3d sn0 = interpolator.TransformPoint(crossSections[0], pickClosest)[0];
486  Pt3d sn1 = interpolator.TransformPoint(crossSections[1], pickClosest)[0];
487  Pt3d sn2 = interpolator.TransformPoint(crossSections[2], pickClosest)[0];
488  Pt3d sn3 = interpolator.TransformPoint(crossSections[3], pickClosest)[0];
489  Pt3d sn4 = interpolator.TransformPoint(crossSections[4], pickClosest)[0];
490  Pt3d sn5 = interpolator.TransformPoint(crossSections[5], pickClosest)[0];
491 
492  VecPt3d snPoints = interpolator.GetInterpolationPoints();
493  double xScale = 1.0;
494 
495  VecPt3d snPoints2 = {sn1, sn4};
496  double result = interpolator.InterpolatePoint(target, snPoints2, xScale);
497  double expect(-1.0);
498  TS_ASSERT_EQUALS(expect, result);
499 
500  VecPt3d snPoints3 = {sn0, sn2, sn3, sn5};
501  result = interpolator.InterpolatePoint(target, snPoints3, xScale);
502  expect = 0.0;
503  TS_ASSERT_EQUALS(expect, result); // -0.6250
504 
505  result = interpolator.InterpolatePoint(target, snPoints, xScale);
506  expect = -0.714;
507  TS_ASSERT_DELTA(expect, result, 0.01);
508 } // AnisotropicInterpolatorUnitTests::testSimple
509 //------------------------------------------------------------------------------
522 //------------------------------------------------------------------------------
524 {
525  VecPt3d centerline = {{-10, 0, 0}, {-10, 10, 0}, {10, 10, 0}, {10, 0, 0}};
526  VecPt3d crossSections = {{-15, 15, 0}, {-10, 10, -1}, {-5, 5, 0},
527  {5, 5, 0}, {10, 10, -1}, {15, 15, 0}};
528  Pt3d target(0, 5, 0);
529 
530  AnisotropicInterpolator interpolator;
531  bool pickClosest = false;
532  interpolator.SetPoints(centerline, crossSections, pickClosest);
533 
534  double cross = interpolator.CrossProduct(0, crossSections[2]);
535  TS_ASSERT(cross < 0.0);
536 
537  VecPt3d sn2 = interpolator.TransformPoint(crossSections[2]);
538  VecPt3d sn2Expected = {{5, -5, 0}, {15, -5, 0}, {35, -15, 0}};
539  TS_ASSERT_DELTA_VECPT3D(sn2Expected, sn2, 0.001);
540  VecPt3d sn3 = interpolator.TransformPoint(crossSections[3]);
541  VecPt3d sn3Expected = {{5, -15, 0}, {25, -5, 0}, {35, -5, 0}};
542  TS_ASSERT_DELTA_VECPT3D(sn3Expected, sn3, 0.001);
543 
544  VecPt3d snPoints = interpolator.GetInterpolationPoints();
545  double xScale = 1.0;
546  double result = interpolator.InterpolatePoint(target, snPoints, xScale);
547  double expect = -0.2699;
548  TS_ASSERT_DELTA(expect, result, 0.01);
549 
550  Pt3d target2(0, 15, 0);
551  double result2 = interpolator.InterpolatePoint(target, snPoints, xScale);
552  TS_ASSERT_DELTA(result, result2, 1.0e-6);
553 
554  Pt3d target3 = crossSections[1];
555  target3.z = 10;
556  double result3 = interpolator.InterpolatePoint(target3, snPoints, xScale);
557  TS_ASSERT_DELTA(crossSections[1].z, result3, 1.0e-6);
558 } // AnisotropicInterpolatorUnitTests::testCrossSectionThroughPoint
559 //------------------------------------------------------------------------------
577 //------------------------------------------------------------------------------
579 {
580  VecPt3d centerline = {{-10, 10, 0}, {0, 0, 0}, {10, 10, 0}};
581  VecPt3d crossSections = {{-8, 2, -3}, {-2, 8, 2}, {2, 8, 2}, {8, 2, -3}};
582 
583  AnisotropicInterpolator interpolator;
584  bool pickClosest = true;
585  interpolator.SetPoints(centerline, crossSections, pickClosest);
586 
587  VecPt3d snPoints = interpolator.GetInterpolationPoints();
588  double xScale = 0.25;
589  double idwPower = 3.0;
590 
591  // Test interpolate to point in both stations
592  Pt3d target1(0, 4.242, 0);
593  double result1 = interpolator.InterpolatePoint(target1, snPoints, xScale, idwPower);
594  double expect1 = 2.0;
595  TS_ASSERT_DELTA(expect1, result1, 0.1);
596 
597  // test interpolate to point inbetween but outside of both stations
598  Pt3d target2(0, -4.242, 0);
599  double result2 = interpolator.InterpolatePoint(target2, snPoints, xScale, idwPower);
600  double expect2 = -3.0;
601  TS_ASSERT_DELTA(expect2, result2, 0.1);
602 
603  VecPt3d transform1 = interpolator.TransformPoint(target1);
604  VecPt3d transform1Expected = {{11.14, 3.00, 0}, {17.14, 3.00, 0}};
605  TS_ASSERT_DELTA_VECPT3D(transform1Expected, transform1, 0.01);
606 
607  VecPt3d transform2 = interpolator.TransformPoint(target2);
608  VecPt3d transform2Expected = {{14.14, -4.242, 0}};
609  TS_ASSERT_DELTA_VECPT3D(transform2Expected, transform2, 0.01);
610 } // AnisotropicInterpolatorUnitTests::testAmbiguity()
611 
612 #endif // CXX_TEST
VecDbl m_accLengths
The accumulated distance along the centerline of each point.
VecPt3d m_centerLinePts
The polyline representing the centerline.
double InterpolatePoint(const Pt3d &a_target, const VecPt3d &a_interpolationPoints, double a_xScale, double a_IDWPower=2, double a_tol=1.0e-5)
Interpolate the z coordinate of the interpolation points to point a_target.
void testAmbiguity()
Test to demonstrate ambiguity.
LineParameters GetLineParameters(const Pt3d &a_p0, const Pt3d &a_p1) const
Compute the 2d slope and intercept for a line through two points.
#define TS_ASSERT_DELTA_VECPT3D(a, b, delta)
bool GetIntersectionOfSegmentWithPoint(size_t a_segmentIndex, const Pt3d &a_p, Pt3d &a_intersection, double &a_param) const
Get the intersecting point and its parameter of a line through point a_p normal to a segment of m_cen...
bool Intersection(const LineParameters &a_other, Pt3d &a_p) const
Get the intersection point of this and another line.
VecPt3d TransformPoint(const Pt3d &a_p, bool a_onlyClosest=false)
Transforms a point from (x, y, z) to (s, n, z) coordinates. s is for station (distance along the cent...
double CalculateIDW(double a_xScale, const VecPt3d &a_points, const Pt3d &a_target, double a_power, double a_tol=1.0e-5)
Calculates the Inverse Distance Weighted interpolation of a set of points at a target point...
VecPt3d m_SNPoints
The interpolation points.
VecPt3d ComputeInterpolationPoints(const VecPt3d &a_interpolationPts, bool a_pickClosest)
Transforms a set of points from normal (x, y, z) coordinates to (s, n, z) coordinates. Note: The z coordinate is left unchanged.
LineParameters NormalThrough(const Pt3d &a_p) const
Get the LineParameters for a line normal to this through a point.
double CrossProduct(size_t a_segmentIndex, const Pt3d &a_p) const
Compute the 2d cross product of the vectors from a_p to the 1st and last points in a segment of m_cen...
Hold data of transforming from xyz to station,normal space.
double AnisotropicDistanceSquared(const Pt3d &a_p0, const Pt3d &a_p1, double a_xScale)
Compute the distance squared between two points, after a scale factor is applied to their x coordinat...
void SetPoints(const VecPt3d &a_centerlinePts, const VecPt3d &a_interpolationPts, bool a_pickClosest=false)
Set the centerline points to parameterize to.
const VecPt3d & GetInterpolationPoints() const
Get the transformed interpolation points in (s, n, z) space.
void testSimple()
Tests simple interpolation problem.
void testCrossSectionThroughPoint()
Tests interpolation of crosssection going through a centerline point.
std::vector< SNResult > VecSNResult
A Vector of station normal results (for a given point).
#define XM_NODATA
Class to transform points relative to a centerline polyline into a space where the centerline segment...
double Distance(const Pt3d &a_p0, const Pt3d &a_p1) const
Compute the distance between two points.
double GetParameterInSegment(size_t a_segmentIndex, const Pt3d &a_p) const
Compute the parameter for a point in a segment of m_centerLinePts. The parameter will be between 0 an...
LineParameters(double a_slope, double a_intercept)
Constructor from slope and intercept.
void GetNormalPointParameter(size_t a_segmentIndex, const Pt3d &a_p, double &a_lastParam, VecSNResult &a_results) const
Append the transform (if any) of a point to a centerline segment.
double m_slope
Slope of the line. 0 is horizontal. Nan is vertical.
void GetAllNormalPointParameters(const Pt3d &a_p, VecSNResult &a_results)
Get the SNResults for a point.
double m_intercept
x intercept if vertical; else, the y intercept.
std::vector< LineParameters > m_lineParams
const double kVERTICAL
Slope value indicating that a line is vertical.
std::vector< Pt3d > VecPt3d
2D Line definition. If vertical m_slope is Nan.