Isis 3 Programmer Reference
GroundGrid.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 #include "GroundGrid.h"
8 
9 #include <cmath>
10 #include <iomanip>
11 
12 #include <QVector>
13 
14 #include "Angle.h"
15 #include "UniversalGroundMap.h"
16 #include "Camera.h"
17 #include "Distance.h"
18 #include "Latitude.h"
19 #include "Longitude.h"
20 #include "Progress.h"
21 #include "Projection.h"
22 #include "ProjectionFactory.h"
23 
24 using namespace std;
25 
26 namespace Isis {
40  GroundGrid::GroundGrid(UniversalGroundMap *gmap,
41  bool splitLatLon,
42  bool extendGrid,
43  unsigned int width,
44  unsigned int height) {
45  p_width = width;
46  p_height = height;
47  m_extendGrid = extendGrid;
48 
49  // Initialize our grid pointer to null
50  p_grid = 0;
51  p_latLinesGrid = 0;
52  p_lonLinesGrid = 0;
53 
54  // Now let's figure out how big the grid needs to be, then allocate and
55  // initialize
56  p_gridSize = (unsigned long)(ceil(width * height / 8.0) + 0.5);
57 
58  if (!splitLatLon) {
59  p_grid = new char[p_gridSize];
60  }
61  else {
62  p_latLinesGrid = new char[p_gridSize];
63  p_lonLinesGrid = new char[p_gridSize];
64  }
65 
66  for (unsigned long i = 0; i < p_gridSize; i++) {
67  if (p_grid) p_grid[i] = 0;
68  if (p_latLinesGrid) p_latLinesGrid[i] = 0;
69  if (p_lonLinesGrid) p_lonLinesGrid[i] = 0;
70  }
71 
72  // The first call of CreateGrid doesn't have to reinitialize
73  p_reinitialize = false;
74 
75  p_groundMap = gmap;
76 
77  // We need a lat/lon range for gridding, use the mapping group
78  // (in case of camera, use BasicMapping)
79  p_minLat = NULL;
80  p_minLon = NULL;
81  p_maxLat = NULL;
82  p_maxLon = NULL;
83 
84  p_mapping = new PvlGroup;
85 
86  if (p_groundMap->Camera()) {
87  Pvl tmp;
88  p_groundMap->Camera()->BasicMapping(tmp);
89  *p_mapping = tmp.findGroup("Mapping");
90  }
91  else {
92  *p_mapping = p_groundMap->Projection()->Mapping();
93  }
94 
95  Distance radius1 = Distance((double)(*p_mapping)["EquatorialRadius"],
96  Distance::Meters);
97  Distance radius2 = Distance((double)(*p_mapping)["PolarRadius"],
98  Distance::Meters);
99 
100  if (p_mapping->hasKeyword("MinimumLatitude")) {
101  p_minLat = new Latitude(toDouble((*p_mapping)["MinimumLatitude"][0]),
102  *p_mapping,
103  Angle::Degrees,
104  Latitude::AllowPastPole);
105  }
106  else {
107  p_minLat = new Latitude;
108  }
109 
110  if (p_mapping->hasKeyword("MaximumLatitude")) {
111  p_maxLat = new Latitude(toDouble((*p_mapping)["MaximumLatitude"][0]),
112  *p_mapping,
113  Angle::Degrees);
114  }
115  else {
116  p_maxLat = new Latitude;
117  }
118 
119  if (p_mapping->hasKeyword("MinimumLongitude")) {
120  p_minLon = new Longitude(toDouble((*p_mapping)["MinimumLongitude"][0]),
121  *p_mapping,
122  Angle::Degrees);
123  }
124  else {
125  p_minLon = new Longitude;
126  }
127 
128  if (p_mapping->hasKeyword("MaximumLongitude")) {
129  p_maxLon = new Longitude(toDouble((*p_mapping)["MaximumLongitude"][0]),
130  *p_mapping,
131  Angle::Degrees);
132  }
133  else {
134  p_maxLon = new Longitude;
135  }
136 
137  if (p_minLon->isValid() && p_maxLon->isValid()) {
138  if (*p_minLon > *p_maxLon) {
139  Longitude tmp(*p_minLon);
140  *p_minLon = *p_maxLon;
141  *p_maxLon = tmp;
142  }
143  }
144 
145  Distance largerRadius = max(radius1, radius2);
146 
147  // p_defaultResolution is in degrees/pixel
148 
149  if (p_groundMap->HasCamera()) {
150  p_defaultResolution =
151  (p_groundMap->Camera()->HighestImageResolution() /
152  largerRadius.meters()) * 10;
153  }
154  else {
155  p_defaultResolution = (p_groundMap->Resolution() /
156  largerRadius.meters()) * 10;
157  }
158 
159  if (p_defaultResolution < 0) {
160  p_defaultResolution = 10.0 / largerRadius.meters();
161  }
162  }
163 
167  GroundGrid::~GroundGrid() {
168  if (p_minLat) {
169  delete p_minLat;
170  p_minLat = NULL;
171  }
172 
173  if (p_minLon) {
174  delete p_minLon;
175  p_minLon = NULL;
176  }
177 
178  if (p_maxLat) {
179  delete p_maxLat;
180  p_maxLat = NULL;
181  }
182 
183  if (p_maxLon) {
184  delete p_maxLon;
185  p_maxLon = NULL;
186  }
187 
188  if (p_mapping) {
189  delete p_mapping;
190  p_mapping = NULL;
191  }
192 
193  if (p_grid) {
194  delete[] p_grid;
195  p_grid = NULL;
196  }
197 
198  if (p_latLinesGrid) {
199  delete[] p_latLinesGrid;
200  p_latLinesGrid = NULL;
201  }
202 
203  if (p_lonLinesGrid) {
204  delete[] p_lonLinesGrid;
205  p_lonLinesGrid = NULL;
206  }
207  }
208 
218  void GroundGrid::CreateGrid(Latitude baseLat,
219  Longitude baseLon,
220  Angle latInc,
221  Angle lonInc,
222  Progress *progress) {
223  CreateGrid(baseLat, baseLon, latInc, lonInc, progress, Angle(), Angle());
224  }
225 
226 
239  void GroundGrid::CreateGrid(Latitude baseLat,
240  Longitude baseLon,
241  Angle latInc,
242  Angle lonInc,
243  Progress *progress,
244  Angle latRes,
245  Angle lonRes) {
246  if (p_groundMap == NULL ||
247  (p_grid == NULL && p_latLinesGrid == NULL && p_lonLinesGrid == NULL)) {
248  IString msg = "GroundGrid::CreateGrid missing ground map or grid array";
249  throw IException(IException::Programmer, msg, _FILEINFO_);
250  }
251 
252  if (p_reinitialize) {
253  for (unsigned long i = 0; i < p_gridSize; i++) {
254  if (p_grid) p_grid[i] = 0;
255  if (p_latLinesGrid) p_latLinesGrid[i] = 0;
256  if (p_lonLinesGrid) p_lonLinesGrid[i] = 0;
257  }
258  }
259 
260  // Verify lat/lon range is okay
261  bool badLatLonRange = false;
262  QVector<IString> badLatLonValues;
263  if (!p_minLat || !p_minLat->isValid()) {
264  badLatLonValues.append("MinimumLatitude");
265  badLatLonRange = true;
266  }
267 
268  if (!p_maxLat || !p_maxLat->isValid()) {
269  badLatLonValues.append("MaximumLatitude");
270  badLatLonRange = true;
271  }
272 
273  if (!p_minLon || !p_minLon->isValid()) {
274  badLatLonValues.append("MinimumLongitude");
275  badLatLonRange = true;
276  }
277 
278  if (!p_maxLon || !p_maxLon->isValid()) {
279  badLatLonValues.append("MaximumLongitude");
280  badLatLonRange = true;
281  }
282 
283 
284  if (badLatLonRange) {
285  IString msg = "Could not determine values for [";
286  for (int i = 0; i < badLatLonValues.size(); i++) {
287  if (i != 0)
288  msg += ",";
289 
290  msg += badLatLonValues[i];
291  }
292 
293  msg += "], please specify them explicitly";
294 
295  // I chose parse because it's not really the user's fault or the
296  // programmer's. It's a stripped keyword in a Pvl.
297  throw IException(IException::Unknown, msg, _FILEINFO_);
298  }
299 
300  // subsequent calls to this method must always reinitialize the grid
301  p_reinitialize = true;
302 
303  // Find starting points for lat/lon
304  Latitude startLat = Latitude(baseLat - Angle(floor((baseLat - *p_minLat) / latInc) * latInc),
305  *GetMappingGroup(),
306  Latitude::AllowPastPole);
307 
308  Longitude startLon = Longitude(baseLon - Angle(floor((baseLon - *p_minLon) / lonInc) * lonInc));
309 
310  if (!latRes.isValid() || latRes <= Angle(0, Angle::Degrees)) {
311  latRes = Angle(p_defaultResolution,
312  Angle::Degrees);
313  }
314 
315  if (!lonRes.isValid() || lonRes <= Angle(0, Angle::Degrees)) {
316  lonRes = Angle(p_defaultResolution,
317  Angle::Degrees);
318  }
319 
320  Latitude endLat = Latitude((long)((*p_maxLat - startLat) / latInc) * latInc + startLat,
321  *GetMappingGroup());
322  Longitude endLon =
323  (long)((*p_maxLon - startLon) / lonInc) * lonInc + startLon;
324 
325  if (progress) {
326  double numSteps = (double)((endLat - startLat) / latInc) + 1;
327  numSteps += (double)((endLon - startLon) / lonInc) + 1;
328 
329  if (numSteps <= 0) {
330  IString msg = "No gridlines would intersect the image";
331  throw IException(IException::Programmer, msg, _FILEINFO_);
332  }
333 
334  progress->SetMaximumSteps((long)(numSteps + 0.5));
335  progress->CheckStatus();
336  }
337 
338  // Ensure that the Latitude being incremented does not throw an exception
339  // if incremented past -90 or 90 degrees.
340  Latitude latStep = startLat;
341  latStep.setErrorChecking(Latitude::AllowPastPole);
342  for (; latStep <= endLat + latInc / 2; latStep += latInc) {
343  unsigned int previousX = 0;
344  unsigned int previousY = 0;
345  bool havePrevious = false;
346 
347  for (Longitude lon = *p_minLon; lon <= *p_maxLon; lon += latRes) {
348  unsigned int x = 0;
349  unsigned int y = 0;
350  bool valid = GetXY(latStep, lon, x, y);
351 
352  if (valid && havePrevious) {
353  if (previousX != x || previousY != y) {
354  DrawLineOnGrid(previousX, previousY, x, y, true);
355  }
356  }
357 
358  havePrevious = valid;
359  previousX = x;
360  previousY = y;
361  }
362 
363  if (progress) {
364  progress->CheckStatus();
365  }
366  }
367 
368  for (Longitude lon = startLon; lon <= endLon + lonInc / 2; lon += lonInc) {
369  unsigned int previousX = 0;
370  unsigned int previousY = 0;
371  bool havePrevious = false;
372 
373  // Ensure that the Latitude being incremented does not throw an exception
374  // if incremented past -90 or 90 degrees.
375  latStep = *p_minLat;
376  latStep.setErrorChecking(Latitude::AllowPastPole);
377  for (; latStep <= *p_maxLat; latStep += lonRes) {
378  unsigned int x = 0;
379  unsigned int y = 0;
380 
381  bool valid = GetXY(latStep, lon, x, y);
382 
383  if (valid && havePrevious) {
384  if (previousX == x && previousY == y) {
385  continue;
386  }
387 
388  DrawLineOnGrid(previousX, previousY, x, y, false);
389  }
390 
391  havePrevious = valid;
392  previousX = x;
393  previousY = y;
394  }
395 
396  if (progress) {
397  progress->CheckStatus();
398  }
399  }
400  }
401 
402 
411  void GroundGrid::SetGroundLimits(Latitude minLat, Longitude minLon,
412  Latitude maxLat, Longitude maxLon) {
413  if (minLat.isValid()) *p_minLat = minLat;
414  if (maxLat.isValid()) *p_maxLat = maxLat;
415  if (minLon.isValid()) *p_minLon = minLon;
416  if (maxLon.isValid()) *p_maxLon = maxLon;
417 
418  if (p_minLat->isValid() && p_maxLat->isValid() && *p_minLat > *p_maxLat) {
419  Latitude tmp(*p_minLat);
420  *p_minLat = *p_maxLat;
421  *p_maxLat = tmp;
422  }
423 
424  if (p_minLon->isValid() && p_maxLon->isValid() && *p_minLon > *p_maxLon) {
425  Longitude tmp(*p_minLon);
426  *p_minLon = *p_maxLon;
427  *p_maxLon = tmp;
428  }
429  }
430 
434  void GroundGrid::WalkBoundary() {
435  Angle latRes = Angle(p_defaultResolution, Angle::Degrees);
436  Angle lonRes = Angle(p_defaultResolution, Angle::Degrees);
437 
438  const Latitude &minLat = *p_minLat;
439  const Latitude &maxLat = *p_maxLat;
440  const Longitude &minLon = *p_minLon;
441  const Longitude &maxLon = *p_maxLon;
442 
443  // Walk the minLat/maxLat lines
444  // Ensure that the Latitude being incremented does not throw an exception
445  // if incremented past -90 or 90 degrees.
446  Latitude latStep = minLat;
447  latStep.setErrorChecking(Latitude::AllowPastPole);
448  for (; latStep <= maxLat; latStep += (maxLat - minLat)) {
449  unsigned int previousX = 0;
450  unsigned int previousY = 0;
451  bool havePrevious = false;
452 
453  for (Longitude lon = minLon; lon <= maxLon; lon += latRes) {
454  unsigned int x = 0;
455  unsigned int y = 0;
456  bool valid = GetXY(latStep, lon, x, y);
457 
458  if (valid && havePrevious) {
459  if (previousX != x || previousY != y) {
460  DrawLineOnGrid(previousX, previousY, x, y, true);
461  }
462  }
463 
464  havePrevious = valid;
465  previousX = x;
466  previousY = y;
467  }
468  }
469 
470  // Walk the minLon/maxLon lines
471  for (Longitude lon = minLon; lon <= maxLon; lon += (maxLon - minLon)) {
472  unsigned int previousX = 0;
473  unsigned int previousY = 0;
474  bool havePrevious = false;
475 
476  // Ensure that the Latitude being incremented does not throw an exception
477  // if incremented past -90 or 90 degrees.
478  latStep = minLat;
479  latStep.setErrorChecking(Latitude::AllowPastPole);
480  for (; latStep <= maxLat; latStep += lonRes) {
481  unsigned int x = 0;
482  unsigned int y = 0;
483  bool valid = GetXY(latStep, lon, x, y);
484 
485  if (valid && havePrevious) {
486  if (previousX != x || previousY != y) {
487  DrawLineOnGrid(previousX, previousY, x, y, false);
488  }
489  }
490 
491  havePrevious = valid;
492  previousX = x;
493  previousY = y;
494  }
495  }
496  }
497 
498 
510  bool GroundGrid::PixelOnGrid(int x, int y, bool latGrid) {
511  if (x < 0) return false;
512  if (y < 0) return false;
513 
514  if (x >= (int)p_width) return false;
515  if (y >= (int)p_height) return false;
516 
517  return GetGridBit(x, y, latGrid);
518  }
519 
520 
529  bool GroundGrid::PixelOnGrid(int x, int y) {
530  if (x < 0) return false;
531  if (y < 0) return false;
532 
533  if (x >= (int)p_width) return false;
534  if (y >= (int)p_height) return false;
535 
536  return GetGridBit(x, y, true); // third argument shouldnt matter
537  }
538 
539 
546  PvlGroup *GroundGrid::GetMappingGroup() {
547  return p_mapping;
548  }
549 
550 
557  Latitude GroundGrid::minLatitude() const {
558  if (p_minLat) {
559  return *p_minLat;
560  }
561  return Latitude();
562  }
563 
564 
571  Longitude GroundGrid::minLongitude() const {
572  if (p_minLon) {
573  return *p_minLon;
574  }
575  return Longitude();
576  }
577 
578 
585  Latitude GroundGrid::maxLatitude() const {
586  if (p_maxLat) {
587  return *p_maxLat;
588  }
589  return Latitude();
590  }
591 
592 
599  Longitude GroundGrid::maxLongitude() const {
600  if (p_maxLon) {
601  return *p_maxLon;
602  }
603  return Longitude();
604  }
605 
606 
618  bool GroundGrid::GetXY(Latitude lat, Longitude lon,
619  unsigned int &x, unsigned int &y) {
620  if (!GroundMap()) return false;
621  if (m_extendGrid) {
622  if (!GroundMap()->SetUnboundGround(lat, lon)) return false;
623  }
624  else {
625  if (!GroundMap()->SetGround(lat, lon)) return false;
626  }
627  if (p_groundMap->Sample() < 0.5 || p_groundMap->Line() < 0.5) return false;
628  if (p_groundMap->Sample() < 0.5 || p_groundMap->Line() < 0.5) return false;
629 
630  x = (int)(p_groundMap->Sample() - 0.5);
631  y = (int)(p_groundMap->Line() - 0.5);
632 
633  if (x >= p_width || y >= p_height) return false;
634 
635  return true;
636  }
637 
638 
643  UniversalGroundMap *GroundGrid::GroundMap() {
644  return p_groundMap;
645  }
646 
647 
655  void GroundGrid::SetGridBit(unsigned int x, unsigned int y, bool latGrid) {
656  unsigned long bitPosition = (y * p_width) + x;
657  unsigned long byteContainer = bitPosition / 8;
658  unsigned int bitOffset = bitPosition % 8;
659 
660  if (byteContainer > p_gridSize) return;
661 
662  if (p_grid) {
663  char &importantByte = p_grid[byteContainer];
664  importantByte |= (1 << bitOffset);
665  }
666  else if (latGrid && p_latLinesGrid) {
667  char &importantByte = p_latLinesGrid[byteContainer];
668  importantByte |= (1 << bitOffset);
669  }
670  else if (!latGrid && p_lonLinesGrid) {
671  char &importantByte = p_lonLinesGrid[byteContainer];
672  importantByte |= (1 << bitOffset);
673  }
674  else {
675  IString msg = "GroundGrid::SetGridBit no grids available";
676  throw IException(IException::Programmer, msg, _FILEINFO_);
677  }
678  }
679 
680 
691  bool GroundGrid::GetGridBit(unsigned int x, unsigned int y, bool latGrid) {
692  unsigned long bitPosition = (y * p_width) + x;
693  unsigned long byteContainer = bitPosition / 8;
694  unsigned int bitOffset = bitPosition % 8;
695 
696  if (byteContainer > p_gridSize) return false;
697 
698  bool result = false;
699 
700  if (p_grid) {
701  char &importantByte = p_grid[byteContainer];
702  result = (importantByte >> bitOffset) & 1;
703  }
704  else if (latGrid && p_latLinesGrid) {
705  char &importantByte = p_latLinesGrid[byteContainer];
706  result = (importantByte >> bitOffset) & 1;
707  }
708  else if (!latGrid && p_lonLinesGrid) {
709  char &importantByte = p_lonLinesGrid[byteContainer];
710  result = (importantByte >> bitOffset) & 1;
711  }
712  else {
713  IString msg = "GroundGrid::GetGridBit no grids available";
714  throw IException(IException::Programmer, msg, _FILEINFO_);
715  }
716 
717  return result;
718  }
719 
720 
730  void GroundGrid::DrawLineOnGrid(unsigned int x1, unsigned int y1,
731  unsigned int x2, unsigned int y2,
732  bool isLatLine) {
733  int dx = x2 - x1;
734  int dy = y2 - y1;
735 
736  SetGridBit(x1, y1, isLatLine);
737 
738  if (dx != 0) {
739  double m = (double)dy / (double)dx;
740  double b = y1 - m * x1;
741 
742  dx = (x2 > x1) ? 1 : -1;
743  while (x1 != x2) {
744  x1 += dx;
745  y1 = (int)(m * x1 + b + 0.5);
746  SetGridBit(x1, y1, isLatLine);
747  }
748  }
749  }
750 };
Isis::PvlObject::findGroup
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:129
Isis::UniversalGroundMap
Universal Ground Map.
Definition: UniversalGroundMap.h:69
Isis::Progress::CheckStatus
void CheckStatus()
Checks and updates the status.
Definition: Progress.cpp:105
Isis::Latitude
This class is designed to encapsulate the concept of a Latitude.
Definition: Latitude.h:51
Isis::Progress::SetMaximumSteps
void SetMaximumSteps(const int steps)
This sets the maximum number of steps in the process.
Definition: Progress.cpp:85
Isis::Pvl
Container for cube-like labels.
Definition: Pvl.h:119
Isis::Distance
Distance measurement, usually in meters.
Definition: Distance.h:34
Isis::Longitude
This class is designed to encapsulate the concept of a Longitude.
Definition: Longitude.h:40
Isis::Latitude::setErrorChecking
void setErrorChecking(ErrorChecking errors)
Set the error checking status.
Definition: Latitude.cpp:420
Isis::PvlGroup
Contains multiple PvlContainers.
Definition: PvlGroup.h:41
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::Angle
Defines an angle and provides unit conversions.
Definition: Angle.h:45
Isis::Progress
Program progress reporter.
Definition: Progress.h:42
Isis::toDouble
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:149
std
Namespace for the standard library.
Isis::Angle::isValid
bool isValid() const
This indicates whether we have a legitimate angle stored or are in an unset, or invalid,...
Definition: Angle.cpp:95
Isis::Distance::meters
double meters() const
Get the distance in meters.
Definition: Distance.cpp:85
Isis::IString
Adds specific functionality to C++ strings.
Definition: IString.h:165
QVector
This is free and unencumbered software released into the public domain.
Definition: Calculator.h:18
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16