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
20namespace Isis {
21
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().release();
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).release();
101
102 if(p->within(multiPoly)) {
103 points.push_back(Isis::globalFactory->createPoint(c).release());
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().release();
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())).release());
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).release();
363 if(p->within(&xymp)) {
364 result = p->clone().release();
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",
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
476extern "C" Isis::PolygonSeeder *GridPolygonSeederPlugin(Isis::Pvl &pvl) {
477 return new Isis::GridPolygonSeeder(pvl);
478}
479
Seed points using a grid.
std::vector< geos::geom::Point * > Seed(const geos::geom::MultiPolygon *mp)
Seed a polygon with points.
virtual void Parse(Pvl &pvl)
Parse the GridPolygonSeeder spicific parameters from the PVL.
virtual PvlGroup PluginParameters(QString grpName)
Plugin parameters.
GridPolygonSeeder(Pvl &pvl)
Construct a GridPolygonSeeder algorithm.
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.
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...
Isis exception class.
Definition IException.h:91
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
Adds specific functionality to C++ strings.
Definition IString.h:165
IString UpCase()
Converst any lower case characters in the object IString with uppercase characters.
Definition IString.cpp:617
This class is used as the base class for all PolygonSeeder objects.
virtual void Parse(Pvl &pvl)
Initialize parameters in the PolygonSeeder class using a PVL specification.
Pvl * invalidInput
The Pvl passed in by the constructor minus what was used.
QString StandardTests(const geos::geom::MultiPolygon *multiPoly, const geos::geom::Envelope *polyBoundBox)
Check the polygon to see if it meets standard criteria.
double MinimumArea()
Return the minimum allowed area of the polygon.
double MinimumThickness()
Return the minimum allowed thickness of the polygon.
QString Algorithm() const
The name of the algorithm, read from the Name Keyword in the PolygonSeeder Pvl passed into the constr...
Contains multiple PvlContainers.
Definition PvlGroup.h:41
Container for cube-like labels.
Definition Pvl.h:119
A single keyword-value pair.
Definition PvlKeyword.h:87
@ Traverse
Search child objects.
Definition PvlObject.h:158
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition PvlObject.h:129
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition IString.cpp:211