OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimSensorModel.cpp
Go to the documentation of this file.
1 //*****************************************************************************
2 // FILE: ossimSensorModel.cc
3 //
4 // License: LGPL
5 //
6 // See LICENSE.txt file in the top level directory for more details.
7 //
8 // AUTHOR: Oscar Kramer
9 //
10 // DESCRIPTION:
11 // Contains implementation of class ossimSensorModel. This is the base
12 // class to all sensor model-related projections including replacement
13 // models such as coarse grids and polynomial models. This base class
14 // supports adjustable parameters for registration adjustment.
15 //
16 // Important note to sensor model implementors: In order to avoid a separate
17 // set of "initial adjustable parameters," this design assumes ALL initial
18 // values are 0. When designing the derived-class model, insure that the
19 // adjustable parameters are 0-based. This applies to the adjustable param
20 // array declared in this base class only. The derived classes can declare
21 // their own adjstable params that are more descriptive and that can be
22 // assigned an initial value thet is non-zero, but that are linearly related
23 // to the adjustable params of this class. In that case, the updateModel()
24 // method will compute the derived-class's parameters based on
25 // theAdjustableParams array, after an adjustment is made.
26 //
27 // LIMITATIONS: None.
28 //
29 //*****************************************************************************
30 // $Id: ossimSensorModel.cpp 23564 2015-10-02 14:12:25Z dburken $
31 #include <iostream>
32 #include <sstream>
33 using namespace std;
34 
35 // #include <stdio.h>
36 
38 
40 
44 #include <ossim/base/ossimDrect.h>
45 #include <ossim/base/ossimDatum.h>
52 
53 #include <ossim/matrix/newmatrc.h>
54 
55 //***
56 // Define Trace flags for use within this file:
57 //***
58 #include <ossim/base/ossimTrace.h>
59 static ossimTrace traceExec ("ossimSensorModel:exec");
60 static ossimTrace traceDebug ("ossimSensorModel:debug");
61 
62 static const char* REF_GPT_LAT_KW = "ref_point_lat";
63 static const char* REF_GPT_LON_KW = "ref_point_lon";
64 static const char* REF_GPT_HGT_KW = "ref_point_hgt";
65 static const char* REF_IPT_LINE_KW = "ref_point_line";
66 static const char* REF_IPT_SAMP_KW = "ref_point_samp";
67 static const char* IMAGE_ID_KW = "image_id";
68 static const char* SENSOR_ID_KW = "sensor";
69 static const ossimString NULL_STRING = "NULL";
70 static const double RAY_ORIGIN_HEIGHT = 10000.0; //meters
71 
72 
73 //DEBUG TBR : output ops
74 std::ostream& operator<<(std::ostream& os, NEWMAT::GeneralMatrix& mat)
75 {
76  int nr=mat.Nrows();
77  int nc=mat.Ncols();
78 
79  NEWMAT::MatrixRow crow(&mat,NEWMAT::LoadOnEntry);
80 // NEWMAT::MatrixRowCol crow;
81  mat.RestoreRow(crow);
82  for (int r=0;r<nr;++r)
83  {
84  for (int c=0;c<nc;++c)
85  {
86  os<<*(crow.Data()+c)<<" ";
87  }
88  os<<std::endl;
89  mat.NextRow(crow);
90  }
91  return os;
92 }
93 
94 //*****************************************************************************
95 // DEFAULT CONSTRUCTOR: ossimSensorModel()
96 //*****************************************************************************
98  :
101  theImageSize (0, 0),
102  theSubImageOffset (0.0, 0.0),
103  theImageID (),
104  theSensorID (),
105  theGSD (0.0, 0.0),
106  theMeanGSD (0.0),
107  theRefGndPt (0.0, 0.0, 0.0),
108  theRefImgPt (0.0, 0.0),
109  theBoundGndPolygon (),
110  theImageClipRect (),
111  theRelPosError (0.0),
112  theNominalPosError (0.0),
113  theParWRTx (0.0, 0.0),
114  theParWRTy (0.0, 0.0),
115  theParWRTz (0.0, 0.0),
116  theObs (0.0, 0.0),
117  theResid (0.0, 0.0),
118  theExtrapolateImageFlag(false),
119  theExtrapolateGroundFlag(false)
120 {
121  if (traceExec())
122  {
124  << "DEBUG ossimSensorModel::ossimSensorModel(geom_kwl): entering...\n"
125  << "DEBUG ossimSensorModel::ossimSensorModel(geom_kwl): returning..."
126  << std::endl;
127  }
128 }
129 
130 //*****************************************************************************
131 // COPY CONSTRUCTOR: ossimSensorModel(ossimSensorModel)
132 //*****************************************************************************
134  :
137  theImageSize (model.theImageSize),
138  theSubImageOffset (model.theSubImageOffset),
139  theImageID (model.theImageID),
140  theSensorID (model.theSensorID),
141  theGSD (model.theGSD),
142  theMeanGSD (model.theMeanGSD),
143  theRefGndPt (model.theRefGndPt),
144  theRefImgPt (model.theRefImgPt),
145  theBoundGndPolygon (model.theBoundGndPolygon),
146  theImageClipRect (model.theImageClipRect),
147  theRelPosError (model.theRelPosError),
148  theNominalPosError (model.theNominalPosError),
149  theParWRTx (model.theParWRTx),
150  theParWRTy (model.theParWRTy),
151  theParWRTz (model.theParWRTz),
152  theObs (model.theObs),
153  theResid (model.theResid),
154  theExtrapolateImageFlag(false),
155  theExtrapolateGroundFlag(false)
156 {
157  if (traceExec())
158  {
160  << "DEBUG ossimSensorModel::ossimSensorModel(model): entering..." << std::endl;
161  }
162 
164 
165  if (traceExec())
166  {
168  << "DEBUG ossimSensorModel::ossimSensorModel(model): returning..." << std::endl;
169  }
170 
171  return;
172 }
173 
174 //*****************************************************************************
175 // CONSTRUCTOR: ossimSensorModel(geom_kwl)
176 //
177 // Constructs from a geometry keywordlist.
178 //
179 //*****************************************************************************
181  :
184  theImageSize (0, 0),
185  theSubImageOffset (0.0, 0.0),
186  theImageID (),
187  theSensorID (),
188  theGSD (0.0, 0.0),
189  theMeanGSD (0.0),
190  theRefGndPt (0.0, 0.0, 0.0),
191  theRefImgPt (0.0, 0.0),
192  theBoundGndPolygon (),
193  theImageClipRect (),
194  theRelPosError (0.0),
195  theNominalPosError (0.0),
196  theParWRTx (0.0, 0.0),
197  theParWRTy (0.0, 0.0),
198  theParWRTz (0.0, 0.0),
199  theObs (0.0, 0.0),
200  theResid (0.0, 0.0),
201  theExtrapolateImageFlag(false),
202  theExtrapolateGroundFlag(false)
203 {
204  if (traceExec())
205  {
207  << "DEBUG ossimSensorModel::ossimSensorModel(geom_kwl): entering..." << std::endl;
208  }
209 
210 // ossimElevManager::instance()->loadState(geom_kwl);
211  loadState(geom_kwl);
212 
213  if (traceExec())
214  {
216  << "DEBUG ossimSensorModel::ossimSensorModel(geom_kwl): returning..." << std::endl;
217  }
218 }
219 
220 
222 {
223  if (this != &rhs)
224  {
225  // ossimProjection base has no data members.
230  theImageID = rhs.theImageID;
231  theSensorID = rhs.theSensorID;
232  theGSD = rhs.theGSD;
233  theMeanGSD = rhs.theMeanGSD;
234  theRefGndPt = rhs.theRefGndPt;
235  theRefImgPt = rhs.theRefImgPt;
240  theParWRTx = rhs.theParWRTx;
241  theParWRTy = rhs.theParWRTy;
242  theParWRTz = rhs.theParWRTz;
243  theObs = rhs.theObs;
244  theResid = rhs.theResid;
247  }
248  return *this;
249 }
250 
251 //*****************************************************************************
252 // DESTRUCTOR: ~ossimSensorModel
253 //
254 //*****************************************************************************
256 {
257  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::~ossimSensorModel: entering..." << std::endl;
258 
259  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::~ossimSensorModel: returning..." << std::endl;
260 }
261 
263 {
264  return this;
265 }
266 
268 {
269  return this;
270 }
271 
272 
273 //*****************************************************************************
274 // METHOD: ossimSensorModel::lineSampleToWorld(image_pt, &gpt)
275 //
276 // Intersects the ray associated with image_pt with the available elevation
277 // model. Returns ground point
278 //
279 //*****************************************************************************
281  ossimGpt& gpt) const
282 {
283  bool debug = false; // setable via interactive debugger
284  if (traceExec() || debug) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::lineSampleToWorld:entering..." << std::endl;
285 
286  if(image_point.hasNans())
287  {
288  gpt.makeNan();
289  return;
290  }
291  //***
292  // Extrapolate if image point is outside image:
293  //***
294  if (!insideImage(image_point)&&(!theExtrapolateImageFlag))
295  {
296  gpt = extrapolate(image_point);
297  return;
298  }
299 
300  //***
301  // Determine imaging ray and invoke elevation source object's services to
302  // intersect ray with terrain model:
303  //***
304  ossimEcefRay ray;
305  imagingRay(image_point, ray);
307 
308  if (traceDebug() || debug)
309  {
310  ossimNotify(ossimNotifyLevel_DEBUG) << "image_point = " << image_point << std::endl;
311  ossimNotify(ossimNotifyLevel_DEBUG) << "ray = " << ray << std::endl;
312  ossimNotify(ossimNotifyLevel_DEBUG) << "gpt = " << gpt << std::endl;
313  }
314 
315  if (traceExec() || debug) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::lineSampleToWorld: returning..." << std::endl;
316  return;
317 }
318 
319 //*****************************************************************************
320 // METHOD: ossimSensorModel::worldToLineSample()
321 //
322 // Performs forward projection of ground point to image space.
323 //
324 //*****************************************************************************
326  ossimDpt& ip) const
327 {
328  // static bool recursionFlag = false;
329 
330  static const double PIXEL_THRESHOLD = .1; // acceptable pixel error
331  static const int MAX_NUM_ITERATIONS = 20;
332 
333 
334  if(worldPoint.isLatNan() || worldPoint.isLonNan())
335  {
336  ip.makeNan();
337  return;
338  }
339 
340  //***
341  // First check if the world point is inside bounding rectangle:
342  //***
343  int iters = 0;
344  ossimDpt wdp (worldPoint);
345  // if ((!recursionFlag)&&!(theBoundGndPolygon.pointWithin(wdp)))
346 
349  {
350  if (!(theBoundGndPolygon.pointWithin(wdp)))
351  {
352  if(theSeedFunction.valid())
353  {
354  theSeedFunction->worldToLineSample(worldPoint, ip);
355  }
356  else if(!theExtrapolateGroundFlag) // if I am not already in the extrapolation routine
357 
358  {
359  // recursionFlag = true;
360  ip = extrapolate(worldPoint);
361  // recursionFlag = false;
362  }
363  return;
364  }
365  }
366 
367  //***
368  // Substitute zero for null elevation if present:
369  //***
370  double height = worldPoint.hgt;
371  if ( ossim::isnan(height) )
372  {
373  height = 0.0;
374  }
375 
376  //
377  // Utilize iterative scheme for arriving at image point. Begin with guess
378  // at image center:
379  //
380  if(theSeedFunction.valid())
381  {
382  theSeedFunction->worldToLineSample(worldPoint, ip);
383  }
384  else
385  {
386  ip.u = theRefImgPt.u;
387  ip.v = theRefImgPt.v;
388  }
389 
390  ossimDpt ip_du;
391  ossimDpt ip_dv;
392 
393  ossimGpt gp, gp_du, gp_dv;
394  double dlat_du, dlat_dv, dlon_du, dlon_dv;
395  double delta_lat, delta_lon, delta_u, delta_v;
396  double inverse_norm;
397  bool done = false;
398  //***
399  // Begin iterations:
400  //***
401  do
402  {
403  //***
404  // establish perturbed image points about the guessed point:
405  //***
406  ip_du.u = ip.u + 1.0;
407  ip_du.v = ip.v;
408  ip_dv.u = ip.u;
409  ip_dv.v = ip.v + 1.0;
410 
411  //***
412  // Compute numerical partials at current guessed point:
413  //***
414  lineSampleHeightToWorld(ip, height, gp);
415  lineSampleHeightToWorld(ip_du, height, gp_du);
416  lineSampleHeightToWorld(ip_dv, height, gp_dv);
417 
418  if(gp.isLatNan() || gp.isLonNan())
419  {
420  gp = extrapolate(ip);
421  }
422  if(gp_du.isLatNan() || gp_du.isLonNan())
423  {
424  gp_du = extrapolate(ip_du);
425  }
426  if(gp_dv.isLatNan() | gp_dv.isLonNan())
427  {
428  gp_dv = extrapolate(ip_dv);
429 
430  }
431  dlat_du = gp_du.lat - gp.lat; //e
432  dlon_du = gp_du.lon - gp.lon; //g
433  dlat_dv = gp_dv.lat - gp.lat; //f
434  dlon_dv = gp_dv.lon - gp.lon; //h
435 
436  //
437  // Test for convergence:
438  //
439  delta_lat = worldPoint.lat - gp.lat;
440  delta_lon = worldPoint.lon - gp.lon;
441 
442 
443  //
444  // Compute linearized estimate of image point given gp delta:
445  //
446  inverse_norm = dlat_dv*dlon_du - dlat_du*dlon_dv; // fg-eh
447 
448  if (!ossim::almostEqual(inverse_norm, 0.0, DBL_EPSILON))
449  {
450  delta_u = (-dlon_dv*delta_lat + dlat_dv*delta_lon)/inverse_norm;
451  delta_v = ( dlon_du*delta_lat - dlat_du*delta_lon)/inverse_norm;
452  ip.u += delta_u;
453  ip.v += delta_v;
454  }
455  else
456  {
457  delta_u = 0;
458  delta_v = 0;
459  }
460  done = ((fabs(delta_u) < PIXEL_THRESHOLD)&&
461  (fabs(delta_v) < PIXEL_THRESHOLD));
462  iters++;
463  } while ((!done) && (iters < MAX_NUM_ITERATIONS));
464 
465 // } while (((fabs(delta_u) > PIXEL_THRESHOLD) ||
466 // (fabs(delta_v) > PIXEL_THRESHOLD)) &&
467 // (iters < MAX_NUM_ITERATIONS));
468 
469 #if 0 /* Please leave for debug. */
470  //---
471  // Note that this error mesage appears only if max count was reached while
472  // iterating. A linear (no iteration) solution would finish with iters =
473  // MAX_NUM_ITERATIONS + 1:
474  //---
475  if (iters >= MAX_NUM_ITERATIONS)
476  {
477  std::cout << "MAX ITERATION!!!" << std::endl;
478  std::cout << "delta_u = " << delta_u
479  << "\ndelta_v = " << delta_v << "\n";
480  }
481  else
482  {
483  std::cout << "ITERS === " << iters << std::endl;
484  }
485  std::cout << "iters = " << iters << "\n";
486 #endif
487 
488  //***
489  // The image point computed this way corresponds to full image space.
490  // Apply image offset in the case this is a sub-image rectangle:
491  //***
492  ip -= theSubImageOffset;
493 }
494 
495 //*****************************************************************************
496 // METHOD: ossimSensorModel::print(ostream)
497 //
498 // Dumps contents of object to ostream.
499 //
500 //*****************************************************************************
502 {
503  out << setprecision(15) << setiosflags(ios::fixed)
504  << "\n ossimSensorModel base-class data members:\n"
505  << "\n theImageID: " << theImageID
506  << "\n theSensorID: " << theSensorID
507  << "\n theImageSize: " << theImageSize
508  << "\n theSubImageOffset: " << theSubImageOffset
509  << "\n theGSD: " << theGSD
510  << "\n theMeanGSD: " << theMeanGSD
511  << "\n theRefGndPt: " << theRefGndPt
512  << "\n theRefImgPt: " << theRefImgPt
513  << "\n theBoundGndPolygon: \n" << theBoundGndPolygon
514  << "\n theImageClipRect: " << theImageClipRect
515  << "\n theNominalPosError: " << theNominalPosError
516  << "\n theRelPosError: " << theRelPosError
517  << endl;
518 
519  //---
520  // Note:
521  // need ossimAdjustableParameterInterface::print(os);
522  //---
523  return ossimProjection::print(out);
524 }
525 
527 {
528  theRefImgPt = pt;
529 }
530 
532 {
533  theRefGndPt = pt;
534 }
535 
537 {
538  theImageClipRect = imageRect;
539  theRefImgPt = imageRect.midPoint();
540  theImageSize = imageRect.size();
541 }
543  const ossimGpt& ur,
544  const ossimGpt& lr,
545  const ossimGpt& ll)
546 {
552 }
553 
554 
555 //*****************************************************************************
556 // METHOD: ossimSensorModel::saveState()
557 //
558 // Saves the state of this object to KWL.
559 //
560 //*****************************************************************************
562  const char* prefix) const
563 {
564  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::saveState: entering..." << std::endl;
565 
566  kwl.add(prefix, IMAGE_ID_KW, theImageID.chars());
567  kwl.add(prefix, SENSOR_ID_KW, theSensorID.chars());
568 
569  kwl.add(prefix,
572  true);
573  kwl.add(prefix,
576  true);
577 
578  kwl.add(prefix,
579  REF_GPT_LAT_KW,
581  true);
582 
583  kwl.add(prefix,
584  REF_GPT_LON_KW,
586  true);
587 
588  kwl.add(prefix,
589  REF_GPT_HGT_KW,
591  true);
592 
593  kwl.add(prefix,
594  REF_IPT_LINE_KW,
596  true);
597 
598  kwl.add(prefix,
599  REF_IPT_SAMP_KW,
601  true);
602 
603  kwl.add(prefix,
605  theGSD.line,
606  true);
607 
608  kwl.add(prefix,
610  theGSD.samp,
611  true);
612 
613  ossimDpt corner;
614  if(!theBoundGndPolygon.vertex(0, corner))
615  {
616  corner = ossimDpt(0,0);
617  }
618 
619  kwl.add(prefix,
621  corner.lat,
622  true);
623 
624  kwl.add(prefix,
626  corner.lon,
627  true);
628 
629  if(!theBoundGndPolygon.nextVertex(corner))
630  {
631  corner = ossimDpt(0,0);
632  }
633  kwl.add(prefix,
635  corner.lat,
636  true);
637 
638  kwl.add(prefix,
640  corner.lon,
641  true);
642 
643  if(!theBoundGndPolygon.nextVertex(corner))
644  {
645  corner = ossimDpt(0,0);
646  }
647  kwl.add(prefix,
649  corner.lat,
650  true);
651  kwl.add(prefix,
653  corner.lon,
654  true);
655 
656 
657  if(!theBoundGndPolygon.nextVertex(corner))
658  {
659  corner = ossimDpt(0,0);
660  }
661  kwl.add(prefix,
663  corner.lat,
664  true);
665  kwl.add(prefix,
667  corner.lon,
668  true);
669 
670  kwl.add(prefix,
673  true, 20);
674 
675  kwl.add(prefix,
678  true, 20);
679 
680  kwl.add(prefix,
681  "rect",
683  + " " + ossimString::toString(theImageClipRect.ul().y) + " " +
686  true);
687 
688  if ( theSeedFunction.valid() )
689  {
690  ossimString seedPrefix = prefix;
691  seedPrefix += "seed_projection.";
692  theSeedFunction->saveState(kwl, seedPrefix.c_str());
693  }
694 
695  // Avoid passing null char* to method that takes an ossimString.
696  ossimString tmpStr;
697  if (prefix)
698  {
699  tmpStr = prefix;
700  }
701  saveAdjustments(kwl, tmpStr);
702 
703  //
704  // Also save the state of the elevation object:
705  //
706 // ossimElevManager::instance()->saveState(kwl, prefix);
707 
708 
709  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::saveState:returning..." << std::endl;
710  return ossimProjection::saveState(kwl, prefix);;
711 }
712 
713 //*****************************************************************************
714 // METHOD: ossimSensorModel::loadState()
715 //
716 // Loads the state of this object from KWL.
717 //
718 //*****************************************************************************
720  const char* prefix)
721 {
722  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::loadState: entering..." << std::endl;
723 
724  const char* keyword;
725  const char* value;
726 // int nconv;
727  ossimDpt v[4]; // temporarily holds vertices for ground polygon
728 
729  //***
730  // Read each keyword, breaking out if error occurs:
731  //***
732  keyword = IMAGE_ID_KW;
733  value = kwl.find(prefix, keyword);
734  if (value)
735  theImageID = value;
736  else
737  theImageID = NULL_STRING;
738 
739  keyword = SENSOR_ID_KW;
740  value = kwl.find(prefix, keyword);
741  if (value)
742  theSensorID = value;
743  else
744  theSensorID = NULL_STRING;
745 
746  keyword = ossimKeywordNames::NUMBER_LINES_KW;//IMAGE_SIZE_LINES_KW;
747  value = kwl.find(prefix, keyword);
748  if (value)
749  {
750  theImageSize.line = ossimString(value).toLong();
751  }
752 
753  keyword = ossimKeywordNames::NUMBER_SAMPLES_KW;// IMAGE_SIZE_SAMPS_KW;
754  value = kwl.find(prefix, keyword);
755  if (value)
756  {
757  theImageSize.samp = ossimString(value).toLong();
758  }
759 
760  keyword = REF_IPT_LINE_KW;
761  value = kwl.find(prefix, keyword);
762  if (value)
763  {
765  }
766 
767  keyword = REF_IPT_SAMP_KW;
768  value = kwl.find(prefix, keyword);
769  if (value)
770  {
772  }
773 
774  keyword = REF_GPT_LAT_KW;
775  value = kwl.find(prefix, keyword);
776  if(value)
777  {
778  theRefGndPt.latd(ossimString(value).toDouble());
779  }
780 
781 
782  keyword = REF_GPT_LON_KW;
783  value = kwl.find(prefix, keyword);
784  if(value)
785  {
786  theRefGndPt.lond(ossimString(value).toDouble());
787  }
788 
789 
790  keyword = REF_GPT_HGT_KW;
791  value = kwl.find(prefix, keyword);
792  if(value)
793  {
794  theRefGndPt.hgt = ossimString(value).toDouble();
795  }
796 
797 
798  keyword = ossimKeywordNames::METERS_PER_PIXEL_Y_KW;// GSD_LINE_DIR_KW;
799  value = kwl.find(prefix, keyword);
800  if (value)
801  {
802  theGSD.line = ossimString(value).toDouble();
803  }
804 
805  keyword = ossimKeywordNames::METERS_PER_PIXEL_X_KW;//GSD_SAMP_DIR_KW;
806  value = kwl.find(prefix, keyword);
807  if (value)
808  {
809  theGSD.samp = ossimString(value).toDouble();
810  }
811 
812  theMeanGSD = (fabs(theGSD.line) + fabs(theGSD.samp))/2.0;
813 
815  value = kwl.find(prefix, keyword);
816  if (value)
817  {
818  v[0].lat = ossimString(value).toDouble();
819  }
820 
822  value = kwl.find(prefix, keyword);
823  if (value)
824  {
825  v[0].lon = ossimString(value).toDouble();
826  }
827 
829  value = kwl.find(prefix, keyword);
830  if (value)
831  {
832  v[1].lat = ossimString(value).toDouble();
833  }
834 
836  value = kwl.find(prefix, keyword);
837  if (value)
838  {
839  v[1].lon = ossimString(value).toDouble();
840  }
841 
843  value = kwl.find(prefix, keyword);
844  if (value)
845  {
846  v[2].lat = ossimString(value).toDouble();
847  }
848 
850  value = kwl.find(prefix, keyword);
851  if (value)
852  {
853  v[2].lon = ossimString(value).toDouble();
854  }
855 
857  value = kwl.find(prefix, keyword);
858  if (value)
859  {
860  v[3].lat = ossimString(value).toDouble();
861  }
862 
864  value = kwl.find(prefix, keyword);
865  if (value)
866  {
867  v[3].lon = ossimString(value).toDouble();
868  }
869 
871  value = kwl.find(prefix, keyword);
872  if (!value)
873  {
874  // Try old keyword for legacy purposes:
876  value = kwl.find(prefix, keyword);
877  }
878  if (value)
879  theNominalPosError = atof(value);
880  else
881  theNominalPosError = 0.0;
882 
884  value = kwl.find(prefix, keyword);
885  if (value)
886  theRelPosError = atof(value);
887  else
889 
890  //***
891  // Initialize other data members given quantities read in KWL:
892  //***
894 
895  const char* rect = kwl.find(prefix, "rect");
896  if(rect)
897  {
898  std::vector<ossimString> splitArray;
899  ossimString rectString(rect);
900  rectString = rectString.trim();
901  rectString.split(splitArray, " ");
902  if(splitArray.size() == 4)
903  {
904  theImageClipRect = ossimDrect(splitArray[0].toDouble(),
905  splitArray[1].toDouble(),
906  splitArray[2].toDouble(),
907  splitArray[3].toDouble());
908  }
909  else
910  {
911  theImageClipRect = ossimDrect(0.0, 0.0,
913  }
914  }
915  else
916  {
917  theImageClipRect = ossimDrect(0.0, 0.0,
919  }
920 
921  // theSeedFunction: This is in the base class ossimSensorModel but is not
922  // in the ossimSensorModel::loadState so do it here.
923  ossimString seedPrefix = prefix;
924  seedPrefix += ".seed_projection.";
925  value = kwl.find( seedPrefix.chars(), ossimKeywordNames::TYPE_KW );
926  if ( value )
927  {
928  // Only do expensive factory call if key is found...
930  createProjection(kwl, seedPrefix.chars());
931  }
932 
933  // Avoid passing null char* to method that takes an ossimString.
934  ossimString tmpStr;
935  if (prefix)
936  {
937  tmpStr = prefix;
938  }
939  loadAdjustments(kwl, tmpStr);
940 
941  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::loadState: returning..." << std::endl;
942  return ossimProjection::loadState(kwl, prefix);;
943 }
944 
945 //*****************************************************************************
946 // PROTECTED METHOD: Model_type::extrapolate(image_point)
947 //
948 // This method computes extrapolated values of latitude and longitude for
949 // points which are outside the actual image boundaries.
950 //*****************************************************************************
952  const double& height) const
953 {
955  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::extrapolate: entering... " << std::endl;
956 
957  //---
958  // If image point supplied has NaN components, return now with a NaN point.
959  // This prevents an infinite recursion between model worldToLineSample
960  // and this method:
961  //---
962  if (imagePoint.hasNans())
963  {
964  theExtrapolateImageFlag = false;
965  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::extrapolate: returning..." << std::endl;
966  return ossimGpt(ossim::nan(), ossim::nan(), ossim::nan());
967  }
968 
969  if(theSeedFunction.valid())
970  {
971  ossimGpt wpt;
972 
973  theSeedFunction->lineSampleToWorld(imagePoint, wpt);
974  theExtrapolateImageFlag = false;
975  return wpt;
976  }
977  //***
978  // Determine which edge is intersected by the radial, and establish
979  // intersection:
980  //***
981  ossimGpt gpt;
982  ossimDpt edgePt (imagePoint);
983  ossimDpt image_center (theRefImgPt);
984  theImageClipRect.clip(image_center, edgePt);
985 
986  //***
987  // Need edgePt relative to image center. Compute an epsilon perturbation in
988  // the direction of edgePt for later computing directional derivative,
989  // and back out the offset to origin:
990  //***
991  ossimDpt deltaPt (edgePt - image_center);
992  ossimDpt epsilon (deltaPt/deltaPt.length());
993  edgePt -= epsilon; // insure that we are inside the image
994  ossimDpt edgePt_prime (edgePt - epsilon); // epsilon=1pixel
995 
996  //***
997  // Establish ground point corresponding to edge point and edgePt+epsilon:
998  //***
999  ossimGpt edgeGP;
1000  ossimGpt edgeGP_prime;
1001 
1002  if (ossim::isnan(height))
1003  {
1004  lineSampleToWorld(edgePt, edgeGP);
1005  lineSampleToWorld(edgePt_prime, edgeGP_prime);
1006  }
1007  else
1008  {
1009  lineSampleHeightToWorld(edgePt, height, edgeGP);
1010  lineSampleHeightToWorld(edgePt_prime, height, edgeGP_prime);
1011  }
1012 
1013  //***
1014  // Compute approximate directional derivatives of lat and lon along radial
1015  // at the edge:
1016  //***
1017  double dpixel = (edgePt-edgePt_prime).length();
1018  double dlat_drad = (edgeGP.lat - edgeGP_prime.lat)/dpixel;
1019  double dlon_drad = (edgeGP.lon - edgeGP_prime.lon)/dpixel;
1020 
1021  //***
1022  // Now extrapolate to image point of interest:
1023  //***
1024  double delta_pixel = (imagePoint - edgePt).length();
1025 
1026  gpt.lat = edgeGP.lat + dlat_drad*delta_pixel;
1027  gpt.lon = edgeGP.lon + dlon_drad*delta_pixel;
1028  if ( ossim::isnan(height) )
1029  {
1031  }
1032  else
1033  {
1034  gpt.hgt = height;
1035  }
1036  theExtrapolateImageFlag = false;
1037  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::extrapolate: returning..." << std::endl;
1038  return gpt;
1039 }
1040 
1041 
1042 //*****************************************************************************
1043 // PROTECTED METHOD: Model_type::extrapolate(ground_point)
1044 //
1045 // This method computes extrapolated values of line and sample for
1046 // points which are outside the actual image boundaries.
1047 //*****************************************************************************
1049 {
1050  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::extrapolate: entering... " << std::endl;
1051  theExtrapolateGroundFlag = true;
1052  double height = 0.0;
1053  //---
1054  // If ground point supplied has NaN components, return now with a NaN point.
1055  //---
1056  if ( (ossim::isnan(gpt.lat)) || (ossim::isnan(gpt.lon)) )
1057 // (gpt.hgt==OSSIM_DBL_NAN))
1058  {
1059  theExtrapolateGroundFlag = false;
1060  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::extrapolate: returning..." << std::endl;
1061  return ossimDpt(ossim::nan(), ossim::nan());
1062  }
1063  if(ossim::isnan(gpt.hgt) == false)
1064  {
1065  height = gpt.hgt;
1066  }
1067 
1068  if(theSeedFunction.valid())
1069  {
1070  ossimDpt ipt;
1071 
1073 
1074  theExtrapolateGroundFlag = false;
1075  return ipt;
1076  }
1077  //***
1078  // Determine which edge is intersected by the radial, and establish
1079  // intersection:
1080  //***
1081  ossimDpt edgePt (gpt);
1082  ossimDpt image_center (theRefGndPt);
1083  theBoundGndPolygon.clipLineSegment(image_center, edgePt);
1084 
1085  //---
1086  // Compute an epsilon perturbation in the direction away from edgePt for
1087  // later computing directional derivative:
1088  //---
1089  const double DEG_PER_MTR = 8.983152841e-06; // Equator WGS-84...
1090  double epsilon = theMeanGSD*DEG_PER_MTR; //degrees (latitude) per pixel
1091  ossimDpt deltaPt (edgePt-image_center);
1092  ossimDpt epsilonPt (deltaPt*epsilon/deltaPt.length());
1093  edgePt -= epsilonPt;
1094  ossimDpt edgePt_prime (edgePt - epsilonPt);
1095 
1096  //***
1097  // Establish image point corresponding to edge point and edgePt+epsilon:
1098  //***
1099  ossimGpt edgeGP (edgePt.lat, edgePt.lon, height);//gpt.hgt);
1100  ossimGpt edgeGP_prime (edgePt_prime.lat, edgePt_prime.lon, height);//gpt.hgt);
1101 
1102  worldToLineSample(edgeGP, edgePt);
1103  worldToLineSample(edgeGP_prime, edgePt_prime);
1104 
1105  //***
1106  // Compute approximate directional derivatives of line and sample along
1107  // radial at the edge:
1108  //***
1109  double dsamp_drad = (edgePt.samp - edgePt_prime.samp)/epsilon;
1110  double dline_drad = (edgePt.line - edgePt_prime.line)/epsilon;
1111 
1112  //***
1113  // Now extrapolate to point of interest:
1114  //***
1115  double delta = (ossimDpt(gpt) - ossimDpt(edgeGP)).length();
1116 
1117 
1118  ossimDpt extrapolated_ip (edgePt.samp + delta*dsamp_drad,
1119  edgePt.line + delta*dline_drad);
1120 
1121  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::extrapolate: returning..." << std::endl;
1122 
1123  theExtrapolateGroundFlag = false;
1124  return extrapolated_ip;
1125 }
1126 
1127 
1128 //*****************************************************************************
1129 // METHOD: ossimSensorModel::imagingRay()
1130 //
1131 // Default implementation for computing imaging ray from image point.
1132 //
1133 //*****************************************************************************
1134 void ossimSensorModel::imagingRay(const ossimDpt& image_point,
1135  ossimEcefRay& image_ray) const
1136 {
1137  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::imagingRay: entering..." << std::endl;
1138 
1139  ossimGpt start;
1140  ossimGpt end;
1141 
1142  lineSampleHeightToWorld(image_point, RAY_ORIGIN_HEIGHT, start);
1143  lineSampleHeightToWorld(image_point, 0.0, end);
1144 
1145  image_ray = ossimEcefRay(start, end);
1146 
1147  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::imagingRay: returning..." << std::endl;
1148  return;
1149 }
1150 
1151 //*****************************************************************************
1152 // METHOD: ossimSensorModel::getObsCovMat()
1153 //
1154 // Default implementation for forming observation covariance matrix.
1155 //
1156 // Note: At this base class level, the only error source currently
1157 // considered is mensuration error. This is obviously optimistic,
1158 // but is included as a placeholder/example, and is presently
1159 // the trivial case.
1160 //
1161 //*****************************************************************************
1163  const ossimDpt& /* ipos */ ,
1164  NEWMAT::SymmetricMatrix& Cov,
1165  const ossim_float64 defPointingSigma) const
1166 {
1167  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1168  // Mensuration error contribution
1169  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1170  // Sensitivity matrix
1171  NEWMAT::SymmetricMatrix B(2);
1172  B = 0.0;
1173  B(1,1) = 1.0;
1174  B(2,2) = B(1,1);
1175 
1176  // Pointing covariance matrix
1177  NEWMAT::SymmetricMatrix P(2);
1178  P = 0.0;
1179  P(1,1) = defPointingSigma*defPointingSigma;
1180  P(2,2) = P(1,1);
1181 
1182  // Propagate
1183  NEWMAT::SymmetricMatrix Cm;
1184  Cm << B * P * B.t();
1185 
1186  // ~~~~~~~~~~~~~~~~~~~~
1187  // Sum total covariance
1188  // ~~~~~~~~~~~~~~~~~~~~
1189  NEWMAT::SymmetricMatrix Ctot = Cm; //+ other contributors as identified
1190 
1191  // ~~~~~~~~~~~~~~~~~~
1192  // Propagate to image
1193  // ~~~~~~~~~~~~~~~~~~
1194  NEWMAT::SymmetricMatrix Bi(2);
1195  Bi = 0.0;
1196  Bi(1,1) = 1.0;
1197  Bi(2,2) = Bi(1,1);
1198 
1199  Cov << Bi * Ctot * Bi.t();
1200 
1202 }
1203 
1205 {
1206  static const char MODULE[] = "ossimSensorModel::computeGsd";
1207 
1208  if (theImageSize.hasNans())
1209  {
1210  std::string e = MODULE;
1211  e += "Error image size has nans!";
1212  throw ossimException(e);
1213  }
1214 
1215  //---
1216  // Compute gsd in the x direction from left to right points across the middle of image.
1217  // Compute gsd in the y direction from top to bottom points across the middle of image.
1218  //---
1219 
1220  // ossim_float64 midLine = 0.0;
1221  // ossim_float64 midSamp = 0.0;
1222  // ossim_float64 endLine = 1.0;
1223  // ossim_float64 endSamp = 1.0;
1224  // if (theImageSize.x > 2)
1225  // {
1226  // midSamp = (theImageSize.x-1)/2.0;
1227  // endSamp = theImageSize.x-1;
1228 
1229  // }
1230  // if (theImageSize.y > 2)
1231  // {
1232  // midLine = (theImageSize.y-1)/2.0;
1233  // endLine = theImageSize.y-1;
1234  // }
1235 
1236  ossimDpt centerImagePoint = theRefImgPt;
1237  ossimDpt quarterSize;
1238  if(!theImageSize.hasNans())
1239  {
1240  quarterSize = ossimDpt(theImageSize.x/4.0, theImageSize.y/4.0);
1241  }
1242  else if(!theImageClipRect.hasNans())
1243  {
1246 
1247  quarterSize = ossimDpt(w/4.0, h/4.0);
1248  }
1249  else
1250  {
1251  quarterSize = ossimDpt(1.0,1.0);
1252  }
1253  if(centerImagePoint.hasNans()&&(!theImageSize.hasNans()))
1254  {
1255  centerImagePoint.x = (theImageSize.x)/2.0;
1256  centerImagePoint.y = (theImageSize.y-1.0)/2.0;
1257  }
1258  else if(centerImagePoint.hasNans()&&!theImageClipRect.hasNans())
1259  {
1260  centerImagePoint = theImageClipRect.midPoint();
1261  }
1262 
1263  if(!centerImagePoint.hasNans())
1264  {
1265  ossimDpt leftDpt(centerImagePoint.x-quarterSize.x, centerImagePoint.y);// (0.0, midLine);
1266  ossimDpt rightDpt(centerImagePoint.x+quarterSize.y, centerImagePoint.y);// (endSamp, midLine);
1267  ossimDpt topDpt(centerImagePoint.x, centerImagePoint.y-quarterSize.y);// (midSamp, 0.0);
1268  ossimDpt bottomDpt(centerImagePoint.x, centerImagePoint.y+quarterSize.y);//(midSamp, endLine);
1269 
1270  ossimGpt leftGpt;
1271  ossimGpt rightGpt;
1272  ossimGpt topGpt;
1273  ossimGpt bottomGpt;
1274 
1275  //---
1276  // Left point.
1277  // For the first point use lineSampleToWorld to get the height.
1278  //---
1279  lineSampleToWorld(leftDpt, leftGpt);
1280  if (leftGpt.hasNans())
1281  {
1282  std::string e = MODULE;
1283  e += "Error leftGpt has nans!";
1284  throw ossimException(e);
1285  }
1286 
1287  //---
1288  // Right point:
1289  // Use lineSampleHeightToWorld using the left height since we want the horizontal distance.
1290  //---
1291  lineSampleHeightToWorld(rightDpt, leftGpt.hgt, rightGpt);
1292  if (rightGpt.hasNans())
1293  {
1294  std::string e = MODULE;
1295  e += "Error rightGpt has nans!";
1296  throw ossimException(e);
1297  }
1298 
1299  //---
1300  // Top point:
1301  // Use lineSampleHeightToWorld using the left height since we want the horizontal distance.
1302  //---
1303  lineSampleHeightToWorld(topDpt, leftGpt.hgt, topGpt);
1304  if (topGpt.hasNans())
1305  {
1306  std::string e = MODULE;
1307  e += "Error topGpt has nans!";
1308  throw ossimException(e);
1309  }
1310 
1311  //---
1312  // Bottom point:
1313  // Use lineSampleHeightToWorld using the left height since we want the horizontal distance.
1314  //---
1315  lineSampleHeightToWorld(bottomDpt, leftGpt.hgt, bottomGpt);
1316  if (bottomGpt.hasNans())
1317  {
1318  std::string e = MODULE;
1319  e += "Error bottomGpt has nans!";
1320  throw ossimException(e);
1321  }
1322 
1323 #if 0 /* Please leave for debug. (drb) */
1325  << "image size: " << theImageSize
1326  << "\nleftDpt: " << leftDpt
1327  << "\nrightDpt: " << rightDpt
1328  << "\ntopDpt: " << topDpt
1329  << "\nbottomDpt: " << bottomDpt
1330  << "\nleftGpt: " << leftGpt
1331  << "\nrightGpt: " << rightGpt
1332  << "\ntopGpt: " << topGpt
1333  << "\nbottomGpt: " << bottomGpt
1334  << "\n";
1335 #endif
1336 
1337  theGSD.x = leftGpt.distanceTo(rightGpt)/(rightDpt.x-leftDpt.x);
1338  theGSD.y = topGpt.distanceTo(bottomGpt)/(bottomDpt.y-topDpt.y);
1339  theMeanGSD = (theGSD.x + theGSD.y)/2.0;
1340 
1341  }
1342  else
1343  {
1344  std::string e = MODULE;
1345  e += "Error center has nans!";
1346  throw ossimException(e);
1347  }
1348 
1349  if (traceDebug())
1350  {
1352  << "ossimSensorModel::computGsd DEBUG:"
1353  << "\ntheGSD: " << theGSD
1354  << "\ntheMeanGSD: " << theMeanGSD << std::endl;
1355  }
1356 }
1357 
1358 //*****************************************************************************
1359 // STATIC METHOD: ossimSensorModel::writeGeomTemplate
1360 //
1361 // Outputs a sample geometry KWL to stream provided.
1362 //
1363 //*****************************************************************************
1365 {
1366  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::writeGeomTemplate:entering..." << std::endl;
1367 
1368  os << "//***\n"
1369  << "// Base-class ossimSensorModel Keywords:\n"
1370  << "//***\n"
1371  << ossimKeywordNames::ID_KW << ": <string>\n"
1372  << SENSOR_ID_KW << ": <string>\n"
1373  << ossimKeywordNames::NUMBER_LINES_KW << ": <int>\n"
1374  << ossimKeywordNames::NUMBER_SAMPLES_KW << ": <int>\n"
1375  << REF_GPT_LAT_KW << ": <decimal degrees>\n"
1376  << REF_GPT_LON_KW << ": <decimal degrees>\n"
1377  << REF_GPT_HGT_KW << ": <float meters>\n"
1378  << REF_IPT_LINE_KW << ": <float>\n"
1379  << REF_IPT_SAMP_KW << ": <float>\n"
1380  << ossimKeywordNames::METERS_PER_PIXEL_Y_KW << ": <float meters>\n"
1381  << ossimKeywordNames::METERS_PER_PIXEL_X_KW << ": <float meters>\n"
1382  << ossimKeywordNames::UL_LAT_KW << ": <decimal degrees>\n"
1383  << ossimKeywordNames::UL_LON_KW << ": <decimal degrees>\n"
1384  << ossimKeywordNames::UR_LAT_KW << ": <decimal degrees>\n"
1385  << ossimKeywordNames::UR_LON_KW << ": <decimal degrees>\n"
1386  << ossimKeywordNames::LR_LAT_KW << ": <decimal degrees>\n"
1387  << ossimKeywordNames::LR_LON_KW << ": <decimal degrees>\n"
1388  << ossimKeywordNames::LL_LAT_KW << ": <decimal degrees>\n"
1389  << ossimKeywordNames::LL_LON_KW << ": <decimal degrees>\n"
1390  << "\n"
1391  << "//***\n"
1392  << "// Repeat following four entries for each adjustable parameter:\n"
1393  << "//***\n"
1394 // << PARAM_PREFIX << "N." << PARAM_NAME_KW << ": <string>\n"
1395 // << PARAM_PREFIX << "N." << PARAM_UNITS_KW << ": <string>\n"
1396 // << PARAM_PREFIX << "N." << PARAM_VALUE_KW << ": <float>\n"
1397 // << PARAM_PREFIX << "N." << PARAM_SIGMA_KW << ": <float>\n"
1398  << std::endl;
1399 
1400  if (traceExec()) ossimNotify(ossimNotifyLevel_DEBUG) << "DEBUG ossimSensorModel::writeGeomTemplate: returning..." << std::endl;
1401  return;
1402 }
1403 
1404 ossim_uint32
1406 {
1407  ossim_uint32 dof = 0;
1408  ossim_uint32 idx = 0;
1410  for(idx = 0; idx < numAdj; ++idx)
1411  {
1412  if(!isParameterLocked(idx))
1413  {
1414  ++dof;
1415  }
1416  }
1417 
1418  return dof;
1419 }
1420 
1421 double
1422 ossimSensorModel::optimizeFit(const ossimTieGptSet& tieSet, double* /* targetVariance */)
1423 {
1424  //use a simple Levenberg-Marquardt non-linear optimization
1425  //note : please limit the number of tie points
1426  //
1427  //INPUTS: requires Jacobian matrix (partial derivatives with regards to parameters)
1428  //OUTPUTS: will also compute parameter covariance matrix
1429  //
1430  //TBD: use targetVariance!
1431 
1433  int nobs = tieSet.size();
1434 
1435  //setup initail values
1436  int iter=0;
1437  int iter_max = 200;
1438  double minResidue = 1e-10; //TBC
1439  double minDelta = 1e-10; //TBC
1440 
1441  //build Least Squares initial normal equation
1442  // don't waste memory, add samples one at a time
1443  NEWMAT::SymmetricMatrix A;
1444  NEWMAT::ColumnVector residue;
1445  NEWMAT::ColumnVector projResidue;
1446  double deltap_scale = 1e-4; //step_Scale is 1.0 because we expect parameters to be between -1 and 1
1447  buildNormalEquation(tieSet, A, residue, projResidue, deltap_scale);
1448  double ki2=residue.SumSquare();
1449 
1450  //get current adjustment (between -1 and 1 normally) and convert to ColumnVector
1451  ossimAdjustmentInfo cadj;
1452  getAdjustment(cadj);
1453  std::vector< ossimAdjustableParameterInfo >& parmlist = cadj.getParameterList();
1454  NEWMAT::ColumnVector cparm(np), nparm(np);
1455  for(int n=0;n<np;++n)
1456  {
1457  cparm(n+1) = parmlist[n].getParameter();
1458  }
1459 
1460  double damping_speed = 2.0;
1461  //find max diag element for A
1462  double maxdiag=0.0;
1463  for(int d=1;d<=np;++d) {
1464  if (maxdiag < A(d,d)) maxdiag=A(d,d);
1465  }
1466  double damping = 1e-3 * maxdiag;
1467  double olddamping = 0.0;
1468  bool found = false;
1469 
1470  //DEBUG TBR
1471  // cout<<"rms="<<sqrt(ki2/nobs)<<" ";
1472  // cout.flush();
1473 
1474  while ( (!found) && (iter < iter_max) ) //non linear optimization loop
1475  {
1476  bool decrease = false;
1477 
1478  do
1479  {
1480  //add damping update to normal matrix
1481  for(int d=1;d<=np;++d) A(d,d) += damping - olddamping;
1482  olddamping = damping;
1483 
1484  NEWMAT::ColumnVector deltap = solveLeastSquares(A, projResidue);
1485 
1486  if (deltap.NormFrobenius() <= minDelta)
1487  {
1488  found = true;
1489  } else {
1490  //update adjustment
1491  nparm = cparm + deltap;
1492  for(int n=0;n<np;++n)
1493  {
1494  setAdjustableParameter(n, nparm(n+1), false); //do not update now, wait
1495  }
1496  updateModel();
1497 
1498  //check residue is reduced
1499  NEWMAT::ColumnVector newresidue = getResidue(tieSet);
1500  double newki2=newresidue.SumSquare();
1501  double res_reduction = (ki2 - newki2) / (deltap.t()*(deltap*damping + projResidue)).AsScalar();
1502  //DEBUG TBR
1503  cout<<sqrt(newki2/nobs)<<" ";
1504  cout.flush();
1505 
1506  if (res_reduction > 0)
1507  {
1508  //accept new parms
1509  cparm = nparm;
1510  ki2=newki2;
1511 
1512  deltap_scale = max(1e-15, deltap.NormInfinity()*1e-4);
1513 
1514  buildNormalEquation(tieSet, A, residue, projResidue, deltap_scale);
1515  olddamping = 0.0;
1516 
1517  found = ( projResidue.NormInfinity() <= minResidue );
1518  //update damping factor
1519  damping *= std::max( 1.0/3.0, 1.0-std::pow((2.0*res_reduction-1.0),3));
1520  damping_speed = 2.0;
1521  decrease = true;
1522  } else {
1523  //cancel parameter update
1524  for(int n=0;n<np;++n)
1525  {
1526  setAdjustableParameter(n, nparm(n+1), false); //do not update right now
1527  }
1528  updateModel();
1529 
1530  damping *= damping_speed;
1531  damping_speed *= 2.0;
1532  }
1533  }
1534  } while (!decrease && !found);
1535  ++iter;
1536  }
1537 
1538 //DEBUG TBR
1539 cout<<endl;
1540 
1541  //compute parameter correlation
1542  // use normal matrix inverse
1543  //TBD
1544 
1545  return ki2/nobs;
1546 }
1547 
1548 void
1550  NEWMAT::SymmetricMatrix& A,
1551  NEWMAT::ColumnVector& residue,
1552  NEWMAT::ColumnVector& projResidue,
1553  double pstep_scale)
1554 {
1555  //goal: build Least Squares system
1556  //constraint: never store full Jacobian matrix in memory (can be huge)
1557  // so we build the matrices incrementally
1558  // the system can be built using forward() or inverse() depending on the projection capabilities : useForward()
1559  //
1560  //TBD : add covariance matrix for each tie point
1561 
1562  //init
1564  int dimObs;
1565  bool useImageObs = useForward(); //caching
1566  if (useImageObs)
1567  {
1568  dimObs = 2; //image observation
1569  } else {
1570  dimObs = 3; //ground observations
1571  }
1572  int no = dimObs * tieSet.size(); //number of observations
1573 
1574  A.ReSize(np);
1575  residue.ReSize(no);
1576  projResidue.ReSize(np);
1577  //Zeroify matrices that will be accumulated
1578  A = 0.0;
1579  projResidue = 0.0;
1580 
1581  const vector<ossimRefPtr<ossimTieGpt> >& theTPV = tieSet.getTiePoints();
1582  vector<ossimRefPtr<ossimTieGpt> >::const_iterator tit;
1583  unsigned long c=1;
1584 
1585  if (useImageObs)
1586  {
1587  //image observations
1588  std::vector<ossimDpt> imDerp(np);
1589  ossimDpt resIm;
1590  // loop on tie points
1591  for (tit = theTPV.begin() ; tit != theTPV.end() ; ++tit)
1592  {
1593  //compute residue
1594  resIm = (*tit)->tie - forward(*(*tit));
1595  residue(c++) = resIm.x;
1596  residue(c++) = resIm.y;
1597 
1598  //compute all image derivatives regarding parametres for the tie point position
1599  for(int p=0;p<np;++p)
1600  {
1601  imDerp[p] = getForwardDeriv( p , *(*tit) , pstep_scale);
1602  }
1603 
1604  //compute influence of tie point on all sytem elements
1605  for(int p1=0;p1<np;++p1)
1606  {
1607  //proj residue: J * residue
1608  projResidue.element(p1) += imDerp[p1].x * resIm.x + imDerp[p1].y * resIm.y;
1609 
1610  //normal matrix A = transpose(J)*J
1611  for(int p2=p1;p2<np;++p2)
1612  {
1613  A.element(p1,p2) += imDerp[p1].x * imDerp[p2].x + imDerp[p1].y * imDerp[p2].y;
1614  }
1615  }
1616  }
1617  }
1618  else
1619  {
1620  // ground observations
1621  std::vector<ossimGpt> gdDerp(np);
1622  ossimGpt gd, resGd;
1623  // loop on tie points
1624  for (tit = theTPV.begin() ; tit != theTPV.end() ; ++tit)
1625  {
1626  //compute residue
1627  gd = inverse((*tit)->tie);
1628  residue(c++) = resGd.lon = ((*tit)->lon - gd.lon) * 100000.0;
1629  residue(c++) = resGd.lat = ((*tit)->lat - gd.lat) * 100000.0 * cos(gd.lat / 180.0 * M_PI);
1630  residue(c++) = resGd.hgt = (*tit)->hgt - gd.hgt; //TBD : normalize to meters?
1631 
1632  //compute all image derivatives regarding parametres for the tie point position
1633  for(int p=0;p<np;++p)
1634  {
1635  gdDerp[p] = getInverseDeriv( p , (*tit)->tie, pstep_scale);
1636  }
1637 
1638  //compute influence of tie point on all sytem elements
1639  for(int p1=0;p1<np;++p1)
1640  {
1641  //proj residue: J * residue
1642  projResidue.element(p1) += gdDerp[p1].lon * resGd.lon + gdDerp[p1].lat * resGd.lat + gdDerp[p1].hgt * resGd.hgt; //TBC
1643 
1644  //normal matrix A = transpose(J)*J
1645  for(int p2=p1;p2<np;++p2)
1646  {
1647  A.element(p1,p2) += gdDerp[p1].lon * gdDerp[p2].lon + gdDerp[p1].lat * gdDerp[p2].lat + gdDerp[p1].hgt * gdDerp[p2].hgt;
1648  }
1649  }
1650  }
1651 
1652  } //end of if (useImageObs)
1653 }
1654 
1655 //give inverse() partial derivative regarding parameter parmIdx (>=0)
1656 ossimGpt
1657 ossimSensorModel::getInverseDeriv(int parmIdx, const ossimDpt& ipos, double hdelta)
1658 {
1659  double den = 0.5/hdelta;
1660  ossimGpt res,gd;
1661 
1662  double middle = getAdjustableParameter(parmIdx);
1663  //set parm to high value
1664  setAdjustableParameter(parmIdx, middle + hdelta, true);
1665  res = inverse(ipos);
1666  //set parm to low value and gte difference
1667  setAdjustableParameter(parmIdx, middle - hdelta, true);
1668  gd = inverse(ipos);
1669 
1670  //reset parm
1671  setAdjustableParameter(parmIdx, middle, true);
1672 
1673  res.lon = den*(res.lon - gd.lon) * 100000.0; //TBC : approx meters
1674  res.lat = den*(res.lat - gd.lat) * 100000.0 * cos(gd.lat / 180.0 * M_PI);
1675  res.hgt = den*(res.hgt - gd.hgt);
1676 
1677  return res;
1678 }
1679 
1680 //give forward() partial derivative regarding parameter parmIdx (>=0)
1681 ossimDpt
1682 ossimSensorModel::getForwardDeriv(int parmIdx, const ossimGpt& gpos, double hdelta)
1683 {
1684  static double den = 0.5/hdelta;
1685  ossimDpt res;
1686 
1687  double middle = getAdjustableParameter(parmIdx);
1688  //set parm to high value
1689  setAdjustableParameter(parmIdx, middle + hdelta, true);
1690  res = forward(gpos);
1691  //set parm to low value and gte difference
1692  setAdjustableParameter(parmIdx, middle - hdelta, true);
1693  res -= forward(gpos);
1694  //get partial derivative
1695  res = res*den;
1696 
1697  //reset parm
1698  setAdjustableParameter(parmIdx, middle, true);
1699 
1700  return res;
1701 }
1702 
1703 NEWMAT::ColumnVector
1705 {
1706  //init
1707  NEWMAT::ColumnVector residue;
1708  int dimObs;
1709 
1710  bool useImageObs = useForward(); //caching
1711  if (useImageObs)
1712  {
1713  dimObs = 2; //image observation
1714  } else {
1715  dimObs = 3; //ground observations
1716  }
1717  int no = dimObs * tieSet.size(); //number of observations
1718 
1719  residue.ReSize(no);
1720 
1721  const vector<ossimRefPtr<ossimTieGpt> >& theTPV = tieSet.getTiePoints();
1722  vector<ossimRefPtr<ossimTieGpt> >::const_iterator tit;
1723  unsigned long c=1;
1724 
1725  if (useImageObs)
1726  {
1727  //image observations
1728  ossimDpt resIm;
1729  // loop on tie points
1730  for (tit = theTPV.begin() ; tit != theTPV.end() ; ++tit)
1731  {
1732  //compute residue
1733  resIm = (*tit)->tie - forward(**tit);
1734  residue(c++) = resIm.x;
1735  residue(c++) = resIm.y;
1736  }
1737  } else {
1738  // ground observations
1739  ossimGpt gd;
1740  // loop on tie points
1741  for (tit = theTPV.begin() ; tit != theTPV.end() ; ++tit)
1742  {
1743  //compute residue
1744  gd = inverse((*tit)->tie);
1745  residue(c++) = ((*tit)->lon - gd.lon) * 100000.0; //approx meters //TBC TBD
1746  residue(c++) = ((*tit)->lat - gd.lat) * 100000.0 * cos(gd.lat / 180.0 * M_PI);
1747  residue(c++) = (*tit)->hgt - gd.hgt; //meters
1748  }
1749  } //end of if (useImageObs)
1750 
1751  return residue;
1752 }
1753 
1759 NEWMAT::ColumnVector
1760 ossimSensorModel::solveLeastSquares(NEWMAT::SymmetricMatrix& A, NEWMAT::ColumnVector& r)const
1761 {
1762  NEWMAT::ColumnVector x = invert(A)*r;
1763  return x;
1764 }
1765 
1769 NEWMAT::Matrix
1770 ossimSensorModel::invert(const NEWMAT::Matrix& m)const
1771 {
1772  ossim_uint32 idx = 0;
1773  NEWMAT::DiagonalMatrix d;
1774  NEWMAT::Matrix u;
1775  NEWMAT::Matrix v;
1776 
1777  // decompose m.t*m which is stored in Temp into the singular values and vectors.
1778  //
1779  NEWMAT::SVD(m, d, u, v, true, true);
1780 
1781  // invert the diagonal
1782  // this is just doing the reciprical fo all diagonal components and store back int
1783  // d. ths compute d inverse.
1784  //
1785  for(idx=0; idx < (ossim_uint32)d.Ncols(); ++idx)
1786  {
1787  if(d[idx] > 1e-14) //TBC : use DBL_EPSILON ?
1788  {
1789  d[idx] = 1.0/d[idx];
1790  }
1791  else
1792  {
1793  d[idx] = 0.0;
1794 
1795 //DEBUG TBR
1796 cout<<"warning: singular matrix in SVD"<<endl;
1797 
1798  }
1799  }
1800 
1801  //compute inverse of decomposed m;
1802  return v*d*u.t();
1803 }
ossimString theSensorID
ossim_uint32 x
unsigned int size() const
bool saveAdjustments(ossimKeywordlist &kwl, const ossimString &prefix=ossimString("")) const
Save all adjustments to the KWL file.
ossim_float64 theRelPosError
virtual bool loadState(const ossimKeywordlist &kwl, const char *prefix=0)
ossim_float64 width() const
Definition: ossimDrect.h:522
double lond() const
Will convert the radian measure to degrees.
Definition: ossimGpt.h:97
Represents serializable keyword/value map.
double u
Definition: ossimDpt.h:164
bool isLonNan() const
Definition: ossimGpt.h:140
static const char * UL_LAT_KW
bool intersectRay(const ossimEcefRay &ray, ossimGpt &gpt, double defaultElevValue=0.0)
METHOD: intersectRay()
bool valid() const
Definition: ossimRefPtr.h:75
bool nextVertex(ossimDpt &tbd_vertex) const
METHOD: nextVertex() Assigns the ossimDpt tbd_vertex following the current vertex.
const char * find(const char *key) const
ossimString theImageID
double samp
Definition: ossimDpt.h:164
float ossim_float32
bool almostEqual(T x, T y, T tolerance=FLT_EPSILON)
Definition: ossimCommon.h:53
virtual std::ostream & print(std::ostream &out) const
Outputs theErrorStatus as an ossimErrorCode and an ossimString.
virtual ossimOptimizableProjection & operator=(const ossimOptimizableProjection &source)
double nan()
Method to return ieee floating point double precision NAN.
Definition: ossimCommon.h:135
const ossimDpt & ul() const
Definition: ossimDrect.h:339
void addPoint(const ossimDpt &pt)
double y
Definition: ossimDpt.h:165
virtual ossimGpt inverse(const ossimDpt &pp) const
void makeNan()
Definition: ossimGpt.h:130
static ossimString toString(bool aValue)
Numeric to string methods.
static const char * NUMBER_LINES_KW
static const char * LR_LON_KW
void split(std::vector< ossimString > &result, const ossimString &separatorList, bool skipBlankFields=false) const
Splits this string into a vector of strings (fields) using the delimiter list specified.
ossim_float64 hgt
Height in meters above the ellipsiod.
Definition: ossimGpt.h:274
static ossimElevManager * instance()
METHOD: instance() Implements singelton pattern.
virtual void lineSampleToWorld(const ossimDpt &image_point, ossimGpt &world_point) const
void setRefGndPt(const ossimGpt &pt)
Sets the center latitude, longitude, height of the image.
bool clipLineSegment(ossimDpt &p1, ossimDpt &p2) const
METHOD: clipLineSegment(p1, p2) Implements Cyrus-Beck clipping algorithm as described in: http://www...
double latd() const
Will convert the radian measure to degrees.
Definition: ossimGpt.h:87
std::vector< ossimAdjustableParameterInfo > & getParameterList()
bool isLatNan() const
Definition: ossimGpt.h:139
bool vertex(int index, ossimDpt &tbd_vertex) const
METHOD: vertex(index) Returns the ossimDpt vertex given the index.
#define A(r, c)
double distanceTo(const ossimGpt &arg_gpt) const
METHOD: distanceTo(ossimGpt) Computes straight-line distance in meters between this and arg gpt: ...
Definition: ossimGpt.cpp:431
static const char * METERS_PER_PIXEL_Y_KW
static const char * TYPE_KW
double length() const
Definition: ossimDpt.h:81
virtual ossimDpt getForwardDeriv(int parmIdx, const ossimGpt &gpos, double hdelta=1e-11)
const ossimSensorModel & operator=(const ossimSensorModel &rhs)
assignment operator
double ossim_float64
void add(const char *prefix, const ossimKeywordlist &kwl, bool overwrite=true)
ossimDpt theSubImageOffset
virtual bool loadState(const ossimKeywordlist &kwl, const char *prefix=0)
virtual class enabling projection optimization (can be used for outlier rejection - RANSAC) ...
void computeGsd()
This method computes the ground sample distance(gsd) and sets class attributes theGSD and theMeanGSD ...
static const char * LR_LAT_KW
double line
Definition: ossimDpt.h:165
double lat
Definition: ossimDpt.h:165
static const char * CE90_ABSOLUTE_KW
#define M_PI
ossim_float64 lon
Definition: ossimGpt.h:266
ossim_float64 theNominalPosError
static const char * LL_LON_KW
virtual ossim_uint32 degreesOfFreedom() const
virtual double getHeightAboveEllipsoid(const ossimGpt &gpt)
os2<< "> n<< " > nendobj n
static const char * IMAGE_CE90_KW
std::ostream & operator<<(std::ostream &os, NEWMAT::GeneralMatrix &mat)
unsigned int ossim_uint32
const char * chars() const
For backward compatibility.
Definition: ossimString.h:77
ossimString trim(const ossimString &valueToTrim=ossimString(" \\)) const
this will strip lead and trailing character passed in.
ossimPolygon theBoundGndPolygon
double toDouble() const
static const char * CE90_RELATIVE_KW
bool loadAdjustments(const ossimKeywordlist &kwl, const ossimString &prefix=ossimString(""))
bool hasNans() const
will sequence through the polygon and check to see if any values are NAN
RTTI_DEF3(ossimSensorModel, "ossimSensorModel", ossimProjection, ossimOptimizableProjection, ossimAdjustableParameterInterface)
virtual void lineSampleHeightToWorld(const ossimDpt &lineSampPt, const double &heightEllipsoid, ossimGpt &worldPt) const =0
static const char * LL_LAT_KW
void SVD(const Matrix &, DiagonalMatrix &, Matrix &, Matrix &, bool=true, bool=true)
Definition: svd.cpp:26
const vector< ossimRefPtr< ossimTieGpt > > & getTiePoints() const
virtual void imagingRay(const ossimDpt &image_point, ossimEcefRay &image_ray) const
virtual double optimizeFit(const ossimTieGptSet &tieSet, double *targetVariance=0)
virtual bool saveState(ossimKeywordlist &kwl, const char *prefix=0) const
virtual std::ostream & print(std::ostream &out) const
bool hasNans() const
Definition: ossimDrect.h:396
#define DBL_EPSILON
bool pointWithin(const ossimDpt &point) const
METHOD: pointWithin(ossimDpt) Returns TRUE if point is inside polygon.
bool hasNans() const
Definition: ossimDpt.h:67
double lon
Definition: ossimDpt.h:164
ossim_float64 height() const
Definition: ossimDrect.h:517
virtual void updateModel()
virtual ossimDpt extrapolate(const ossimGpt &gp) const
static void writeGeomTemplate(ostream &os)
ossimRefPtr< ossimProjection > theSeedFunction
Used as an initial guess for iterative solutions and a guess for points outside the support bounds...
void setImageRect(const ossimDrect &imageRect)
bool clip(ossimDpt &p1, ossimDpt &p2) const
Definition: ossimDrect.cpp:653
static ossimProjectionFactoryRegistry * instance()
virtual ossimObject * getBaseObject()
NEWMAT::ColumnVector getResidue(const ossimTieGptSet &tieSet)
ossim_uint32 getNumberOfVertices() const
virtual void worldToLineSample(const ossimGpt &world_point, ossimDpt &image_point) const
ossimDrect theImageClipRect
NEWMAT::ColumnVector solveLeastSquares(NEWMAT::SymmetricMatrix &A, NEWMAT::ColumnVector &r) const
long toLong() const
toLong&#39;s deprecated, please use the toInts...
ossim_int32 samp
Definition: ossimIpt.h:141
ossimDpt size() const
Definition: ossimDrect.h:524
virtual bool useForward() const =0
virtual ossimDpt forward(const ossimGpt &wp) const
ossimDpt midPoint() const
Definition: ossimDrect.h:817
bool hasNans() const
Definition: ossimGpt.h:135
virtual void setAdjustableParameter(ossim_uint32 idx, double value, bool notify=false)
#define max(a, b)
Definition: auxiliary.h:76
ossim_int32 y
Definition: ossimIpt.h:142
storage class for a set of geographic tie points, between master and slave images ...
virtual ossimGpt getInverseDeriv(int parmIdx, const ossimDpt &ipos, double hdelta=1e-11)
void setGroundRect(const ossimGpt &ul, const ossimGpt &ur, const ossimGpt &lr, const ossimGpt &ll)
double x
Definition: ossimDpt.h:164
static const char * UL_LON_KW
static const char * UR_LAT_KW
static const char * UR_LON_KW
const char * c_str() const
Returns a pointer to a null-terminated array of characters representing the string&#39;s contents...
Definition: ossimString.h:396
static const char * ID_KW
virtual bool saveState(ossimKeywordlist &kwl, const char *prefix=0) const
void buildNormalEquation(const ossimTieGptSet &tieSet, NEWMAT::SymmetricMatrix &A, NEWMAT::ColumnVector &residue, NEWMAT::ColumnVector &projResidue, double pstep_scale)
ossim_int32 line
Definition: ossimIpt.h:142
virtual bool insideImage(const ossimDpt &p) const
const ossimAdjustableParameterInterface & operator=(const ossimAdjustableParameterInterface &rhs)
virtual ossimSensorModel::CovMatStatus getObsCovMat(const ossimDpt &ipos, NEWMAT::SymmetricMatrix &Cov, const ossim_float64 defPointingSigma=0.5) const
Gives 2X2 covariance matrix of observations.
ossim_int32 x
Definition: ossimIpt.h:141
ossim_float64 lat
Definition: ossimGpt.h:265
void getAdjustment(ossimAdjustmentInfo &adj) const
void setRefImgPt(const ossimDpt &pt)
Sets the center line sampe of the image.
ossimDpt theParWRTx
Partials for current point.
virtual void lineSampleToWorld(const ossimDpt &lineSampPt, ossimGpt &worldPt) const =0
bool hasNans() const
Definition: ossimIpt.h:58
ossimDpt theObs
Observations & residuals for current point.
virtual void worldToLineSample(const ossimGpt &worldPoint, ossimDpt &lineSampPt) const =0
const ossimDpt & lr() const
Definition: ossimDrect.h:341
static const char * METERS_PER_PIXEL_X_KW
static const char * NUMBER_SAMPLES_KW
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
void makeNan()
Definition: ossimDpt.h:65
ossim_float64 theMeanGSD
std::basic_ostream< char > ostream
Base class for char output streams.
Definition: ossimIosFwd.h:23
double v
Definition: ossimDpt.h:165
NEWMAT::Matrix invert(const NEWMAT::Matrix &m) const
stable invert stolen from ossimRpcSolver
bool isnan(const float &v)
isnan Test for floating point Not A Number (NAN) value.
Definition: ossimCommon.h:91