Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

Isis 3 Programmer Reference
GridPolygonSeeder.cpp
1
5
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
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 *mPolyBoundBox = multiPoly->getEnvelopeInternal();
75
76 // Call the parents standardTests member
77 QString msg = StandardTests(multiPoly, mPolyBoundBox);
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 for(unsigned int i = 0; i < multiPoly->getNumGeometries(); ++i) {
86 const geos::geom::Polygon *poly = multiPoly->getGeometryN(i);
87 // Starting at the centroid of the xy polygon populate the polygon with a
88 // grid of points with the requested spacing
89 geos::geom::Point *centroid = poly->getCentroid().release();
90 double centerX = centroid->getX();
91 double centerY = centroid->getY();
92 delete centroid;
93 const geos::geom::Envelope *polyBoundBox = poly->getEnvelopeInternal();
94
95 int xStepsLeft = (int)((centerX - polyBoundBox->getMinX()) / p_Xspacing + 0.5);
96 int yStepsLeft = (int)((centerY - polyBoundBox->getMinY()) / p_Yspacing + 0.5);
97 double dRealMinX = centerX - (xStepsLeft * p_Xspacing);
98 double dRealMinY = centerY - (yStepsLeft * p_Yspacing);
99
100 for(double y = dRealMinY; y <= polyBoundBox->getMaxY(); y += p_Yspacing) {
101 for(double x = dRealMinX; x <= polyBoundBox->getMaxX(); x += p_Xspacing) {
102 geos::geom::Coordinate c(x, y);
103 geos::geom::Point *p = Isis::globalFactory->createPoint(c).release();
104
105 if(p->within(multiPoly)) {
106 points.push_back(Isis::globalFactory->createPoint(c).release());
107 }
108 delete p;
109 }
110 }
111 }
112
113 return points;
114 }
115
126 std::vector<geos::geom::Point *> GridPolygonSeeder::SeedSubGrid(const geos::geom::MultiPolygon *multiPoly) {
127 //Projection *proj) {
128 // Storage for the points to be returned
129 std::vector<geos::geom::Point *> points;
130
131 // Create some things we will need shortly
132 //geos::geom::MultiPolygon *xyp = PolygonTools::LatLonToXY(*lonLatPoly, proj);
133 const geos::geom::Envelope *mPolyBoundBox = multiPoly->getEnvelopeInternal();
134
135 // Call the parents standardTests member
136 QString msg = StandardTests(multiPoly, mPolyBoundBox);
137 if(!msg.isEmpty()) {
138 return points;
139 }
140
141 for(unsigned int i = 0; i < multiPoly->getNumGeometries(); ++i) {
142 const geos::geom::Polygon *poly = multiPoly->getGeometryN(i);
143 geos::geom::Point *centroid = poly->getCentroid().release();
144 double centerX = centroid->getX();
145 double centerY = centroid->getY();
146 delete centroid;
147
148 const geos::geom::Envelope *polyBoundBox = poly->getEnvelopeInternal();
149 // Do grid specific tests to make sure this poly should be seeded
150 // (none for now)
151
160 enum PointStatus {
161 pointShouldCheck,
162 pointShouldSubGridCheck,
163 pointFound,
164 pointNotFound,
165 pointCantFind
166 };
167
168 // For maintaining an idea of what's going on in this polygon, we needs to know the dimensions
169 // of the grid.
170 int xSteps = (int)((polyBoundBox->getMaxX() - polyBoundBox->getMinX()) / p_Xspacing + 1.5);
171 int ySteps = (int)((polyBoundBox->getMaxY() - polyBoundBox->getMinY()) / p_Yspacing + 1.5);
172 PointStatus pointCheck[xSteps][ySteps];
173
174 // Initialize our grid of point status'
175 for(int y = 0; y < ySteps; y++) {
176 for(int x = 0; x < xSteps; x++) {
177 pointCheck[x][y] = pointShouldCheck;
178 }
179 }
180
189 int precision = (int)pow(0.5 / MinimumThickness(), 0.5) * 2;
190 bool bGridCleared = true;
191 int xStepsToCentroid = (int)((centerX - polyBoundBox->getMinX()) / p_Xspacing + 0.5);
192 int yStepsToCentroid = (int)((centerY - polyBoundBox->getMinY()) / p_Yspacing + 0.5);
193 double dRealMinX = centerX - (xStepsToCentroid * p_Xspacing);
194 double dRealMinY = centerY - (yStepsToCentroid * p_Yspacing);
195
196 do {
197 // gridCleared is true if we did nothing, if we performed any actions on the grid
198 // it becomes false and another pass should be used.
199 bGridCleared = true;
200
201 for(int y = 0; y < ySteps; y++) {
202 double centerY = dRealMinY + p_Yspacing * y;
203 for(int x = 0; x < xSteps; x++) {
204 double centerX = dRealMinX + p_Xspacing * x;
205 geos::geom::Point *p = NULL;
206
207 // pointShouldCheck tells us we need to check center. Calling
208 // CheckSubGrid with precision=0 will do this for us.
209 if(pointCheck[x][y] == pointShouldCheck) {
210 p = CheckSubGrid(*poly, centerX, centerY, 0);
211 }
212 // pointShouldSubGridCheck tells us we're next to a grid
213 // square where a point was found, so check in depth
214 else if(pointCheck[x][y] == pointShouldSubGridCheck) {
215 p = CheckSubGrid(*poly, centerX, centerY, precision);
216 }
217
218 // If we found a point, verify we can setCoordinate and save the point
219 if(p != NULL) {
220 // Convert the x/y point to a lon/lat point
221 /*if (proj->SetCoordinate(p->getX(),p->getY())) {
222 points.push_back(Isis::globalFactory->createPoint(
223 geos::geom::Coordinate(proj->UniversalLongitude(),
224 proj->UniversalLatitude())));
225 }
226 else {
227 IString msg = "Unable to convert [(" + IString(x) + ",";
228 msg += IString(y) + ")] to a (lon,lat)";
229 throw iException::Message(iException::Programmer, msg, _FILEINFO_);
230 }*/
231 points.push_back(Isis::globalFactory->createPoint(
232 geos::geom::Coordinate(p->getX(), p->getY())).release());
233
234 // We found something new and need a new pass
235 bGridCleared = false;
236 pointCheck[x][y] = pointFound;
237 }
238 else {
239 if(pointCheck[x][y] == pointShouldCheck) {
240 pointCheck[x][y] = pointNotFound;
241 }
242 else if(pointCheck[x][y] == pointShouldSubGridCheck) {
243 pointCheck[x][y] = pointCantFind;
244 }
245 }
246 }
247 }
248
249 // now that the grid has been updated with it's founds, we can look for subgrid checks
250 for(int y = 0; y < ySteps; y++) {
251 for(int x = 0; x < xSteps; x++) {
252 if(pointCheck[x][y] == pointFound) {
253 for(int yOff = -1; yOff <= 1; yOff++) {
254 for(int xOff = -1; xOff <= 1; xOff++) {
255 if(x + xOff >= 0 && x + xOff < xSteps &&
256 y + yOff >= 0 && y + yOff < ySteps &&
257 pointCheck[x+xOff][y+yOff] == pointNotFound) {
258
259 pointCheck[x+xOff][y+yOff] = pointShouldSubGridCheck;
260
261 // We need to do a searchso we need another pass
262 bGridCleared = false;
263 }
264 }
265 }
266 }
267 }
268 }
269
270 }
271 while(!bGridCleared);
272 }
273
274 return points;
275 }
276
298 geos::geom::Point *GridPolygonSeeder::CheckSubGrid(const geos::geom::Polygon &xyp, const double &centerX,
299 const double &centerY, const int &precision) {
300 // We'll make a 2D array detailing which points to check, and which not to, in this rectangle.
301 // Figure out how many points across and vertically we need to check
302 int gridSize = 1;
303 for(int prec = 0; prec < precision && prec < 6; prec ++) {
304 // Maybe solve the recurrence relation for a single equation??
305 gridSize = gridSize * 2 + 1;
306 }
307
308 // These are the possible values in which the 2D array can be. We need the transition value
309 // gridNewCheckPt to not count new points as old points.
310 enum GridPoint {
311 gridEmpty,
312 gridNewCheckPt,
313 gridCheckPt
314 };
315
316 GridPoint grid[gridSize][gridSize];
317
318 for(int y = 0; y < gridSize; y++) {
319 for(int x = 0; x < gridSize; x++) {
320 grid[x][y] = gridEmpty;
321 }
322 }
323
324 // Precision 0: Always center, this is always true
325 grid[gridSize/2][gridSize/2] = gridCheckPt;
326
327 // now populate the grid with what we wish to check for
328 for(int prec = 0; prec < precision; prec ++) {
329 // This tells us how far over in the 2D array to go at a precision from already found points
330 int checkDist = (gridSize + 1) / (int)(4 * (pow(2.0, prec)) + 0.5);
331
332 // Search the grid for already found points and set everything checkDist away to be checked too
333 for(int y = 0; y < gridSize; y++) {
334 for(int x = 0; x < gridSize; x++) {
335 if(grid[x][y] == gridCheckPt) {
336 // We should never overwrite found points, the checkDist should assure this wont happen
337 if(x - checkDist > 0) grid[x-checkDist][y] = gridNewCheckPt;
338 if(y - checkDist > 0) grid[x][y-checkDist] = gridNewCheckPt;
339 if(x + checkDist < gridSize) grid[x+checkDist][y] = gridNewCheckPt;
340 if(y + checkDist < gridSize) grid[x][y+checkDist] = gridNewCheckPt;
341 }
342 }
343 }
344
345 // Convert temporary check pt to final.
346 // Do this separately to avoid changing the data we're processing
347 for(int y = 0; y < gridSize; y++) {
348 for(int x = 0; x < gridSize; x++) {
349 if(grid[x][y] == gridNewCheckPt) grid[x][y] = gridCheckPt;
350 }
351 }
352 }
353
354 // We now have a grid of points to check inside this grid square. Figure out the
355 // distance each of these subsquare pixels are worth.
356 double deltaXSize = p_Xspacing / (gridSize + 1);
357 double deltaYSize = p_Yspacing / (gridSize + 1);
358
359 geos::geom::Point *result = NULL;
360 // Now loop through this grid, checking each point that needs checked, return the first valid pt
361 for(int y = 0; !result && y < gridSize; y++) {
362 for(int x = 0; !result && x < gridSize; x++) {
363 if(grid[x][y] != gridCheckPt) continue;
364
365 double xPos = centerX + (x - gridSize / 2) * deltaXSize;
366 double yPos = centerY + (y - gridSize / 2) * deltaYSize;
367 geos::geom::Coordinate c(xPos, yPos);
368 geos::geom::Point *p = Isis::globalFactory->createPoint(c).release();
369 if(p->within(&xyp)) {
370 result = p->clone().release();
371 }
372 delete p;
373 }
374 }
375
376 return result;
377 }
378
386 // Call the parents Parse method
388
389 // Pull parameters specific to this algorithm out
390 try {
391 // Get info from Algorithm group
392 PvlGroup &algo = pvl.findGroup("PolygonSeederAlgorithm", Pvl::Traverse);
393 PvlGroup &invalgo = invalidInput->findGroup("PolygonSeederAlgorithm",
394 Pvl::Traverse);
395
396 // Set the spacing
397 p_Xspacing = 0.0;
398 if(algo.hasKeyword("XSpacing")) {
399 p_Xspacing = (double) algo["XSpacing"];
400 if(invalgo.hasKeyword("XSpacing")) {
401 invalgo.deleteKeyword("XSpacing");
402 }
403 }
404 else {
405 QString msg = "PVL for GridPolygonSeeder must contain [XSpacing] in [";
406 msg += pvl.fileName() + "]";
407 throw IException(IException::User, msg, _FILEINFO_);
408 }
409
410 p_Yspacing = 0.0;
411 if(algo.hasKeyword("YSpacing")) {
412 p_Yspacing = (double) algo["YSpacing"];
413 if(invalgo.hasKeyword("YSpacing")) {
414 invalgo.deleteKeyword("YSpacing");
415 }
416 }
417 else {
418 QString msg = "PVL for GridPolygonSeeder must contain [YSpacing] in [";
419 msg += pvl.fileName() + "]";
420 throw IException(IException::User, msg, _FILEINFO_);
421 }
422
423 p_subGrid = false;
424 if(algo.hasKeyword("SubGrid")) {
425 p_subGrid = IString((QString)algo["SubGrid"]).UpCase() != "FALSE";
426 if(invalgo.hasKeyword("SubGrid")) {
427 invalgo.deleteKeyword("SubGrid");
428 }
429 }
430 }
431 catch(IException &e) {
432 QString msg = "Improper format for PolygonSeeder PVL [" + pvl.fileName() + "]";
433 throw IException(e, IException::User, msg, _FILEINFO_);
434 }
435
436 if(p_Xspacing <= 0.0) {
437 IString msg = "X Spacing must be greater that 0.0 [(" + IString(p_Xspacing) + "]";
438 throw IException(IException::User, msg, _FILEINFO_);
439 }
440 if(p_Yspacing <= 0.0) {
441 IString msg = "Y Spacing must be greater that 0.0 [(" + IString(p_Yspacing) + "]";
442 throw IException(IException::User, msg, _FILEINFO_);
443 }
444 }
445
446 PvlGroup GridPolygonSeeder::PluginParameters(QString grpName) {
447 PvlGroup pluginInfo(grpName);
448
449 PvlKeyword name("Name", Algorithm());
450 PvlKeyword minThickness("MinimumThickness", toString(MinimumThickness()));
451 PvlKeyword minArea("MinimumArea", toString(MinimumArea()));
452 PvlKeyword xSpac("XSpacing", toString(p_Xspacing));
453 PvlKeyword ySpac("YSpacing", toString(p_Yspacing));
454 PvlKeyword subGrid("SubGrid", toString(p_subGrid));
455
456 pluginInfo.addKeyword(name);
457 pluginInfo.addKeyword(minThickness);
458 pluginInfo.addKeyword(minArea);
459 pluginInfo.addKeyword(xSpac);
460 pluginInfo.addKeyword(ySpac);
461 pluginInfo.addKeyword(subGrid);
462
463 return pluginInfo;
464 }
465
466}; // End of namespace Isis
467
468
480extern "C" Isis::PolygonSeeder *GridPolygonSeederPlugin(Isis::Pvl &pvl) {
481 return new Isis::GridPolygonSeeder(pvl);
482}
483
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::Polygon &, 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...
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.
PolygonSeeder(Pvl &pvl)
Create PolygonSeeder object.
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...
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(const LinearAlgebra::Vector &vector, int precision)
A global function to format LinearAlgebra::Vector as a QString with the given precision.