Isis 3 Programmer Reference
ProjectionFactory.cpp
Go to the documentation of this file.
1 
23 #include "ProjectionFactory.h"
24 
25 
26 #include <cfloat>
27 #include <cmath>
28 #include <iomanip>
29 
30 #include "Camera.h"
31 #include "Cube.h"
32 #include "Displacement.h"
33 #include "Distance.h"
34 #include "FileName.h"
35 #include "IException.h"
36 #include "Plugin.h"
37 #include "Projection.h"
38 #include "RingPlaneProjection.h"
39 #include "TProjection.h"
40 
41 using namespace std;
42 
43 namespace Isis {
44  Plugin ProjectionFactory::m_projPlugin;
45 
67  Isis::Projection *ProjectionFactory::Create(Isis::Pvl &label,
68  bool allowDefaults) {
69  // Try to load a plugin file in the current working directory and then
70  // load the system file
71  Plugin p;
72 
73  if (m_projPlugin.fileName() == "") {
74  FileName localFile("Projection.plugin");
75  if (localFile.fileExists())
76  m_projPlugin.read(localFile.expanded());
77 
78  FileName systemFile("$ISISROOT/lib/Projection.plugin");
79  if (systemFile.fileExists())
80  m_projPlugin.read(systemFile.expanded());
81  }
82 
83  try {
84  // Look for info in the mapping group
85  Isis::PvlGroup &mapGroup = label.findGroup("Mapping", Isis::Pvl::Traverse);
86  QString proj = mapGroup["ProjectionName"];
87 
88  // Now get the plugin for the projection
89  QFunctionPointer ptr;
90  try {
91  ptr = m_projPlugin.GetPlugin(proj);
92  }
93  catch(IException &e) {
94  QString msg = "Unsupported projection, unable to find plugin for [" +
95  proj + "]";
96  throw IException(e, IException::Unknown, msg, _FILEINFO_);
97  }
98 
99  // Now cast that pointer in the proper way
100  Isis::TProjection * (*plugin)(Isis::Pvl & label, bool flag);
101  // plugin = (Isis::TProjection * ( *)(Isis::Pvl & label, bool flag)) ptr;
102  plugin = (Isis::TProjection * ( *)(Isis::Pvl & label, bool flag)) ptr;
103  // Create the projection as requested
104  return (Isis::Projection *) (*plugin)(label, allowDefaults);
105  }
106  catch(IException &e) {
107  QString message = "Unable to initialize Projection information ";
108  message += "from group [Mapping]";
109  throw IException(e, IException::Io, message, _FILEINFO_);
110  }
111  }
112 
113 
135  Isis::Projection *ProjectionFactory::RingsCreate(Isis::Pvl &label,
136  bool allowDefaults) {
137  // Try to load a plugin file in the current working directory and then
138  // load the system file
139  Plugin p;
140 
141  if (m_projPlugin.fileName() == "") {
142  FileName localFile("Projection.plugin");
143  if (localFile.fileExists())
144  m_projPlugin.read(localFile.expanded());
145 
146  FileName systemFile("$ISISROOT/lib/Projection.plugin");
147  if (systemFile.fileExists())
148  m_projPlugin.read(systemFile.expanded());
149  }
150 
151  try {
152  // Look for info in the mapping group
153  Isis::PvlGroup &mapGroup = label.findGroup("Mapping", Isis::Pvl::Traverse);
154  QString proj = mapGroup["ProjectionName"];
155 
156  // Now get the plugin for the projection
157  QFunctionPointer ptr;
158  try {
159  ptr = m_projPlugin.GetPlugin(proj);
160  }
161  catch(IException &e) {
162  QString msg = "Unsupported projection, unable to find plugin for [" +
163  proj + "]";
164  throw IException(e, IException::Unknown, msg, _FILEINFO_);
165  }
166 
167  // Now cast that pointer in the proper way
168  Isis::RingPlaneProjection * (*plugin)(Isis::Pvl & label, bool flag);
169  plugin = (Isis::RingPlaneProjection * ( *)(Isis::Pvl & label, bool flag)) ptr;
170  // Create the projection as requested
171  return (Projection *) (*plugin)(label, allowDefaults);
172  }
173  catch(IException &e) {
174  QString message = "Unable to initialize Projection information ";
175  message += "from group [Mapping]";
176  throw IException(e, IException::Io, message, _FILEINFO_);
177  }
178  }
179 
180 
204  Isis::Projection *ProjectionFactory::CreateForCube(Isis::Pvl &label,
205  int &samples, int &lines,
206  bool sizeMatch) {
207  // Create a temporary projection and get the radius at the special latitude
208  Isis::TProjection *proj = (Isis::TProjection *) Create(label, true);
209  double trueScaleLat = proj->TrueScaleLatitude();
210  double localRadius = proj->LocalRadius(trueScaleLat);
211  delete proj;
212 
213  IException errors;
214  try {
215  // Try to get the pixel resolution and then compute the scale
216  double scale, pixelResolution;
217  Isis::PvlGroup &mapGroup = label.findGroup("Mapping", Isis::Pvl::Traverse);
218  try {
219  pixelResolution = mapGroup["PixelResolution"];
220  scale = (2.0 * Isis::PI * localRadius) / (360.0 * pixelResolution);
221  }
222 
223  // If not get the scale and then compute the pixel resolution
224  catch(IException &e) {
225  errors.append(e);
226 
227  scale = mapGroup["Scale"];
228  pixelResolution = (2.0 * Isis::PI * localRadius) / (360.0 * scale);
229  }
230  // Write out the scale and resolution with units and truescale latitude
231  mapGroup.addKeyword(Isis::PvlKeyword("PixelResolution", toString(pixelResolution), "meters/pixel"),
232  Isis::Pvl::Replace);
233  mapGroup.addKeyword(Isis::PvlKeyword("Scale", toString(scale), "pixels/degree"), Isis::Pvl::Replace);
234  //mapGroup.addKeyword(Isis::PvlKeyword ("TrueScaleLatitude", trueScaleLat),
235  // Isis::Pvl::Replace);
236 
237  // Get the upper left corner from the labels if possible
238  // This forces an exact match of projection parameters for
239  // output cubes
240  bool sizeFound = false;
241  double upperLeftX = Null, upperLeftY = Null;
242  if (label.hasObject("IsisCube")) {
243  Isis::PvlGroup &dims = label.findGroup("Dimensions", Isis::Pvl::Traverse);
244  samples = dims["Samples"];
245  lines = dims["Lines"];
246 
247  upperLeftX = mapGroup["UpperLeftCornerX"];
248  upperLeftY = mapGroup["UpperLeftCornerY"];
249  sizeFound = true;
250  }
251  if (!sizeMatch) sizeFound = false;
252 
253  // Initialize the rest of the projection
254  proj = (Isis::TProjection *) Create(label, true);
255 
256  // Couldn't find the cube size from the labels so compute it
257  if (!sizeFound) {
258  if (!proj->HasGroundRange()) {
259  QString msg = "Invalid ground range [MinimumLatitude,MaximumLatitude,";
260  msg += "MinimumLongitude,MaximumLongitude] missing or invalid";
261  throw IException(IException::Unknown, msg, _FILEINFO_);
262  }
263 
264  double minX, maxX, minY, maxY;
265  if (!proj->XYRange(minX, maxX, minY, maxY)) {
266  QString msg = "Invalid ground range [MinimumLatitude,MaximumLatitude,";
267  msg += "MinimumLongitude,MaximumLongitude] cause invalid computation ";
268  msg += "of image size";
269  throw IException(IException::Unknown, msg, _FILEINFO_);
270  }
271 
272 
273  // Convert upperleft coordinate to units of pixel
274  // Truncate it to the nearest whole pixel (floor/ceil)
275  // Convert it back to meters. But don't do this if
276  // the X/Y position is already close to a whole pixel because
277  // the floor/ceil function could cause an extra pixel to be added
278  // just due to machine precision issues
279 
280  bool flipX = false;
281  bool flipY = false;
282 
283  double minXFlipped = minX;
284  double maxXFlipped = maxX;
285  double minYFlipped = minY;
286  double maxYFlipped = maxY;
287 
288  //New range is (-1)*[0,maxX] = [-maxX, 0]
289 
290  if (minX == 0) {
291  minXFlipped = -maxX;
292  maxXFlipped = 0;
293  flipX = true;
294 
295  }
296 
297  if (fabs(fmod(minXFlipped, pixelResolution)) > 1.0e-6) {
298  if (pixelResolution - fabs(fmod(minXFlipped, pixelResolution)) > 1.0e-6) {
299  double sampleOffset = floor(minXFlipped / pixelResolution);
300 
301  minXFlipped = sampleOffset * pixelResolution;
302 
303  }
304  }
305 
306 
307  // make sure that the distance from minX to maxX is at least one pixel wide
308  // so we have at least one sample in the created cube
309 
310 
311  if (maxXFlipped < minXFlipped + pixelResolution) {
312  maxXFlipped = minXFlipped + pixelResolution;
313  }
314 
315 
316  //New range is (-1)*[minY,0] = [0,-minY]
317  if (maxY == 0) {
318  maxYFlipped = -minY;
319  minYFlipped = 0;
320  flipY = true;
321 
322  }
323 
324  if (fabs(fmod(maxYFlipped, pixelResolution)) > 1.0e-6) {
325  if (abs(pixelResolution - fabs(fmod(maxYFlipped, pixelResolution))) > 1.0e-6) {
326  double lineOffset = ceil(maxYFlipped / pixelResolution);
327  maxYFlipped = lineOffset * pixelResolution;
328 
329  }
330  }
331 
332 
333  // make sure that the distance from minY to maxY is at least one pixel wide
334  // so we have at least one line in the created cube
335  if (minYFlipped > maxYFlipped - pixelResolution) {
336  minYFlipped = maxYFlipped - pixelResolution;
337  }
338 
339  // Determine the number of samples and lines
340 
341  samples = (int)((maxXFlipped - minXFlipped) / pixelResolution + 0.5);
342  lines = (int)((maxYFlipped - minYFlipped) / pixelResolution + 0.5);
343 
344 
345  // Set the upper left corner and add to the labels
346  if (flipX) {
347  upperLeftX = 0;
348 
349  }
350  else {
351  upperLeftX = minXFlipped;
352  }
353  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerX", toString(upperLeftX)),
354  Isis::Pvl::Replace);
355 
356  if (flipY) {
357  upperLeftY = 0;
358 
359  }
360  else {
361  upperLeftY = maxYFlipped;
362 
363  }
364 
365  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerY", toString(upperLeftY)),
366  Isis::Pvl::Replace);
367 
368  // Write it in pixel units as well
369 #if 0
370  lineOffset += 0.5; // This matches the PDS definition
371  sampleOffset += 0.5; // of the offsets (center of pixel). This statement is questionable!
372  mapGroup.addKeyword(Isis::PvlKeyword("LineProjectionOffset", lineOffset),
373  Isis::Pvl::Replace);
374  mapGroup.addKeyword(Isis::PvlKeyword("SampleProjectionOffset", sampleOffset),
375  Isis::Pvl::Replace);
376 #endif
377  }
378 
379 
380  // Make sure labels have good units
381  mapGroup.addKeyword(Isis::PvlKeyword("PixelResolution",
382  (QString) mapGroup["PixelResolution"],
383  "meters/pixel"), Isis::Pvl::Replace);
384 
385  mapGroup.addKeyword(Isis::PvlKeyword("Scale",
386  (QString) mapGroup["Scale"],
387  "pixels/degree"), Isis::Pvl::Replace);
388 
389  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerX",
390  (QString) mapGroup["UpperLeftCornerX"],
391  "meters"), Isis::Pvl::Replace);
392 
393  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerY",
394  (QString) mapGroup["UpperLeftCornerY"],
395  "meters"), Isis::Pvl::Replace);
396 
397  mapGroup.addKeyword(Isis::PvlKeyword("EquatorialRadius",
398  (QString) mapGroup["EquatorialRadius"],
399  "meters"), Isis::Pvl::Replace);
400 
401  mapGroup.addKeyword(Isis::PvlKeyword("PolarRadius",
402  (QString) mapGroup["PolarRadius"],
403  "meters"), Isis::Pvl::Replace);
404 
405  // Add the mapper from pixel coordinates to projection coordinates
406  PFPixelMapper *pixelMapper = new PFPixelMapper(pixelResolution, upperLeftX, upperLeftY);
407  proj->SetWorldMapper(pixelMapper);
408 
409  proj->SetUpperLeftCorner(Displacement(upperLeftX, Displacement::Meters),
410  Displacement(upperLeftY, Displacement::Meters));
411  }
412  catch(IException &e) {
413  QString msg = "Unable to create projection";
414  if (label.fileName() != "") msg += " from file [" + label.fileName() + "]";
415  IException finalError(IException::Unknown, msg, _FILEINFO_);
416  finalError.append(errors);
417  finalError.append(e);
418  throw finalError;
419  }
420  return (Isis::Projection *) proj;
421  }
422 
423 
448  Isis::Projection *ProjectionFactory::RingsCreateForCube(Isis::Pvl &label,
449  int &samples, int &lines, bool sizeMatch) {
450 
451  // Create a temporary projection and get the radius at the special radius
452  // NOTE: by "special radius" we mean that radius where the projection is
453  // not distorted
454  Isis::RingPlaneProjection *proj = (Isis::RingPlaneProjection *) RingsCreate(label, true);
455  double localRadius = proj->TrueScaleRingRadius();
456  delete proj;
457 
458  IException errors;
459  try {
460  // Try to get the pixel resolution and then compute the scale
461  double scale, pixelResolution;
462  Isis::PvlGroup &mapGroup = label.findGroup("Mapping", Isis::Pvl::Traverse);
463  try {
464  pixelResolution = mapGroup["PixelResolution"];
465  scale = (2.0 * Isis::PI * localRadius) / (360.0 * pixelResolution);
466  }
467  catch(IException &e) {
468  // If this fails, use the scale to compute the pixel resolution
469  errors.append(e);
470 
471  scale = mapGroup["Scale"];
472  pixelResolution = (2.0 * Isis::PI * localRadius) / (360.0 * scale);
473  }
474 
475  // Write out the scale and resolution with units and truescale radius
476  mapGroup.addKeyword(Isis::PvlKeyword("PixelResolution", toString(pixelResolution),
477  "meters/pixel"),
478  Isis::Pvl::Replace);
479  mapGroup.addKeyword(Isis::PvlKeyword("Scale", toString(scale), "pixels/degree"),
480  Isis::Pvl::Replace);
481 
482  //mapGroup.AddKeyword(Isis::PvlKeyword ("TrueScaleRadius", trueScaleRadius),
483  // Isis::Pvl::Replace);
484 
485  // Get the upper left corner from the labels if possible
486  // This forces an exact match of projection parameters for
487  // output cubes
488  bool sizeFound = false;
489  double upperLeftX = Null, upperLeftY = Null;
490  if (label.hasObject("IsisCube")) {
491  Isis::PvlGroup &dims = label.findGroup("Dimensions", Isis::Pvl::Traverse);
492  samples = dims["Samples"];
493  lines = dims["Lines"];
494 
495  upperLeftX = mapGroup["UpperLeftCornerX"];
496  upperLeftY = mapGroup["UpperLeftCornerY"];
497  sizeFound = true;
498  }
499 
500  if (!sizeMatch) {
501  sizeFound = false;
502  }
503 
504  // Initialize the rest of the projection
505  proj = (Isis::RingPlaneProjection *) RingsCreate(label, true);
506 
507  // Couldn't find the cube size from the labels so compute it
508  if (!sizeFound) {
509  if (!proj->HasGroundRange()) {
510  QString msg = "Invalid ring range [MinimumRingRadius,MaximumRingRadius,";
511  msg += "MinimumRingLongitude,MaximumRingLongitude] missing or invalid";
512  throw IException(IException::Unknown, msg, _FILEINFO_);
513  }
514 
515  double minX, maxX, minY, maxY;
516  if (!proj->XYRange(minX, maxX, minY, maxY)) {
517  QString msg = "Invalid ring range [MinimumRingRadius,MaximumRingRadius,";
518  msg += "MinimumRingLongitude,MaximumRingLongitude] cause invalid computation ";
519  msg += "of image size";
520  throw IException(IException::Unknown, msg, _FILEINFO_);
521  }
522 
523  // Convert upperleft coordinate to pixel units
524  // Truncate it to the nearest whole pixel (floor/ceil)
525  // Convert it back to meters. But don't do this if
526  // the X/Y position is already close to a whole pixel because
527  // the floor/ceil function could cause an extra pixel to be added
528  // just due to machine precision issues
529  if (fabs(fmod(minX, pixelResolution)) > 1.0e-6) {
530  if (pixelResolution - fabs(fmod(minX, pixelResolution)) > 1.0e-6) {
531  double sampleOffset = floor(minX / pixelResolution);
532  minX = sampleOffset * pixelResolution;
533  }
534  }
535  // make sure that the distance from minX to maxX is at least one pixel wide
536  // so we have at least one sample in the created cube
537  if (maxX < minX + pixelResolution) {
538  maxX = minX + pixelResolution;
539  }
540  if (fabs(fmod(maxY, pixelResolution)) > 1.0e-6) {
541  if (pixelResolution - fabs(fmod(maxY, pixelResolution)) > 1.0e-6) {
542  double lineOffset = -1.0 * ceil(maxY / pixelResolution);
543  maxY = -1.0 * lineOffset * pixelResolution;
544  }
545  }
546  // make sure that the distance from minY to maxY is at least one pixel wide
547  // so we have at least one line in the created cube
548  if (minY > maxY - pixelResolution) {
549  minY = maxY - pixelResolution;
550  }
551 
552  // Determine the number of samples and lines
553  samples = (int)((maxX - minX) / pixelResolution + 0.5);
554  lines = (int)((maxY - minY) / pixelResolution + 0.5);
555 
556  // Set the upper left corner and add to the labels
557  upperLeftX = minX;
558  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerX", toString(upperLeftX)),
559  Isis::Pvl::Replace);
560 
561  upperLeftY = maxY;
562  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerY", toString(upperLeftY)),
563  Isis::Pvl::Replace);
564 
565  // Write it in pixel units as well
566 #if 0
567  lineOffset += 0.5; // This matches the PDS definition
568  sampleOffset += 0.5; // of the offsets (center of pixel). This statement is questionable!
569  mapGroup.AddKeyword(Isis::PvlKeyword("LineProjectionOffset", lineOffset),
570  Isis::Pvl::Replace);
571  mapGroup.AddKeyword(Isis::PvlKeyword("SampleProjectionOffset", sampleOffset),
572  Isis::Pvl::Replace);
573 #endif
574  }
575 
576 
577  // Make sure labels have good units
578  mapGroup.addKeyword(Isis::PvlKeyword("PixelResolution",
579  (QString) mapGroup["PixelResolution"],
580  "meters/pixel"), Isis::Pvl::Replace);
581 
582  mapGroup.addKeyword(Isis::PvlKeyword("Scale",
583  (QString) mapGroup["Scale"],
584  "pixels/degree"), Isis::Pvl::Replace);
585 
586  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerX",
587  (QString) mapGroup["UpperLeftCornerX"],
588  "meters"), Isis::Pvl::Replace);
589 
590  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerY",
591  (QString) mapGroup["UpperLeftCornerY"],
592  "meters"), Isis::Pvl::Replace);
593 
594  // Add the mapper from pixel coordinates to projection coordinates
595  PFPixelMapper *pixelMapper = new PFPixelMapper(pixelResolution, upperLeftX, upperLeftY);
596  proj->SetWorldMapper(pixelMapper);
597 
598  proj->SetUpperLeftCorner(Displacement(upperLeftX, Displacement::Meters),
599  Displacement(upperLeftY, Displacement::Meters));
600  }
601  catch(IException &e) {
602  QString msg = "Unable to create projection";
603  if (label.fileName() != "") msg += " from file [" + label.fileName() + "]";
604  IException finalError(IException::Unknown, msg, _FILEINFO_);
605  finalError.append(errors);
606  finalError.append(e);
607  throw finalError;
608  }
609  return (Isis::Projection *) proj;
610  }
611 
612 
637  Isis::Projection *ProjectionFactory::CreateForCube(Isis::Pvl &label,
638  int &samples, int &lines,
639  Camera &cam) {
640  // Create a temporary projection and get the radius at the special latitude
641  Isis::TProjection *proj = (Isis::TProjection *) Create(label, true);
642  double trueScaleLat = proj->TrueScaleLatitude();
643  double localRadius = proj->LocalRadius(trueScaleLat);
644  delete proj;
645 
646  try {
647  // Try to get the pixel resolution and then compute the scale
648  double scale, pixelResolution;
649  Isis::PvlGroup &mapGroup = label.findGroup("Mapping", Isis::Pvl::Traverse);
650  try {
651  pixelResolution = mapGroup["PixelResolution"];
652  scale = (2.0 * Isis::PI * localRadius) / (360.0 * pixelResolution);
653  }
654 
655  // If not get the scale and then compute the pixel resolution
656  catch(IException &) {
657  scale = mapGroup["Scale"];
658  pixelResolution = (2.0 * Isis::PI * localRadius) / (360.0 * scale);
659  }
660  // Write out the scale and resolution with units and truescale latitude
661  mapGroup.addKeyword(Isis::PvlKeyword("PixelResolution", toString(pixelResolution), "meters/pixel"),
662  Isis::Pvl::Replace);
663  mapGroup.addKeyword(Isis::PvlKeyword("Scale", toString(scale), "pixels/degree"), Isis::Pvl::Replace);
664  //mapGroup.AddKeyword(Isis::PvlKeyword ("TrueScaleLatitude", trueScaleLatitude),
665  // Isis::Pvl::Replace);
666 
667  // Initialize the rest of the projection
668  proj = (Isis::TProjection *) Create(label, true);
669  double minX = DBL_MAX;
670  double maxX = -DBL_MAX;
671  double minY = DBL_MAX;
672  double maxY = -DBL_MAX;
673 
674  // Walk the boundaries of the camera to determine the x/y range
675  int eband = cam.Bands();
676  if (cam.IsBandIndependent()) eband = 1;
677  for(int band = 1; band <= eband; band++) {
678  cam.SetBand(band);
679 
680  // Loop for each line testing the left and right sides of the image
681  for(int line = 0; line <= cam.Lines(); line++) {
682  // Look for the first good lat/lon on the left edge of the image
683  // If it is the first or last line then test the whole line
684  int samp;
685  for(samp = 0; samp <= cam.Samples(); samp++) {
686  if (cam.SetImage((double)samp + 0.5, (double)line + 0.5)) {
687  double lat = cam.UniversalLatitude();
688  double lon = cam.UniversalLongitude();
689  proj->SetUniversalGround(lat, lon);
690  if (proj->IsGood()) {
691  if (proj->XCoord() < minX) minX = proj->XCoord();
692  if (proj->XCoord() > maxX) maxX = proj->XCoord();
693  if (proj->YCoord() < minY) minY = proj->YCoord();
694  if (proj->YCoord() > maxY) maxY = proj->YCoord();
695  if ((line != 0) && (line != cam.Lines())) break;
696  }
697  }
698  }
699 
700  // Look for the first good lat/lon on the right edge of the image
701  if (samp < cam.Samples()) {
702  for(samp = cam.Samples(); samp >= 0; samp--) {
703  if (cam.SetImage((double)samp + 0.5, (double)line + 0.5)) {
704  double lat = cam.UniversalLatitude();
705  double lon = cam.UniversalLongitude();
706  proj->SetUniversalGround(lat, lon);
707  if (proj->IsGood()) {
708  if (proj->XCoord() < minX) minX = proj->XCoord();
709  if (proj->XCoord() > maxX) maxX = proj->XCoord();
710  if (proj->YCoord() < minY) minY = proj->YCoord();
711  if (proj->YCoord() > maxY) maxY = proj->YCoord();
712  break;
713  }
714  }
715  }
716  }
717  }
718 
719  // Special test for ground range to see if either pole is in the image
720  if (cam.SetUniversalGround(90.0, 0.0)) {
721  if (cam.Sample() >= 0.5 && cam.Line() >= 0.5 &&
722  cam.Sample() <= cam.Samples() + 0.5 && cam.Line() <= cam.Lines() + 0.5) {
723  double lat = cam.UniversalLatitude();
724  double lon = cam.UniversalLongitude();
725  proj->SetUniversalGround(lat, lon);
726  if (proj->IsGood()) {
727  if (proj->XCoord() < minX) minX = proj->XCoord();
728  if (proj->XCoord() > maxX) maxX = proj->XCoord();
729  if (proj->YCoord() < minY) minY = proj->YCoord();
730  if (proj->YCoord() > maxY) maxY = proj->YCoord();
731  }
732  }
733  }
734 
735  if (cam.SetUniversalGround(-90.0, 0.0)) {
736  if (cam.Sample() >= 0.5 && cam.Line() >= 0.5 &&
737  cam.Sample() <= cam.Samples() + 0.5 && cam.Line() <= cam.Lines() + 0.5) {
738  double lat = cam.UniversalLatitude();
739  double lon = cam.UniversalLongitude();
740  proj->SetUniversalGround(lat, lon);
741  if (proj->IsGood()) {
742  if (proj->XCoord() < minX) minX = proj->XCoord();
743  if (proj->XCoord() > maxX) maxX = proj->XCoord();
744  if (proj->YCoord() < minY) minY = proj->YCoord();
745  if (proj->YCoord() > maxY) maxY = proj->YCoord();
746  }
747  }
748  }
749 
750 #if 0
751  // Another special test for ground range as we could have the
752  // 0-360 seam running right through the image so
753  // test it as well (the increment may not be fine enough !!!)
754  for(double lat = p_minlat; lat <= p_maxlat; lat += (p_maxlat - p_minlat) / 10.0) {
755  if (SetUniversalGround(lat, 0.0)) {
756  if (Sample() >= 0.5 && Line() >= 0.5 &&
757  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
758  p_minlon = 0.0;
759  p_maxlon = 360.0;
760  break;
761  }
762  }
763  }
764 
765  // Another special test for ground range as we could have the
766  // -180-180 seam running right through the image so
767  // test it as well (the increment may not be fine enough !!!)
768  for(double lat = p_minlat; lat <= p_maxlat; lat += (p_maxlat - p_minlat) / 10.0) {
769  if (SetUniversalGround(lat, 180.0)) {
770  if (Sample() >= 0.5 && Line() >= 0.5 &&
771  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
772  p_minlon180 = -180.0;
773  p_maxlon180 = 180.0;
774  break;
775  }
776  }
777  }
778 #endif
779  }
780 
781  // Convert upperleft coordinate to units of pixel
782  // Truncate it to the nearest whole pixel (floor/ceil)
783  // Convert it back to meters. But don't do this if
784  // the X/Y position is already close to a whole pixel because
785  // the floor/ceil function could cause an extra pixel to be added
786  // just due to machine precision issues
787  if (fabs(fmod(minX, pixelResolution)) > 1.0e-6) {
788  if (pixelResolution - fabs(fmod(minX, pixelResolution)) > 1.0e-6) {
789  double sampleOffset = floor(minX / pixelResolution);
790  minX = sampleOffset * pixelResolution;
791  }
792  }
793  // make sure that the distance from minX to maxX is at least one pixel wide
794  // so we have at least one sample in the created cube
795  if (maxX < minX + pixelResolution) {
796  maxX = minX + pixelResolution;
797  }
798  if (fabs(fmod(maxY, pixelResolution)) > 1.0e-6) {
799  if (pixelResolution - fabs(fmod(maxY, pixelResolution)) > 1.0e-6) {
800  double lineOffset = -1.0 * ceil(maxY / pixelResolution);
801  maxY = -1.0 * lineOffset * pixelResolution;
802  }
803  }
804  // make sure that the distance from minY to maxY is at least one pixel wide
805  // so we have at least one line in the created cube
806  if (minY > maxY - pixelResolution) {
807  minY = maxY - pixelResolution;
808  }
809 
810  // Determine the number of samples and lines
811  samples = (int)((maxX - minX) / pixelResolution + 0.5);
812  lines = (int)((maxY - minY) / pixelResolution + 0.5);
813 
814  // Set the upper left corner and add to the labels
815  double upperLeftX = minX;
816  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerX", toString(upperLeftX)),
817  Isis::Pvl::Replace);
818 
819  double upperLeftY = maxY;
820  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerY", toString(upperLeftY)),
821  Isis::Pvl::Replace);
822 
823  // Make sure labels have good units
824  mapGroup.addKeyword(Isis::PvlKeyword("PixelResolution",
825  (QString) mapGroup["PixelResolution"],
826  "meters/pixel"), Isis::Pvl::Replace);
827 
828  mapGroup.addKeyword(Isis::PvlKeyword("Scale",
829  (QString) mapGroup["Scale"],
830  "pixels/degree"), Isis::Pvl::Replace);
831 
832  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerX",
833  (QString) mapGroup["UpperLeftCornerX"],
834  "meters"), Isis::Pvl::Replace);
835 
836  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerY",
837  (QString) mapGroup["UpperLeftCornerY"],
838  "meters"), Isis::Pvl::Replace);
839 
840  mapGroup.addKeyword(Isis::PvlKeyword("EquatorialRadius",
841  (QString) mapGroup["EquatorialRadius"],
842  "meters"), Isis::Pvl::Replace);
843 
844  mapGroup.addKeyword(Isis::PvlKeyword("PolarRadius",
845  (QString) mapGroup["PolarRadius"],
846  "meters"), Isis::Pvl::Replace);
847 
848  // Add the mapper from pixel coordinates to projection coordinates
849  PFPixelMapper *pixelMapper = new PFPixelMapper(pixelResolution, upperLeftX, upperLeftY);
850  proj->SetWorldMapper(pixelMapper);
851 
852  proj->SetUpperLeftCorner(Displacement(upperLeftX, Displacement::Meters),
853  Displacement(upperLeftY, Displacement::Meters));
854  }
855  catch(IException &e) {
856  QString msg = "Unable to create projection";
857  if (label.fileName() != "") msg += " from file [" + label.fileName() + "]";
858  throw IException(e, IException::Unknown, msg, _FILEINFO_);
859  }
860  return (Isis::Projection *) proj;
861  }
862 
863 
888  Isis::Projection *ProjectionFactory::RingsCreateForCube(Isis::Pvl &label,
889  int &samples, int &lines, Camera &cam) {
890 
891  // Create a temporary projection
892  Isis::RingPlaneProjection *proj = (Isis::RingPlaneProjection *) RingsCreate(label, true);
893  double localRadius = proj->TrueScaleRingRadius();
894  delete proj;
895 
896  IException errors;
897  try {
898  // Try to get the pixel resolution and then compute the scale
899  double scale, pixelResolution;
900  Isis::PvlGroup &mapGroup = label.findGroup("Mapping", Isis::Pvl::Traverse);
901  try {
902  pixelResolution = mapGroup["PixelResolution"];
903  scale = (2.0 * Isis::PI * localRadius) / (360.0 * pixelResolution);
904  // scale = proj->Scale();
905  }
906 
907  // If not get the scale and then compute the pixel resolution
908  catch(IException &e) {
909  errors.append(e);
910  scale = mapGroup["Scale"];
911  pixelResolution = (2.0 * Isis::PI * localRadius) / (360.0 * scale);
912  }
913  // Write out the scale and resolution with units and truescale radius
914  mapGroup.addKeyword(Isis::PvlKeyword("PixelResolution", toString(pixelResolution),
915  "meters/pixel"), Isis::Pvl::Replace);
916  mapGroup.addKeyword(Isis::PvlKeyword("Scale", toString(scale), "pixels/degree"),
917  Isis::Pvl::Replace);
918 
919  // Initialize the rest of the projection
920  proj = (Isis::RingPlaneProjection *) RingsCreate(label, true);
921  double minX = DBL_MAX;
922  double maxX = -DBL_MAX;
923  double minY = DBL_MAX;
924  double maxY = -DBL_MAX;
925 
926  // Walk the boundaries of the camera to determine the x/y range
927  int eband = cam.Bands();
928  if (cam.IsBandIndependent()) eband = 1;
929  for(int band = 1; band <= eband; band++) {
930  cam.SetBand(band);
931 
932  // Loop for each line testing the left and right sides of the image
933  for(int line = 0; line <= cam.Lines(); line++) {
934  // Look for the first good rad/az on the left edge of the image
935  // If it is the first or last line then test the whole line
936  int samp;
937  for(samp = 0; samp <= cam.Samples(); samp++) {
938  if (cam.SetImage((double)samp + 0.5, (double)line + 0.5)) {
939  double radius = cam.LocalRadius().meters();
940  double az = cam.UniversalLongitude();
941  proj->SetGround(radius, az);
942  if (proj->IsGood()) {
943  if (proj->XCoord() < minX) minX = proj->XCoord();
944  if (proj->XCoord() > maxX) maxX = proj->XCoord();
945  if (proj->YCoord() < minY) minY = proj->YCoord();
946  if (proj->YCoord() > maxY) maxY = proj->YCoord();
947  if ((line != 0) && (line != cam.Lines())) break;
948  }
949  }
950  }
951 
952  // Look for the first good rad/az on the right edge of the image
953  if (samp < cam.Samples()) {
954  for(samp = cam.Samples(); samp >= 0; samp--) {
955  if (cam.SetImage((double)samp + 0.5, (double)line + 0.5)) {
956  double radius = cam.LocalRadius().meters();
957  double az = cam.UniversalLongitude();
958  proj->SetGround(radius, az);
959  if (proj->IsGood()) {
960  if (proj->XCoord() < minX) minX = proj->XCoord();
961  if (proj->XCoord() > maxX) maxX = proj->XCoord();
962  if (proj->YCoord() < minY) minY = proj->YCoord();
963  if (proj->YCoord() > maxY) maxY = proj->YCoord();
964  break;
965  }
966  }
967  }
968  }
969  }
970 
971 #if 0
972  // Another special test for ground range as we could have the
973  // 0-360 seam running right through the image so
974  // test it as well (the increment may not be fine enough !!!)
975  for(double rad = p_minRadius; rad <= p_maxRadius; rad += (p_maxRadius - p_minRadius) / 10.0) {
976  if (SetUniversalGround(rad, 0.0)) {
977  if (Sample() >= 0.5 && Line() >= 0.5 &&
978  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
979  p_minaz = 0.0;
980  p_maxaz = 360.0;
981  break;
982  }
983  }
984  }
985 
986  // Another special test for ground range as we could have the
987  // -180-180 seam running right through the image so
988  // test it as well (the increment may not be fine enough !!!)
989  for(double rad = p_minrad; rad <= p_maxrad; rad += (p_maxrad - p_minrad) / 10.0) {
990  if (SetUniversalGround(rad, 180.0)) {
991  if (Sample() >= 0.5 && Line() >= 0.5 &&
992  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
993  p_minaz180 = -180.0;
994  p_maxaz180 = 180.0;
995  break;
996  }
997  }
998  }
999 #endif
1000  }
1001 
1002  // Convert upperleft coordinate to units of pixel
1003  // Truncate it to the nearest whole pixel (floor/ceil)
1004  // Convert it back to meters. But don't do this if
1005  // the X/Y position is already close to a whole pixel because
1006  // the floor/ceil function could cause an extra pixel to be added
1007  // just due to machine precision issues
1008  if (fabs(fmod(minX, pixelResolution)) > 1.0e-6) {
1009  if (pixelResolution - fabs(fmod(minX, pixelResolution)) > 1.0e-6) {
1010  double sampleOffset = floor(minX / pixelResolution);
1011  minX = sampleOffset * pixelResolution;
1012  }
1013  }
1014  // make sure that the distance from minX to maxX is at least one pixel wide
1015  // so we have at least one sample in the created cube
1016  if (maxX < minX + pixelResolution) {
1017  maxX = minX + pixelResolution;
1018  }
1019 
1020  if (fabs(fmod(maxY, pixelResolution)) > 1.0e-6) {
1021  if (pixelResolution - fabs(fmod(maxY, pixelResolution)) > 1.0e-6) {
1022  double lineOffset = -1.0 * ceil(maxY / pixelResolution);
1023  maxY = -1.0 * lineOffset * pixelResolution;
1024  }
1025  }
1026  // make sure that the distance from minY to maxY is at least one pixel wide
1027  // so we have at least one line in the created cube
1028  if (minY > maxY - pixelResolution) {
1029  minY = maxY - pixelResolution;
1030  }
1031 
1032  // Determine the number of samples and lines
1033  samples = (int)((maxX - minX) / pixelResolution + 0.5);
1034  lines = (int)((maxY - minY) / pixelResolution + 0.5);
1035 
1036  // Set the upper left corner and add to the labels
1037  double upperLeftX = minX;
1038  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerX", toString(upperLeftX)),
1039  Isis::Pvl::Replace);
1040 
1041  double upperLeftY = maxY;
1042  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerY", toString(upperLeftY)),
1043  Isis::Pvl::Replace);
1044 
1045  // Make sure labels have good units
1046  mapGroup.addKeyword(Isis::PvlKeyword("PixelResolution",
1047  (QString) mapGroup["PixelResolution"],
1048  "meters/pixel"), Isis::Pvl::Replace);
1049 
1050  mapGroup.addKeyword(Isis::PvlKeyword("Scale",
1051  (QString) mapGroup["Scale"],
1052  "pixels/degree"), Isis::Pvl::Replace);
1053 
1054  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerX",
1055  (QString) mapGroup["UpperLeftCornerX"],
1056  "meters"), Isis::Pvl::Replace);
1057 
1058  mapGroup.addKeyword(Isis::PvlKeyword("UpperLeftCornerY",
1059  (QString) mapGroup["UpperLeftCornerY"],
1060  "meters"), Isis::Pvl::Replace);
1061 
1062  // Add the mapper from pixel coordinates to projection coordinates
1063  PFPixelMapper *pixelMapper = new PFPixelMapper(pixelResolution, upperLeftX, upperLeftY);
1064  proj->SetWorldMapper(pixelMapper);
1065 
1066  proj->SetUpperLeftCorner(Displacement(upperLeftX, Displacement::Meters),
1067  Displacement(upperLeftY, Displacement::Meters));
1068  }
1069  catch(IException &e) {
1070  QString msg = "Unable to create projection";
1071  if (label.fileName() != "") msg += " from file [" + label.fileName() + "]";
1072  throw IException(e, IException::Unknown, msg, _FILEINFO_);
1073  }
1074  return (Isis::Projection *) proj;
1075  }
1076 
1077 
1085  Isis::Projection *ProjectionFactory::CreateFromCube(Isis::Cube &cube) {
1086  return CreateFromCube(*cube.label());
1087  }
1088 
1089 
1097  Isis::Projection *ProjectionFactory::RingsCreateFromCube(Isis::Cube &cube) {
1098  return RingsCreateFromCube(*cube.label());
1099  }
1100 
1101 
1111  Isis::Projection *ProjectionFactory::CreateFromCube(Isis::Pvl &label) {
1112  Isis::TProjection *proj;
1113  try {
1114  // Get the pixel resolution
1115  Isis::PvlGroup &mapGroup = label.findGroup("Mapping", Isis::Pvl::Traverse);
1116  double pixelResolution = mapGroup["PixelResolution"];
1117 
1118  // Get the upper left corner
1119  double upperLeftX = mapGroup["UpperLeftCornerX"];
1120  double upperLeftY = mapGroup["UpperLeftCornerY"];
1121 
1122  // Initialize the rest of the projection
1123  proj = (Isis::TProjection *) Create(label, true);
1124 
1125  // Create a mapper to transform pixels into projection x/y and vice versa
1126  PFPixelMapper *pixelMapper = new PFPixelMapper(pixelResolution, upperLeftX, upperLeftY);
1127  proj->SetWorldMapper(pixelMapper);
1128 
1129  proj->SetUpperLeftCorner(Displacement(upperLeftX, Displacement::Meters),
1130  Displacement(upperLeftY, Displacement::Meters));
1131  }
1132  catch (IException &e) {
1133  QString msg = "Unable to initialize cube projection";
1134  if (label.fileName() != "") msg += " from file [" + label.fileName() + "]";
1135  throw IException(e, IException::Unknown, msg, _FILEINFO_);
1136  }
1137  return (Isis::Projection *) proj;
1138  }
1139 
1140 
1150  Isis::Projection *ProjectionFactory::RingsCreateFromCube(Isis::Pvl &label) {
1152  try {
1153  // Get the pixel resolution
1154  Isis::PvlGroup &mapGroup = label.findGroup("Mapping", Isis::Pvl::Traverse);
1155  double pixelResolution = mapGroup["PixelResolution"];
1156 
1157  // Get the upper left corner
1158  double upperLeftX = mapGroup["UpperLeftCornerX"];
1159  double upperLeftY = mapGroup["UpperLeftCornerY"];
1160 
1161  // Initialize the rest of the projection
1162  proj = (Isis::RingPlaneProjection *) RingsCreate(label, true);
1163 
1164  // Create a mapper to transform pixels into projection x/y and vice versa
1165  PFPixelMapper *pixelMapper = new PFPixelMapper(pixelResolution, upperLeftX, upperLeftY);
1166  proj->SetWorldMapper(pixelMapper);
1167 
1168  proj->SetUpperLeftCorner(Displacement(upperLeftX, Displacement::Meters),
1169  Displacement(upperLeftY, Displacement::Meters));
1170  }
1171  catch (IException &e) {
1172  QString msg = "Unable to initialize cube projection";
1173  if (label.fileName() != "") msg += " from file [" + label.fileName() + "]";
1174  throw IException(e, IException::Unknown, msg, _FILEINFO_);
1175  }
1176  return (Isis::Projection *) proj;
1177  }
1178 } //end namespace isis
double meters() const
Get the distance in meters.
Definition: Distance.cpp:97
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:110
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:141
bool SetUniversalGround(const double lat, const double lon)
This method is used to set the latitude/longitude which must be Planetocentric (latitude) and Positiv...
File name manipulation and expansion.
Definition: FileName.h:116
const double PI
The mathematical constant PI.
Definition: Constants.h:56
double UniversalLatitude() const
Returns the planetocentric latitude, in degrees, at the surface intersection point in the body fixed ...
Definition: Sensor.cpp:225
Base class for Map TProjections.
Definition: TProjection.h:182
void append(const IException &exceptionSource)
Appends the given exception (and its list of previous exceptions) to this exception&#39;s causational exc...
Definition: IException.cpp:425
Namespace for the standard library.
void SetWorldMapper(WorldMapper *mapper)
If desired the programmer can use this method to set a world mapper to be used in the SetWorld...
Definition: Projection.cpp:489
Search child objects.
Definition: PvlObject.h:170
bool IsGood() const
This indicates if the last invocation of SetGround, SetCoordinate, SetUniversalGround, or SetWorld was with successful or not.
Definition: Projection.cpp:389
double XCoord() const
This returns the projection X provided SetGround, SetCoordinate, SetUniversalGround, or SetWorld returned with success.
Definition: Projection.cpp:402
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
void addKeyword(const PvlKeyword &keyword, const InsertMode mode=Append)
Add a keyword to the container.
Loads plugins from a shared library.
Definition: Plugin.h:71
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
bool hasObject(const QString &name) const
Returns a boolean value based on whether the object exists in the current PvlObject or not...
Definition: PvlObject.h:335
Distance LocalRadius() const
Returns the local radius at the intersection point.
Definition: Sensor.cpp:282
bool SetUniversalGround(const double latitude, const double longitude)
Sets the lat/lon values to get the sample/line values.
Definition: Camera.cpp:396
bool SetImage(const double sample, const double line)
Sets the sample/line values of the image to get the lat/lon values.
Definition: Camera.cpp:170
Base class for Map Projections.
Definition: Projection.h:171
bool HasGroundRange() const
This indicates if the longitude direction type is positive west (as opposed to postive east)...
Definition: Projection.cpp:364
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
double Sample()
Returns the current sample number.
Definition: Camera.cpp:2702
A single keyword-value pair.
Definition: PvlKeyword.h:98
virtual double TrueScaleRingRadius() const
This method returns the radius of true scale.
double LocalRadius(double lat) const
This method returns the local radius in meters at the specified latitude position.
double YCoord() const
This returns the projection Y provided SetGround, SetCoordinate, SetUniversalGround, or SetWorld returned with success.
Definition: Projection.cpp:415
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition: FileName.cpp:212
virtual bool SetGround(const double ringRadius, const double ringLongitude)
This method is used to set the ring radius/longitude (assumed to be of the correct LatitudeType...
Container for cube-like labels.
Definition: Pvl.h:135
double UniversalLongitude() const
Returns the positive east, 0-360 domain longitude, in degrees, at the surface intersection point in t...
Definition: Sensor.cpp:248
void SetUpperLeftCorner(const Displacement &x, const Displacement &y)
This method is used to find the XY range for oblique aspect projections (non-polar projections) by "w...
virtual bool IsBandIndependent()
Virtual method that checks if the band is independent.
Definition: Camera.cpp:2661
Base class for Map Projections of plane shapes.
Pvl * label() const
Returns a pointer to the IsisLabel object associated with the cube.
Definition: Cube.cpp:1346
int Bands() const
Returns the number of bands in the image.
Definition: Camera.cpp:2808
Displacement is a signed length, usually in meters.
Definition: Displacement.h:43
double Line()
Returns the current line number.
Definition: Camera.cpp:2722
int Lines() const
Returns the number of lines in the image.
Definition: Camera.cpp:2798
int Samples() const
Returns the number of samples in the image.
Definition: Camera.cpp:2788
QString fileName() const
Returns the filename used to initialise the Pvl object.
Definition: PvlContainer.h:246
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
virtual bool XYRange(double &minX, double &maxX, double &minY, double &maxY)
This method is used to determine the x/y range which completely covers the area of interest specified...
virtual void SetBand(const int band)
Virtual method that sets the band number.
Definition: Camera.cpp:2692
bool fileExists() const
Returns true if the file exists; false otherwise.
Definition: FileName.cpp:465
virtual double TrueScaleLatitude() const
This method returns the latitude of true scale.
IO Handler for Isis Cubes.
Definition: Cube.h:170
virtual bool XYRange(double &minX, double &maxX, double &minY, double &maxY)
This method is used to determine the x/y range which completely covers the area of interest specified...