Isis 3 Programmer Reference
GridPolygonSeeder.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 
8 #include <string>
9 #include <vector>
10 #include <cmath>
11 
12 #include "Pvl.h"
13 #include "PvlGroup.h"
14 #include "IException.h"
15 #include "PolygonTools.h"
16 #include "IString.h"
17 
18 #include "GridPolygonSeeder.h"
19 
20 namespace Isis {
21 
30  Parse(pvl);
31  };
32 
33 
52  std::vector<geos::geom::Point *> GridPolygonSeeder::Seed(const geos::geom::MultiPolygon *lonLatPoly) {
53  //Projection *proj) {
54  /*if (proj == NULL) {
55  QString msg = "No Projection object available";
56  throw iException::Message(iException::Programmer, msg, _FILEINFO_);
57  }*/
58 
59  if(!p_subGrid)
60  //return SeedGrid(lonLatPoly, proj);
61  return SeedGrid(lonLatPoly);
62  else
63  //return SeedSubGrid(lonLatPoly, proj);
64  return SeedSubGrid(lonLatPoly);
65 
66  }
67 
68  std::vector<geos::geom::Point *> GridPolygonSeeder::SeedGrid(const geos::geom::MultiPolygon *multiPoly) {
69 
70  // Storage for the points to be returned
71  std::vector<geos::geom::Point *> points;
72 
73  // Create some things we will need shortly
74  const geos::geom::Envelope *polyBoundBox = multiPoly->getEnvelopeInternal();
75 
76  // Call the parents standardTests member
77  QString msg = StandardTests(multiPoly, polyBoundBox);
78  if(!msg.isEmpty()) {
79  return points;
80  }
81 
82  // Do grid specific tests to make sure this poly should be seeded
83  // (none for now)
84 
85  // Starting at the centroid of the xy polygon populate the polygon with a
86  // grid of points with the requested spacing
87  geos::geom::Point *centroid = multiPoly->getCentroid();
88  double centerX = centroid->getX();
89  double centerY = centroid->getY();
90  delete centroid;
91 
92  int xStepsLeft = (int)((centerX - polyBoundBox->getMinX()) / p_Xspacing + 0.5);
93  int yStepsLeft = (int)((centerY - polyBoundBox->getMinY()) / p_Yspacing + 0.5);
94  double dRealMinX = centerX - (xStepsLeft * p_Xspacing);
95  double dRealMinY = centerY - (yStepsLeft * p_Yspacing);
96 
97  for(double y = dRealMinY; y <= polyBoundBox->getMaxY(); y += p_Yspacing) {
98  for(double x = dRealMinX; x <= polyBoundBox->getMaxX(); x += p_Xspacing) {
99  geos::geom::Coordinate c(x, y);
100  geos::geom::Point *p = Isis::globalFactory->createPoint(c);
101 
102  if(p->within(multiPoly)) {
103  points.push_back(Isis::globalFactory->createPoint(c));
104  }
105  else {
106  delete p;
107  }
108  }
109  }
110 
111  return points;
112  }
113 
124  std::vector<geos::geom::Point *> GridPolygonSeeder::SeedSubGrid(const geos::geom::MultiPolygon *multiPoly) {
125  //Projection *proj) {
126  // Storage for the points to be returned
127  std::vector<geos::geom::Point *> points;
128 
129  // Create some things we will need shortly
130  //geos::geom::MultiPolygon *xymp = PolygonTools::LatLonToXY(*lonLatPoly, proj);
131  const geos::geom::Envelope *polyBoundBox = multiPoly->getEnvelopeInternal();
132 
133  // Call the parents standardTests member
134  QString msg = StandardTests(multiPoly, polyBoundBox);
135  if(!msg.isEmpty()) {
136  return points;
137  }
138 
139  geos::geom::Point *centroid = multiPoly->getCentroid();
140  double centerX = centroid->getX();
141  double centerY = centroid->getY();
142  delete centroid;
143 
144  // Do grid specific tests to make sure this poly should be seeded
145  // (none for now)
146 
155  enum PointStatus {
156  pointShouldCheck,
157  pointShouldSubGridCheck,
158  pointFound,
159  pointNotFound,
160  pointCantFind
161  };
162 
163  // For maintaining an idea of what's going on in this polygon, we needs to know the dimensions
164  // of the grid.
165  int xSteps = (int)((polyBoundBox->getMaxX() - polyBoundBox->getMinX()) / p_Xspacing + 1.5);
166  int ySteps = (int)((polyBoundBox->getMaxY() - polyBoundBox->getMinY()) / p_Yspacing + 1.5);
167  PointStatus pointCheck[xSteps][ySteps];
168 
169  // Initialize our grid of point status'
170  for(int y = 0; y < ySteps; y++) {
171  for(int x = 0; x < xSteps; x++) {
172  pointCheck[x][y] = pointShouldCheck;
173  }
174  }
175 
184  int precision = (int)pow(0.5 / MinimumThickness(), 0.5) * 2;
185  bool bGridCleared = true;
186  int xStepsToCentroid = (int)((centerX - polyBoundBox->getMinX()) / p_Xspacing + 0.5);
187  int yStepsToCentroid = (int)((centerY - polyBoundBox->getMinY()) / p_Yspacing + 0.5);
188  double dRealMinX = centerX - (xStepsToCentroid * p_Xspacing);
189  double dRealMinY = centerY - (yStepsToCentroid * p_Yspacing);
190 
191  do {
192  // gridCleared is true if we did nothing, if we performed any actions on the grid
193  // it becomes false and another pass should be used.
194  bGridCleared = true;
195 
196  for(int y = 0; y < ySteps; y++) {
197  double centerY = dRealMinY + p_Yspacing * y;
198  for(int x = 0; x < xSteps; x++) {
199  double centerX = dRealMinX + p_Xspacing * x;
200  geos::geom::Point *p = NULL;
201 
202  // pointShouldCheck tells us we need to check center. Calling
203  // CheckSubGrid with precision=0 will do this for us.
204  if(pointCheck[x][y] == pointShouldCheck) {
205  p = CheckSubGrid(*multiPoly, centerX, centerY, 0);
206  }
207  // pointShouldSubGridCheck tells us we're next to a grid
208  // square where a point was found, so check in depth
209  else if(pointCheck[x][y] == pointShouldSubGridCheck) {
210  p = CheckSubGrid(*multiPoly, centerX, centerY, precision);
211  }
212 
213  // If we found a point, verify we can setCoordinate and save the point
214  if(p != NULL) {
215  // Convert the x/y point to a lon/lat point
216  /*if (proj->SetCoordinate(p->getX(),p->getY())) {
217  points.push_back(Isis::globalFactory->createPoint(
218  geos::geom::Coordinate(proj->UniversalLongitude(),
219  proj->UniversalLatitude())));
220  }
221  else {
222  IString msg = "Unable to convert [(" + IString(x) + ",";
223  msg += IString(y) + ")] to a (lon,lat)";
224  throw iException::Message(iException::Programmer, msg, _FILEINFO_);
225  }*/
226  points.push_back(Isis::globalFactory->createPoint(
227  geos::geom::Coordinate(p->getX(), p->getY())));
228 
229  // We found something new and need a new pass
230  bGridCleared = false;
231  pointCheck[x][y] = pointFound;
232  }
233  else {
234  if(pointCheck[x][y] == pointShouldCheck) {
235  pointCheck[x][y] = pointNotFound;
236  }
237  else if(pointCheck[x][y] == pointShouldSubGridCheck) {
238  pointCheck[x][y] = pointCantFind;
239  }
240  }
241  }
242  }
243 
244  // now that the grid has been updated with it's founds, we can look for subgrid checks
245  for(int y = 0; y < ySteps; y++) {
246  for(int x = 0; x < xSteps; x++) {
247  if(pointCheck[x][y] == pointFound) {
248  for(int yOff = -1; yOff <= 1; yOff++) {
249  for(int xOff = -1; xOff <= 1; xOff++) {
250  if(x + xOff >= 0 && x + xOff < xSteps &&
251  y + yOff >= 0 && y + yOff < ySteps &&
252  pointCheck[x+xOff][y+yOff] == pointNotFound) {
253 
254  pointCheck[x+xOff][y+yOff] = pointShouldSubGridCheck;
255 
256  // We need to do a searchso we need another pass
257  bGridCleared = false;
258  }
259  }
260  }
261  }
262  }
263  }
264 
265  }
266  while(!bGridCleared);
267 
268  return points;
269  }
270 
292  geos::geom::Point *GridPolygonSeeder::CheckSubGrid(const geos::geom::MultiPolygon &xymp, const double &centerX,
293  const double &centerY, const int &precision) {
294  // We'll make a 2D array detailing which points to check, and which not to, in this rectangle.
295  // Figure out how many points across and vertically we need to check
296  int gridSize = 1;
297  for(int prec = 0; prec < precision && prec < 6; prec ++) {
298  // Maybe solve the recurrence relation for a single equation??
299  gridSize = gridSize * 2 + 1;
300  }
301 
302  // These are the possible values in which the 2D array can be. We need the transition value
303  // gridNewCheckPt to not count new points as old points.
304  enum GridPoint {
305  gridEmpty,
306  gridNewCheckPt,
307  gridCheckPt
308  };
309 
310  GridPoint grid[gridSize][gridSize];
311 
312  for(int y = 0; y < gridSize; y++) {
313  for(int x = 0; x < gridSize; x++) {
314  grid[x][y] = gridEmpty;
315  }
316  }
317 
318  // Precision 0: Always center, this is always true
319  grid[gridSize/2][gridSize/2] = gridCheckPt;
320 
321  // now populate the grid with what we wish to check for
322  for(int prec = 0; prec < precision; prec ++) {
323  // This tells us how far over in the 2D array to go at a precision from already found points
324  int checkDist = (gridSize + 1) / (int)(4 * (pow(2.0, prec)) + 0.5);
325 
326  // Search the grid for already found points and set everything checkDist away to be checked too
327  for(int y = 0; y < gridSize; y++) {
328  for(int x = 0; x < gridSize; x++) {
329  if(grid[x][y] == gridCheckPt) {
330  // We should never overwrite found points, the checkDist should assure this wont happen
331  if(x - checkDist > 0) grid[x-checkDist][y] = gridNewCheckPt;
332  if(y - checkDist > 0) grid[x][y-checkDist] = gridNewCheckPt;
333  if(x + checkDist < gridSize) grid[x+checkDist][y] = gridNewCheckPt;
334  if(y + checkDist < gridSize) grid[x][y+checkDist] = gridNewCheckPt;
335  }
336  }
337  }
338 
339  // Convert temporary check pt to final.
340  // Do this separately to avoid changing the data we're processing
341  for(int y = 0; y < gridSize; y++) {
342  for(int x = 0; x < gridSize; x++) {
343  if(grid[x][y] == gridNewCheckPt) grid[x][y] = gridCheckPt;
344  }
345  }
346  }
347 
348  // We now have a grid of points to check inside this grid square. Figure out the
349  // distance each of these subsquare pixels are worth.
350  double deltaXSize = p_Xspacing / (gridSize + 1);
351  double deltaYSize = p_Yspacing / (gridSize + 1);
352 
353  geos::geom::Point *result = NULL;
354  // Now loop through this grid, checking each point that needs checked, return the first valid pt
355  for(int y = 0; !result && y < gridSize; y++) {
356  for(int x = 0; !result && x < gridSize; x++) {
357  if(grid[x][y] != gridCheckPt) continue;
358 
359  double xPos = centerX + (x - gridSize / 2) * deltaXSize;
360  double yPos = centerY + (y - gridSize / 2) * deltaYSize;
361  geos::geom::Coordinate c(xPos, yPos);
362  geos::geom::Point *p = Isis::globalFactory->createPoint(c);
363  if(p->within(&xymp)) {
364  result = p;
365  }
366  else {
367  delete p;
368  }
369  }
370  }
371 
372  return result;
373  }
374 
382  // Call the parents Parse method
384 
385  // Pull parameters specific to this algorithm out
386  try {
387  // Get info from Algorithm group
388  PvlGroup &algo = pvl.findGroup("PolygonSeederAlgorithm", Pvl::Traverse);
389  PvlGroup &invalgo = invalidInput->findGroup("PolygonSeederAlgorithm",
390  Pvl::Traverse);
391 
392  // Set the spacing
393  p_Xspacing = 0.0;
394  if(algo.hasKeyword("XSpacing")) {
395  p_Xspacing = (double) algo["XSpacing"];
396  if(invalgo.hasKeyword("XSpacing")) {
397  invalgo.deleteKeyword("XSpacing");
398  }
399  }
400  else {
401  QString msg = "PVL for GridPolygonSeeder must contain [XSpacing] in [";
402  msg += pvl.fileName() + "]";
403  throw IException(IException::User, msg, _FILEINFO_);
404  }
405 
406  p_Yspacing = 0.0;
407  if(algo.hasKeyword("YSpacing")) {
408  p_Yspacing = (double) algo["YSpacing"];
409  if(invalgo.hasKeyword("YSpacing")) {
410  invalgo.deleteKeyword("YSpacing");
411  }
412  }
413  else {
414  QString msg = "PVL for GridPolygonSeeder must contain [YSpacing] in [";
415  msg += pvl.fileName() + "]";
416  throw IException(IException::User, msg, _FILEINFO_);
417  }
418 
419  p_subGrid = false;
420  if(algo.hasKeyword("SubGrid")) {
421  p_subGrid = IString((QString)algo["SubGrid"]).UpCase() != "FALSE";
422  if(invalgo.hasKeyword("SubGrid")) {
423  invalgo.deleteKeyword("SubGrid");
424  }
425  }
426  }
427  catch(IException &e) {
428  QString msg = "Improper format for PolygonSeeder PVL [" + pvl.fileName() + "]";
429  throw IException(e, IException::User, msg, _FILEINFO_);
430  }
431 
432  if(p_Xspacing <= 0.0) {
433  IString msg = "X Spacing must be greater that 0.0 [(" + IString(p_Xspacing) + "]";
434  throw IException(IException::User, msg, _FILEINFO_);
435  }
436  if(p_Yspacing <= 0.0) {
437  IString msg = "Y Spacing must be greater that 0.0 [(" + IString(p_Yspacing) + "]";
438  throw IException(IException::User, msg, _FILEINFO_);
439  }
440  }
441 
443  PvlGroup pluginInfo(grpName);
444 
445  PvlKeyword name("Name", Algorithm());
446  PvlKeyword minThickness("MinimumThickness", toString(MinimumThickness()));
447  PvlKeyword minArea("MinimumArea", toString(MinimumArea()));
448  PvlKeyword xSpac("XSpacing", toString(p_Xspacing));
449  PvlKeyword ySpac("YSpacing", toString(p_Yspacing));
450  PvlKeyword subGrid("SubGrid", toString(p_subGrid));
451 
452  pluginInfo.addKeyword(name);
453  pluginInfo.addKeyword(minThickness);
454  pluginInfo.addKeyword(minArea);
455  pluginInfo.addKeyword(xSpac);
456  pluginInfo.addKeyword(ySpac);
457  pluginInfo.addKeyword(subGrid);
458 
459  return pluginInfo;
460  }
461 
462 }; // End of namespace Isis
463 
464 
476 extern "C" Isis::PolygonSeeder *GridPolygonSeederPlugin(Isis::Pvl &pvl) {
477  return new Isis::GridPolygonSeeder(pvl);
478 }
479 
Isis::GridPolygonSeeder::Seed
std::vector< geos::geom::Point * > Seed(const geos::geom::MultiPolygon *mp)
Seed a polygon with points.
Definition: GridPolygonSeeder.cpp:52
Isis::GridPolygonSeeder::PluginParameters
virtual PvlGroup PluginParameters(QString grpName)
Plugin parameters.
Definition: GridPolygonSeeder.cpp:442
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::PvlKeyword
A single keyword-value pair.
Definition: PvlKeyword.h:82
Isis::PvlContainer::addKeyword
void addKeyword(const PvlKeyword &keyword, const InsertMode mode=Append)
Add a keyword to the container.
Definition: PvlContainer.cpp:202
Isis::PvlContainer::hasKeyword
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
Definition: PvlContainer.cpp:159
Isis::Pvl
Container for cube-like labels.
Definition: Pvl.h:119
Isis::PolygonSeeder::MinimumArea
double MinimumArea()
Return the minimum allowed area of the polygon.
Definition: PolygonSeeder.cpp:195
Isis::toString
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:211
Isis::GridPolygonSeeder
Seed points using a grid.
Definition: GridPolygonSeeder.h:48
Isis::PvlObject::Traverse
@ Traverse
Search child objects.
Definition: PvlObject.h:158
Isis::PolygonSeeder::MinimumThickness
double MinimumThickness()
Return the minimum allowed thickness of the polygon.
Definition: PolygonSeeder.cpp:183
Isis::IString::UpCase
IString UpCase()
Converst any lower case characters in the object IString with uppercase characters.
Definition: IString.cpp:617
Isis::PvlGroup
Contains multiple PvlContainers.
Definition: PvlGroup.h:41
Isis::GridPolygonSeeder::GridPolygonSeeder
GridPolygonSeeder(Pvl &pvl)
Construct a GridPolygonSeeder algorithm.
Definition: GridPolygonSeeder.cpp:29
Isis::PolygonSeeder::invalidInput
Pvl * invalidInput
The Pvl passed in by the constructor minus what was used.
Definition: PolygonSeeder.h:78
Isis::PvlContainer::fileName
QString fileName() const
Returns the filename used to initialise the Pvl object.
Definition: PvlContainer.h:232
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::PolygonSeeder::Parse
virtual void Parse(Pvl &pvl)
Initialize parameters in the PolygonSeeder class using a PVL specification.
Definition: PolygonSeeder.cpp:85
Isis::PvlContainer::deleteKeyword
void deleteKeyword(const QString &name)
Remove a specified keyword.
Definition: PvlContainer.cpp:97
Isis::PolygonSeeder::Algorithm
QString Algorithm() const
The name of the algorithm, read from the Name Keyword in the PolygonSeeder Pvl passed into the constr...
Definition: PolygonSeeder.cpp:172
Isis::PolygonSeeder::StandardTests
QString StandardTests(const geos::geom::MultiPolygon *multiPoly, const geos::geom::Envelope *polyBoundBox)
Check the polygon to see if it meets standard criteria.
Definition: PolygonSeeder.cpp:146
Isis::GridPolygonSeeder::CheckSubGrid
geos::geom::Point * CheckSubGrid(const geos::geom::MultiPolygon &, const double &, const double &, const int &)
This method is used to search for a valid point, on the polygon, within the square whose center is de...
Definition: GridPolygonSeeder.cpp:292
Isis::IString
Adds specific functionality to C++ strings.
Definition: IString.h:165
Isis::PolygonSeeder
This class is used as the base class for all PolygonSeeder objects.
Definition: PolygonSeeder.h:47
Isis::GridPolygonSeeder::SeedSubGrid
std::vector< geos::geom::Point * > SeedSubGrid(const geos::geom::MultiPolygon *mp)
This method works a lot like SeedGrid, except around the edges of known polygons.
Definition: GridPolygonSeeder.cpp:124
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16
Isis::IException::User
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition: IException.h:126
Isis::GridPolygonSeeder::Parse
virtual void Parse(Pvl &pvl)
Parse the GridPolygonSeeder spicific parameters from the PVL.
Definition: GridPolygonSeeder.cpp:381