OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
AtpTileSource.cpp
Go to the documentation of this file.
1 //**************************************************************************************************
2 //
3 // OSSIM Open Source Geospatial Data Processing Library
4 // See top level LICENSE.txt file for license information
5 //
6 //**************************************************************************************************
7 
8 #include "AtpTileSource.h"
9 #include "../AtpCommon.h"
10 #include "AtpGenerator.h"
15 
16 using namespace std;
17 
18 namespace ATP
19 {
20 double AtpTileSource::s_minVectorResDiff = 0;
21 double AtpTileSource::s_maxDiffRatio = 1.0;
22 double AtpTileSource::s_cosMaxAngleDiff = 0;
23 double AtpTileSource::s_maxPaxDev = 1.0;
24 double AtpTileSource::s_maxPaxPix = 2.0;
25 unsigned int AtpTileSource::s_minNumConsistent = 3;
26 unsigned int AtpTileSource::s_percentConsistent = 50;
27 unsigned int AtpTileSource::s_numTpsPerTile = 2;
28 unsigned int AtpTileSource::s_numFilterIterations = 0;
29 bool AtpTileSource::s_useRasterMode = false;
30 bool AtpTileSource::s_initialized = false;
31 
32 AtpTileSource::AtpTileSource()
33 : m_considerParallax (true)
34 {
35 }
36 
38 : ossimImageCombiner(inputSources),
39  m_considerParallax (true)
40 
41 {
42 }
43 
45 : m_generator(generator),
46  m_considerParallax (true)
47 {
49  if (m_generator)
50  {
51  connectMyInputTo(0, m_generator->getRefChain().get());
52  connectMyInputTo(1, m_generator->getCmpChain().get());
53  }
55  initialize();
56 }
57 
59 {
60  const char* MODULE = "AtpTileSource::initialize() -- ";
61  if (!s_initialized)
63 
65  m_tiePoints.clear();
66  if ( getNumberOfInputs() < 2)
67  return;
68 }
69 
71 {
72  // The AtpTileSources cannot be relied on to return valid pixel data, since their main product
73  // are the tie points, not pixels. However, the tile is used to communicate scalar type, rect,
74  // and other tile metadata, so is still needed:
75  m_tile = NULL;
76  if ( getNumberOfInputs() > 1)
77  {
78  ossimImageSource* firstSource = dynamic_cast<ossimImageSource*>(theInputObjectList[0].get());
79  m_tile = ossimImageDataFactory::instance()->create(this, firstSource);
80  m_tile->initialize();
81  }
82 }
83 
85 {
86  const char* MODULE = "AtpTileSource::filterPoints() -- ";
87  AtpConfig& config = AtpConfig::instance();
88 
89  // First remove points with no matches:
90  auto tiePoint = m_tiePoints.begin();
91  while (tiePoint != m_tiePoints.end())
92  {
93  if ((*tiePoint) && (*tiePoint)->hasValidMatch())
94  ++tiePoint;
95  else
96  tiePoint = m_tiePoints.erase(tiePoint);
97  }
98 
99  // No filtering done for raster mode:
100  if (s_useRasterMode)
101  return;
102 
103  if (config.diagnosticLevel(3))
104  {
105  // Annotate red before filtering:
106  m_generator->m_annotatedRefImage->annotateResiduals(m_tiePoints, 255, 0, 0);
107  m_generator->m_annotatedCmpImage->annotateCorrelations(m_tiePoints, 255, 0, 0);
108  }
109 
110  // Check for consistency check override
111  if (s_minNumConsistent == 0)
112  return;
113 
114  // Don't accept any if there are an insufficient number of neighbors:
115  if (m_tiePoints.size() < s_minNumConsistent)
116  {
117  m_tiePoints.clear();
118  return;
119  }
120 
121  if (config.paramExists("considerParallax"))
122  m_considerParallax = config.getParameter("considerParallax").asBool();
123  if (m_considerParallax)
124  {
125  // If the perspective differences are small, fall back to non-parallax check:
126  computeParallax();
127  if ((fabs(m_paxInfo.dx_dH) < 1.0) && (fabs(m_paxInfo.dy_dH) < 1.0))
128  {
129  if (config.diagnosticLevel(5))
130  CINFO << MODULE << "Insignificant parallax, falling back to non-parallax filtering."
131  << endl;
133  }
134  else
135  {
138  }
139  }
140  else
142 
143  if (config.diagnosticLevel(3))
144  {
145  // Annotate yellow before pruning:
146  m_generator->m_annotatedRefImage->annotateResiduals(m_tiePoints, 255, 255, 0);
147  m_generator->m_annotatedCmpImage->annotateCorrelations(m_tiePoints, 255, 255, 0);
148  }
149 
150  pruneList();
151 
152  if (config.diagnosticLevel(2))
153  {
154  CINFO<<MODULE<<"After filtering & pruning: num TPs in tile = "<<m_tiePoints.size()<<endl;
155  if (config.diagnosticLevel(3))
156  {
157  // Annotate Green after accepting TP:
158  m_generator->m_annotatedRefImage->annotateResiduals(m_tiePoints, 0, 255, 0);
159  m_generator->m_annotatedCmpImage->annotateCorrelations(m_tiePoints, 0, 255, 0);
160  //m_annotatedRefImage->annotateTPIDs(m_tiePoints, 180, 180, 0);
161  }
162  }
163 }
164 
166 {
167  // May be called recursively, limited by s_numFilterIterations < 5
168 
169  const char* MODULE = "AtpTileSource::filterWithParallax() ";
170  AtpConfig& config = AtpConfig::instance();
171  if (m_tiePoints.size() < 2)
172  {
173  m_tiePoints.clear();
174  return;
175  }
176 
178 
179  // This is where recursion is stopped:
181  {
182  m_tiePoints.clear();
183  if (config.diagnosticLevel(5))
184  {
185  CINFO << MODULE << "Could not establish good parallax statistics for this set. "
186  << "Removing all points for this tile." << endl;
187  }
188  return;
189  }
190 
191  // Loop to eliminate bad peaks and inconsistent ATPs in tile:
192  ossimDpt residual;
193  bool statsChanged = false;
194  auto tiePoint = m_tiePoints.begin();
195  while (tiePoint != m_tiePoints.end())
196  {
197  // Check point's active peak against the set of residuals along parallax:
198  auto entry = m_paxInfo.distanceMap.find((*tiePoint)->getTiePointId());
199  if (entry == m_paxInfo.distanceMap.end())
200  { // Should never happen
201  tiePoint = m_tiePoints.erase(tiePoint);
202  continue;
203  }
204  double distance = entry->second;
205 
206  // Loop to find a match in this ATP that is consistent.
207  while (distance > m_paxInfo.maxDistance)
208  {
209  // This residual is outside the acceptable limit, try the next match in the tiepoint or
210  // remove it if it has no more peaks.
211  statsChanged = true;
212  if (!(*tiePoint)->bumpActiveMatch())
213  {
214  if (config.diagnosticLevel(5))
215  {
216  CINFO << MODULE << "Removing TP " << (*tiePoint)->getTiePointId() << endl;
217  CINFO << " distance: " << distance << ", maxDistance: " << m_paxInfo.maxDistance << endl;
218  }
219  tiePoint = m_tiePoints.erase(tiePoint);
220  break;
221  }
222  else if (config.diagnosticLevel(5))
223  {
224  CINFO << MODULE << "Bumping active match for TP " << (*tiePoint)->getTiePointId()
225  << endl;
226  CINFO << " distance: " << distance << ", maxDistance: " << m_paxInfo.maxDistance << endl;
227  }
228 
229  // A new peak is being used, will need to recompute statistics later.
230  (*tiePoint)->getVectorResidual(residual);
231  if (m_paxInfo.dx_dH == 0.0)
232  distance = residual.x;
233  else if (m_paxInfo.dx_dH == 0.0)
234  distance = residual.y;
235  else
236  {
238  residual.y) / m_paxInfo.denom;
239  }
240  }
242  {
243  // The match was deemed consistent. proceed to next point:
244  ++tiePoint;
245  }
246  }
247 
248  if (statsChanged)
249  {
250  // Recursive call to recompute statistics and eliminate suspect points
252  if (config.diagnosticLevel(5))
253  {
254  CINFO << MODULE << "Iterating filterWithParallax after ATP list change (N = "
255  << s_numFilterIterations << ")."<< endl;
256  }
258  }
259 }
260 
262 {
263  // Use the point at tile center to establish reference ground point at two different elevations:
264  static const double DELTA_HEIGHT = 500.0;
265  ossimDpt vpt, ipt0, ipt1;
266  ossimGpt gpt0, gpt1;
267 
268  // Possible debug mode will not have a tile assigned:
270  m_tiePoints[0]->getRefViewPoint(vpt);
271  else
273  ossimImageGeometry *geom = m_generator->m_refIVT->getViewGeometry();
274  geom->localToWorld(vpt, gpt0);
275  if (gpt0.isHgtNan())
277  gpt1 = gpt0;
278  gpt1.hgt += DELTA_HEIGHT;
279 
280  // Determine dx/dh and dy/dh for ref image:
281  geom = m_generator->m_refIVT->getImageGeometry();
282  geom->worldToLocal(gpt0, ipt0);
283  geom->worldToLocal(gpt1, ipt1);
284  double dx_ref = (ipt1.x - ipt0.x);
285  double dy_ref = (ipt1.y - ipt0.y);
286 
287  // Determine dx/dh and dy/dh for cmp image:
288  geom = m_generator->m_cmpIVT->getImageGeometry();
289  geom->worldToLocal(gpt0, ipt0);
290  geom->worldToLocal(gpt1, ipt1);
291  double dx_cmp = (ipt1.x - ipt0.x);
292  double dy_cmp = (ipt1.y - ipt0.y);
293 
294  // Compute parallax direction REF-->CMP
295  m_paxInfo.dx_dH = dx_cmp - dx_ref;
296  m_paxInfo.dy_dH = dy_cmp - dy_ref;
297  if (m_paxInfo.dx_dH != 0)
298  {
301  }
302  else
303  {
306  }
307  //CINFO << "### dx_dH: " << m_paxInfo.dx_dH << endl; // TODO REMOVE
308  //CINFO << "### dy_dH: " << m_paxInfo.dy_dH << endl; // TODO REMOVE
309  //CINFO << "### parallaxSlope: " << m_paxInfo.parallaxSlope << endl; // TODO REMOVE
310 }
311 
312 
314 {
315  // Determining the parallax direction (slope) is easy. It is the offset (y-intercept) that
316  // depends on the collection of residuals.
317  // Use the point at tile center to establish dx/dH and dy/dH in view-space:
318  // Compute parallax slope and intercept:
319  ossimDpt residual;
320  if (m_paxInfo.dx_dH != 0.0)
321  {
322  // Compute parallax intercept using collection of all primary matches:
323  double sum_c_i = 0;
324  for (auto &tiePoint : m_tiePoints)
325  {
326  tiePoint->getVectorResidual(residual);
327  sum_c_i += residual.y - m_paxInfo.parallaxSlope * residual.x;
328  }
329  m_paxInfo.parallaxOffset = sum_c_i/m_tiePoints.size();
330  }
331 
332  // Compute statistics of residual vector distribution, saving individual distance samples:
333  m_paxInfo.distanceMap.clear();
334  double distance, sumDistance2=0.0;
335  for (auto &tiePoint : m_tiePoints)
336  {
337  tiePoint->getVectorResidual(residual);
338  if (m_paxInfo.dx_dH == 0)
339  distance = residual.x;
340  else if (m_paxInfo.dy_dH == 0)
341  distance = residual.y;
342  else
343  {
344  distance = fabs(m_paxInfo.parallaxOffset + m_paxInfo.parallaxSlope*residual.x - residual.y)/m_paxInfo.denom;
345  }
346  sumDistance2 += distance*distance;
347  m_paxInfo.distanceMap.emplace(tiePoint->getTiePointId(), distance);
348  //CINFO << "### TP: "<<tiePoint->getTiePointId()<<" distance: " << distance << endl; // TODO REMOVE
349 
350  }
351  double sigmaDistance = sqrt(sumDistance2/(m_tiePoints.size()-1));
352  m_paxInfo.maxDistance = s_maxPaxDev*sigmaDistance;
355 
356  //CINFO << "### sigmaDistance: " << sigmaDistance << endl; // TODO REMOVE
357  //CINFO << "### maxDistance: " << m_paxInfo.maxDistance << endl; // TODO REMOVE
358 }
359 
361 {
362  const char* MODULE = "AtpTileSource::filterWithoutParallax() ";
363  AtpConfig& config = AtpConfig::instance();
364 
365  // Loop to eliminate bad peaks and inconsistent ATPs in tile:
366  auto tiePoint = m_tiePoints.begin();
367  while (tiePoint != m_tiePoints.end())
368  {
369  // The "tiepoint" may not have any matches -- it may be just a feature:
370  ossimDpt r_atp, r_nbr;
371  (*tiePoint)->getVectorResidual(r_atp);
372 
373  if (config.diagnosticLevel(5))
374  {
375  ossimDpt refPt;
376  (*tiePoint)->getRefImagePoint(refPt);
377  CINFO<<"AutoTiePoint::checkConsistency() -- Processing TP "<<(*tiePoint)->getTiePointId()
378  <<" at REF image pt: ("<<refPt.x<<", "<<refPt.y<<")"<<endl;
379  }
380 
381  // Loop over all of the TPs neighbors to count the number that are consistent:
382  unsigned int num_consistent_neighbors = 0;
383  for(auto &neighbor : m_tiePoints)
384  {
385  if ((*tiePoint)->getTiePointId() == neighbor->getTiePointId())
386  continue;
387 
388  // Check for simple minimum vector difference:
389  neighbor->getVectorResidual(r_nbr);
390  double diff = (r_nbr - r_atp).length();
391  if (diff < s_minVectorResDiff)
392  {
393  num_consistent_neighbors++;;
394  continue;
395  }
396 
397  // Compute angle and magnitude ratio between residuals. Note no check for valid peak:
398  double cos_theta=1.0, mag_ratio=0;
399  double r0 = r_atp.length();
400  double r1 = r_nbr.length();
401  mag_ratio = 2.0*fabs(r1-r0)/(r0+r1);
402  if ((r1 > 0.0) && (r0 > 0.0))
403  cos_theta = (r_atp.x*r_nbr.x + r_atp.y*r_nbr.y)/(r0*r1);
404 
405  // Test for consistency with this neighbor's peak:
406  if ((mag_ratio < s_maxDiffRatio) && (cos_theta > s_cosMaxAngleDiff))
407  num_consistent_neighbors++;;
408  }
409 
410  // Decide if this peak was good or out there:
411  int minConsistent = (int) ceil(s_percentConsistent*(m_tiePoints.size()-1)/100);
412  if (minConsistent < (int)s_minNumConsistent)
413  minConsistent = s_minNumConsistent;
414  if (num_consistent_neighbors < minConsistent)
415  {
416  // Peak was bad, fetch next strongest match or exit loop if no more available:
417  if (!(*tiePoint)->bumpActiveMatch())
418  {
419  if (config.diagnosticLevel(5))
420  CINFO << MODULE << "Removing TP " << (*tiePoint)->getTiePointId() << endl;
421  tiePoint = m_tiePoints.erase(tiePoint);
422  if (m_tiePoints.empty())
423  break;
424  }
425  }
426  else
427  {
428  // Match is consistent, keep the tiepoint in the list an advance to the next tiepoint
429  tiePoint++;
430  }
431  }
432 }
433 
434 
436 {
437  const char* MODULE = "AtpTileSource::pruneList() ";
438  AtpConfig &config = AtpConfig::instance();
439 
440  // Use a map to sort by confidence measure:
441  multimap<double, shared_ptr<AutoTiePoint> > atpMap;
442  auto atp = m_tiePoints.begin();
443  double confidence;
444  while (atp != m_tiePoints.end())
445  {
446  if (!(*atp)) // Should never happen
447  {
448  CWARN<<MODULE<<"WARNING: Null AutoTiePoint encountred in list. Skipping point."<<endl;
449  ++atp;
450  continue;
451  }
452 
453  // The "tiepoint" may not have any matches -- it may be just a feature:
454  bool hasValidMatch = (*atp)->getConfidenceMeasure(confidence);
455  if (hasValidMatch)
456  atpMap.emplace(1.0/confidence, *atp);
457 
458  atp++;
459  }
460 
461  // Skim off the top best:
462  m_tiePoints.clear();
463  multimap<double, shared_ptr<AutoTiePoint> >::iterator tp = atpMap.begin();
464  while (tp != atpMap.end())
465  {
466  m_tiePoints.push_back(tp->second);
467  if (m_tiePoints.size() == s_numTpsPerTile)
468  break;
469  tp++;
470  }
471 }
472 
474 {
475  static const char* MODULE = "AtpTileSource::initializeStaticMembers() -- ";
476 
477  AtpConfig& config = AtpConfig::instance();
478  s_maxDiffRatio = config.getParameter("maxResMagDiffRatio").asFloat();
480  {
481  CWARN<<MODULE<<"Bad or missing parameter in config: "
482  "maxResMagDiffRatio = "<<s_maxDiffRatio<<". Defaulting to 0.1."<<endl;
483  s_maxDiffRatio = 0.1;
484  }
485 
486  double theta = config.getParameter("maxResAngleDiff").asFloat();
487  if (ossim::isnan(theta) || (theta <= 0))
488  {
489  CWARN<<MODULE<<"Bad or missing parameter in config: "
490  "maxAngleDiff = "<<theta<<". Defaulting to 10 deg."<<endl;
491  theta = 10.0;
492  }
494 
495  s_minVectorResDiff = config.getParameter("minVectorResDiff").asFloat();
497  {
498  CWARN<<MODULE<<"Bad or missing parameter in config: "
499  "minVectorResDiff = "<<s_minVectorResDiff<<". Defaulting to 1.0."<<endl;
500  s_minVectorResDiff = 1.0;
501  }
502 
503  s_minNumConsistent = config.getParameter("minNumConsistentNeighbors").asUint();
504  if (s_minNumConsistent == 0)
505  {
506  CWARN<<MODULE<<"Bad or missing parameter in config: "
507  "minNumConsistentNeighbors = "<<s_minNumConsistent<<". Defaulting to 3."<<endl;
508  s_minNumConsistent = 3.0;
509  }
510 
511  s_percentConsistent = config.getParameter("percentConsistentThreshold").asUint();
512  if (s_percentConsistent == 0)
513  {
514  CWARN<<MODULE<<"Bad or missing parameter in config: "
515  "percentConsistentThreshold = "<<s_percentConsistent<<". Defaulting to 50."<<endl;
516  s_percentConsistent = 50;
517  }
518 
519  s_numTpsPerTile = config.getParameter("numTiePointsPerTile").asUint();
520  if (s_numTpsPerTile == 0)
521  {
522  CWARN<<MODULE<<"Bad or missing parameter in config: "
523  "numTiePointsPerTile = "<<s_numTpsPerTile<<". Defaulting to 2."<<endl;
524  s_numTpsPerTile = 2;
525  }
526 
527  s_maxPaxDev = config.getParameter("maxResParallaxDev").asFloat();
528  if (ossim::isnan(s_maxPaxDev) || (s_maxPaxDev <= 0))
529  {
530  CWARN<<MODULE<<"Bad or missing parameter in config: "
531  "maxResParallaxDev = "<<s_maxPaxDev<<". Defaulting to 1.0."<<endl;
532  s_maxPaxDev = 1.0;
533  }
534 
535  s_maxPaxPix = (double) config.getParameter("maxResParallaxPix").asUint();
536  if (ossim::isnan(s_maxPaxPix) || (s_maxPaxPix <= 0))
537  {
538  CWARN<<MODULE<<"Bad or missing parameter in config: "
539  "maxResParallaxPix = "<<s_maxPaxPix<<". Defaulting to 1.0."<<endl;
540  s_maxPaxPix = 2.0;
541  }
542 
543  if (config.paramExists("useRasterMode"))
544  s_useRasterMode = config.getParameter("useRasterMode").asBool();
545 
546  s_initialized = true;
547 }
548 
550 {
551  // Engineering code for manually specifying TPs to test consistency checking
552  string id;
553  ossimDpt rip, cip;
554  NEWMAT::SymmetricMatrix cov;
555  m_tiePoints.clear();
556  for (auto &tp : tpList)
557  {
558  shared_ptr<AutoTiePoint> atp (new AutoTiePoint(m_generator, tp->getTiePointId()));
559  tp->getImagePoint(0,id,rip,cov);
560  tp->getImagePoint(1,id,cip,cov);
561  atp->setRefImagePt(rip);
562  atp->addImageMatch(cip);
563  m_tiePoints.emplace_back(atp);
564  }
565 }
566 
567 }
568 
virtual bool addListener(ossimListener *listener)
JsonParam & getParameter(const char *paramName)
Returns a parameter (might be a null parameter if paramName not found in the configuration.
Definition: JsonConfig.cpp:377
Base class for all automatic tiepoints.
Definition: AutoTiePoint.h:30
This will be a base for all combiners.
std::shared_ptr< AtpGenerator > m_generator
Definition: AtpTileSource.h:85
double nan()
Method to return ieee floating point double precision NAN.
Definition: ossimCommon.h:135
double y
Definition: ossimDpt.h:165
std::map< std::string, double > distanceMap
Definition: AtpTileSource.h:66
double asFloat() const
Definition: JsonConfig.cpp:278
unsigned int asUint() const
Definition: JsonConfig.cpp:264
virtual ossimDataObjectStatus getDataObjectStatus() const
ossim_float64 hgt
Height in meters above the ellipsiod.
Definition: ossimGpt.h:274
static ossimElevManager * instance()
METHOD: instance() Implements singelton pattern.
static bool s_useRasterMode
bool paramExists(const char *paramName) const
Definition: JsonConfig.cpp:395
double length() const
Definition: ossimDpt.h:81
virtual void initialize()
Initialize the data buffer.
bool isHgtNan() const
Definition: ossimGpt.h:143
static unsigned int s_minNumConsistent
Definition: AtpTileSource.h:97
virtual void initialize()
std::vector< ossimRefPtr< ossimConnectableObject > > ConnectableObjectList
static ossimImageDataFactory * instance()
static double s_maxDiffRatio
Definition: AtpTileSource.h:93
std::vector< std::shared_ptr< TiePoint > > TiePointList
Definition: TiePoint.h:21
bool localToWorld(const ossimDpt &local_pt, ossimGpt &world_pt) const
Exposes the 3D projection from image to world coordinates.
void setTiePoints(ossim::TiePointList &atpList)
Used for testing.
ParallaxInfo m_paxInfo
Definition: AtpTileSource.h:88
virtual double getHeightAboveEllipsoid(const ossimGpt &gpt)
Base class for OSSIM-based ATP generators.
Definition: AtpGenerator.h:33
bool asBool() const
Definition: JsonConfig.cpp:257
static unsigned int s_percentConsistent
Definition: AtpTileSource.h:98
virtual ossimIrect getImageRectangle() const
static double s_minVectorResDiff
Definition: AtpTileSource.h:92
virtual ossim_int32 connectMyInputTo(ossimConnectableObject *inputObject, bool makeOutputConnection=true, bool createEventFlag=true)
Will try to connect this objects input to the passed in object.
void pruneList()
Caps the max number of TPs given the list, which is the list of filtered TPs for the tile...
virtual ossimRefPtr< ossimImageData > create(ossimSource *owner, ossimScalarType scalar, ossim_uint32 bands=1) const
double cosd(double x)
Definition: ossimCommon.h:259
THESE FUNCTIONS REQUIRE OPENCV.
ossimRefPtr< ossimImageData > m_tile
Definition: AtpTileSource.h:87
bool diagnosticLevel(unsigned int level) const
Convenience method returns TRUE if the currently set diagnostic level is <= level.
Definition: JsonConfig.cpp:461
Container class that holds both 2D transform and 3D projection information for an image Only one inst...
ConnectableObjectList theInputObjectList
Holds a list of input objects.
static unsigned int s_numFilterIterations
static unsigned int s_numTpsPerTile
Definition: AtpTileSource.h:99
static double s_cosMaxAngleDiff
Definition: AtpTileSource.h:94
void computeParallaxStatistics()
virtual ossim_uint32 getNumberOfInputs() const
Returns the number of input objects.
double x
Definition: ossimDpt.h:164
static AtpConfig & instance()
Singleton implementation.
Definition: AtpConfig.cpp:20
static double s_maxPaxPix
Definition: AtpTileSource.h:96
bool worldToLocal(const ossimGpt &world_pt, ossimDpt &local_pt) const
Exposes the 3D world-to-local image coordinate reverse projection.
static bool s_initialized
#define CWARN
virtual void allocate()
float distance(double lat1, double lon1, double lat2, double lon2, int units)
#define CINFO
static double s_maxPaxDev
Definition: AtpTileSource.h:95
void getCenter(ossimDpt &center_point) const
Definition: ossimIrect.cpp:672
Singleton class maintaining parameters affecting the automatic tie point generation.
Definition: AtpConfig.h:24
bool isnan(const float &v)
isnan Test for floating point Not A Number (NAN) value.
Definition: ossimCommon.h:91