Isis 3 Programmer Reference
PolygonTools.cpp
Go to the documentation of this file.
1 
25 #include <string>
26 #include <iostream>
27 #include <sstream>
28 #include <iomanip>
29 #include <vector>
30 #include <cmath>
31 
32 #include <QDebug>
33 #include "geos/geom/BinaryOp.h"
34 #include "geos/geom/CoordinateArraySequence.h"
35 #include "geos/geom/CoordinateSequence.h"
36 #include "geos/geom/LinearRing.h"
37 #include "geos/geom/Point.h"
38 #include "geos/geom/Polygon.h"
39 #include "geos/operation/distance/DistanceOp.h"
40 #include "geos/opOverlay.h"
41 #include "geos/operation/overlay/snap/GeometrySnapper.h"
42 
43 #include "SpecialPixel.h"
44 #include "PolygonTools.h"
45 #include "TProjection.h"
46 #include "ProjectionFactory.h"
47 #include "UniversalGroundMap.h"
48 
49 using namespace std;
50 namespace Isis {
63  geos::geom::MultiPolygon *PolygonTools::LatLonToXY(
64  const geos::geom::MultiPolygon &lonLatPolygon, TProjection *projection) {
65  if (projection == NULL) {
66  string msg = "Unable to convert Lon/Lat polygon to X/Y. ";
67  msg += "No projection has was supplied";
68  throw IException(IException::Programmer, msg, _FILEINFO_);
69  }
70 
71  // Convert the Lat/Lon poly coordinates to X/Y coordinates
72  if(lonLatPolygon.isEmpty()) {
73  return globalFactory.createMultiPolygon();
74  }
75  else {
76  vector<geos::geom::Geometry *> *xyPolys = new vector<geos::geom::Geometry *>;
77  // Convert each polygon in this multi-polygon
78  for(unsigned int g = 0; g < lonLatPolygon.getNumGeometries(); ++g) {
79  const geos::geom::Polygon *poly =
80  dynamic_cast<const geos::geom::Polygon *>(
81  lonLatPolygon.getGeometryN(g));
82 
83  // Convert each hole inside this polygon
84  vector<geos::geom::Geometry *> *holes = new vector<geos::geom::Geometry *>;
85  for(unsigned int h = 0; h < poly->getNumInteriorRing(); ++h) {
86  geos::geom::CoordinateSequence *xycoords = new geos::geom::CoordinateArraySequence();
87  geos::geom::CoordinateSequence *llcoords =
88  poly->getInteriorRingN(h)->getCoordinates();
89 
90  // Convert each coordinate in this hole
91  for(unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
92  projection->SetGround(llcoords->getAt(cord).y,
93  llcoords->getAt(cord).x);
94  xycoords->add(geos::geom::Coordinate(projection->XCoord(),
95  projection->YCoord()));
96  } // end num coords in hole loop
97 
98  geos::geom::LinearRing *hole = globalFactory.createLinearRing(xycoords);
99 
100  if(hole->isValid() && !hole->isEmpty()) {
101  holes->push_back(hole);
102  }
103  else {
104  delete hole;
105  }
106  } // end num holes in polygon loop
107 
108  // Convert the exterior ring of this polygon
109  geos::geom::CoordinateSequence *xycoords = new geos::geom::CoordinateArraySequence();
110  geos::geom::CoordinateSequence *llcoords =
111  poly->getExteriorRing()->getCoordinates();
112 
113  // Convert each coordinate in the exterior ring of this polygon
114  for (unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
115  projection->SetGround(llcoords->getAt(cord).y,
116  llcoords->getAt(cord).x);
117  xycoords->add(geos::geom::Coordinate(projection->XCoord(),
118  projection->YCoord()));
119  } // end exterior ring coordinate loop
120 
121  geos::geom::Polygon *newPoly = globalFactory.createPolygon(
122  globalFactory.createLinearRing(xycoords), holes);
123 
124  if(newPoly->isValid() && !newPoly->isEmpty() && newPoly->getArea() > 1.0e-14) {
125  xyPolys->push_back(newPoly);
126  }
127  else {
128  delete newPoly;
129  }
130  } // end num geometry in multi-poly
131 
132  // Create a new multipoly from all the new X/Y polygon(s)
133  geos::geom::MultiPolygon *spikedPoly = globalFactory.createMultiPolygon(xyPolys);
134 
135  if(spikedPoly->isValid() && !spikedPoly->isEmpty()) {
136  return spikedPoly;
137  }
138  else {
139  try {
140  geos::geom::MultiPolygon *despikedPoly = Despike(spikedPoly);
141 
142  delete spikedPoly;
143  spikedPoly = NULL;
144 
145  return despikedPoly;
146  }
147  catch(IException &e) {
148  IString msg = "Unable to convert polygon from Lat/Lon to X/Y";
149  throw IException(IException::Programmer, msg, _FILEINFO_);
150  }
151  }
152 
153  } // end else
154  }
155 
156 
169  geos::geom::MultiPolygon *PolygonTools::XYToLatLon(
170  const geos::geom::MultiPolygon &xYPolygon, TProjection *projection) {
171 
172  if(projection == NULL) {
173  string msg = "Unable to convert X/Y polygon to Lon/Lat. ";
174  msg += "No projection was supplied";
175  throw IException(IException::Programmer, msg, _FILEINFO_);
176  }
177 
178  // Convert the X/Y poly coordinates to Lat/Lon coordinates
179  if(xYPolygon.isEmpty()) {
180  return globalFactory.createMultiPolygon();
181  }
182  else {
183  vector<geos::geom::Geometry *> *llPolys = new vector<geos::geom::Geometry *>;
184  // Convert each polygon in this multi-polygon
185  for(unsigned int g = 0; g < xYPolygon.getNumGeometries(); ++g) {
186  const geos::geom::Polygon *poly =
187  dynamic_cast<const geos::geom::Polygon *>(
188  xYPolygon.getGeometryN(g));
189 
190  // Convert each hole inside this polygon
191  vector<geos::geom::Geometry *> *holes = new vector<geos::geom::Geometry *>;
192  for(unsigned int h = 0; h < poly->getNumInteriorRing(); ++h) {
193  geos::geom::CoordinateSequence *llcoords = new geos::geom::CoordinateArraySequence();
194  geos::geom::CoordinateSequence *xycoords =
195  poly->getInteriorRingN(h)->getCoordinates();
196 
197  // Convert each coordinate in this hole
198  for(unsigned int cord = 0; cord < xycoords->getSize(); ++cord) {
199  projection->SetWorld(xycoords->getAt(cord).x,
200  xycoords->getAt(cord).y);
201  llcoords->add(geos::geom::Coordinate(projection->Longitude(),
202  projection->Latitude()));
203  } // end num coords in hole loop
204  holes->push_back(globalFactory.createLinearRing(llcoords));
205  } // end num holes in polygon loop
206 
207  // Convert the exterior ring of this polygon
208  geos::geom::CoordinateSequence *llcoords = new geos::geom::DefaultCoordinateSequence();
209  geos::geom::CoordinateSequence *xycoords =
210  poly->getExteriorRing()->getCoordinates();
211 
212  // Convert each coordinate in the exterior ring of this polygon
213  for(unsigned int cord = 0; cord < xycoords->getSize(); ++cord) {
214  projection->SetWorld(xycoords->getAt(cord).x,
215  xycoords->getAt(cord).y);
216  llcoords->add(geos::geom::Coordinate(projection->Longitude(),
217  projection->Latitude()));
218  } // end exterior ring coordinate loop
219 
220  llPolys->push_back(globalFactory.createPolygon(
221  globalFactory.createLinearRing(llcoords), holes));
222  } // end num geometry in multi-poly
223 
224 
225  // Create a new multipoly from all the new Lat/Lon polygon(s)
226  geos::geom::MultiPolygon *spikedPoly = globalFactory.createMultiPolygon(llPolys);
227 
228  if(spikedPoly->isValid() && !spikedPoly->isEmpty()) {
229  return spikedPoly;
230  }
231  else {
232  try {
233  geos::geom::MultiPolygon *despikedPoly = Despike(spikedPoly);
234 
235  delete spikedPoly;
236  spikedPoly = NULL;
237 
238  return despikedPoly;
239  }
240  catch(IException &e) {
241  IString msg = "Unable to convert polygon from X/Y to Lat/Lon";
242  throw IException(IException::Programmer, msg, _FILEINFO_);
243  }
244  }
245  } // end else
246  }
247 
248 
261  geos::geom::MultiPolygon *PolygonTools::LatLonToSampleLine(
262  const geos::geom::MultiPolygon &lonLatPolygon, UniversalGroundMap *ugm) {
263 
264  if(ugm == NULL) {
265  string msg = "Unable to convert Lon/Lat polygon to Sample/Line. ";
266  msg += "No UniversalGroundMap was supplied";
267  throw IException(IException::Programmer, msg, _FILEINFO_);
268  }
269 
270  // Convert the Lon/Lat poly coordinates to Sample/Line coordinates
271  if(lonLatPolygon.isEmpty()) {
272  return globalFactory.createMultiPolygon();
273  }
274  else {
275  vector<geos::geom::Geometry *> *slPolys = new vector<geos::geom::Geometry *>;
276  // Convert each polygon in this multi-polygon
277  for(unsigned int g = 0; g < lonLatPolygon.getNumGeometries(); g++) {
278  const geos::geom::Polygon *poly =
279  dynamic_cast<const geos::geom::Polygon *>(
280  lonLatPolygon.getGeometryN(g));
281 
282  // Convert each hole inside this polygon
283  vector<geos::geom::Geometry *> *holes = new vector<geos::geom::Geometry *>;
284  for(unsigned int h = 0; h < poly->getNumInteriorRing(); ++h) {
285  geos::geom::CoordinateSequence *slcoords = new geos::geom::DefaultCoordinateSequence();
286  geos::geom::CoordinateSequence *llcoords =
287  poly->getInteriorRingN(h)->getCoordinates();
288 
289  // Convert each coordinate in this hole
290  for(unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
291  ugm->SetUniversalGround(llcoords->getAt(cord).y,
292  llcoords->getAt(cord).x);
293  slcoords->add(geos::geom::Coordinate(ugm->Sample(),
294  ugm->Line()));
295  } // end num coords in hole loop
296  holes->push_back(globalFactory.createLinearRing(slcoords));
297  delete slcoords;
298  delete llcoords;
299  } // end num holes in polygon loop
300 
301  // Convert the exterior ring of this polygon
302  geos::geom::CoordinateSequence *slcoords = new geos::geom::CoordinateArraySequence();
303  geos::geom::CoordinateSequence *llcoords =
304  poly->getExteriorRing()->getCoordinates();
305 
306  // Convert each coordinate in the exterior ring of this polygon
307  for(unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
308  if (ugm->SetUniversalGround(llcoords->getAt(cord).y,
309  llcoords->getAt(cord).x)) {
310  slcoords->add(geos::geom::Coordinate(ugm->Sample(),
311  ugm->Line()));
312  }
313  } // end exterior ring coordinate loop
314 
315  // Make sure that the line string is closed.
316  if (slcoords->getSize() > 0 && !slcoords->front().equals(slcoords->back())) {
317  slcoords->add(slcoords->front());
318  }
319 
320  try {
321  slPolys->push_back(globalFactory.createPolygon(
322  globalFactory.createLinearRing(slcoords), holes));
323  }
324  catch (std::exception &e) {
325  throw IException(IException::Unknown,
326  QObject::tr("Unable to convert polygon from Lat/Lon to Sample/Line. The "
327  "error given was [%1].").arg(e.what()),
328  _FILEINFO_);
329  }
330 
331  delete llcoords;
332  } // end num geometry in multi-poly
333 
334  // Create a new multipoly from all the new Sample/Line polygon(s)
335  geos::geom::MultiPolygon *spikedPoly = globalFactory.createMultiPolygon(slPolys);
336 
337  if(spikedPoly->isValid() && !spikedPoly->isEmpty()) {
338  return spikedPoly;
339  }
340  else {
341  try {
342  geos::geom::MultiPolygon *despikedPoly = Despike(spikedPoly);
343 
344  delete spikedPoly;
345  spikedPoly = NULL;
346 
347  return despikedPoly;
348  }
349  catch (IException &e) {
350  IString msg = "Unable to convert polygon from Lat/Lon to Sample/Line";
351  throw IException(IException::Programmer, msg, _FILEINFO_);
352  }
353  }
354  } // end else
355  }
356 
357 
370  geos::geom::MultiPolygon *PolygonTools::CopyMultiPolygon(const geos::geom::MultiPolygon *mpolygon) {
371 
372  vector<geos::geom::Geometry *> *polys = new vector<geos::geom::Geometry *>;
373  for(unsigned int i = 0; i < mpolygon->getNumGeometries(); ++i) {
374  polys->push_back((mpolygon->getGeometryN(i))->clone());
375  }
376  return globalFactory.createMultiPolygon(polys);
377  }
378 
379 
392  geos::geom::MultiPolygon *PolygonTools::CopyMultiPolygon(const geos::geom::MultiPolygon &mpolygon) {
393 
394  vector<geos::geom::Geometry *> *polys = new vector<geos::geom::Geometry *>;
395  for(unsigned int i = 0; i < mpolygon.getNumGeometries(); ++i) {
396  polys->push_back((mpolygon.getGeometryN(i))->clone());
397  }
398  return globalFactory.createMultiPolygon(polys);
399  }
400 
401 
410  QString PolygonTools::ToGML(const geos::geom::MultiPolygon *mpolygon, QString idString,
411  QString schema) {
412 
413  ostringstream os;
414 
415  //Write out the GML header
416  os << "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" << endl;
417  os << "<ogr:FeatureCollection" << endl;
418  os << " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" << endl;
419  os << " xsi:schemaLocation=\"http://ogr.maptools.org/ " << schema << "\"" << endl;
420  os << " xmlns:ogr=\"http://ogr.maptools.org/\"" << endl;
421  os << " xmlns:gml=\"http://www.opengis.net/gml\">" << endl;
422  os << " <gml:boundedBy>" << endl;
423  os << " <gml:Box>" << endl;
424  os << " <gml:coord>";
425  os << "<gml:X>" <<
426  setprecision(15) << mpolygon->getEnvelopeInternal()->getMinX() <<
427  "</gml:X>";
428  os << "<gml:Y>" <<
429  setprecision(15) << mpolygon->getEnvelopeInternal()->getMinY() <<
430  "</gml:Y>";
431  os << "</gml:coord>" << endl;
432  os << " <gml:coord>";
433  os << "<gml:X>" <<
434  setprecision(15) << mpolygon->getEnvelopeInternal()->getMaxX() <<
435  "</gml:X>";
436  os << "<gml:Y>" <<
437  setprecision(15) << mpolygon->getEnvelopeInternal()->getMaxY() <<
438  "</gml:Y>";
439  os << "</gml:coord>" << endl;
440  os << " </gml:Box>" << endl;
441  os << " </gml:boundedBy>" << endl << endl;
442  os << " <gml:featureMember>" << endl;
443  os << " <ogr:multi_polygon fid=\"0\">" << endl;
444  os << " <ogr:ID>" << idString << "</ogr:ID>" << endl;
445  os << " <ogr:geometryProperty><gml:MultiPolygon><gml:polygonMember>" <<
446  "<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>";
447 
448 
449  for(unsigned int polyN = 0; polyN < mpolygon->getNumGeometries(); polyN++) {
450  geos::geom::CoordinateSequence *pts = mpolygon->getGeometryN(polyN)->getCoordinates();
451 
452  for(unsigned int i = 0; i < pts->getSize(); i++) {
453  double lon = pts->getAt(i).x;
454  double lat = pts->getAt(i).y;
455 
456  os << setprecision(15) << lon << "," << setprecision(15) << lat << " ";
457  }
458  }
459 
460  os << "</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs>" <<
461  "</gml:Polygon></gml:polygonMember></gml:MultiPolygon>" <<
462  "</ogr:geometryProperty>" << endl;
463  os << " </ogr:multi_polygon>" << endl;
464  os << " </gml:featureMember>" << endl;
465  os << "</ogr:FeatureCollection>";
466 
467  return os.str().c_str();
468  }
469 
470 
477  QString PolygonTools::GMLSchema() {
478 
479  ostringstream os;
480 
481  os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
482  os << "<xs:schema targetNamespace=\"http://ogr.maptools.org/\" "
483  "xmlns:ogr=\"http://ogr.maptools.org/\" "
484  "xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" "
485  "xmlns:gml=\"http://www.opengis.net/gml\" "
486  "elementFormDefault=\"qualified\" "
487  "version=\"1.0\">" << endl;
488  os << " <xs:import namespace=\"http://www.opengis.net/gml\" "
489  "schemaLocation=\"http://schemas.opengis.net/gml/2.1.2/feature.xsd\"/>" << endl;
490  os << " <xs:element name=\"FeatureCollection\" "
491  "type=\"ogr:FeatureCollectionType\" "
492  "substitutionGroup=\"gml:_FeatureCollection\"/>" << endl;
493  os << " <xs:complexType name=\"FeatureCollectionType\">" << endl;
494  os << " <xs:complexContent>" << endl;
495  os << " <xs:extension base=\"gml:AbstractFeatureCollectionType\">" << endl;
496  os << " <xs:attribute name=\"lockId\" type=\"xs:string\" use=\"optional\"/>" << endl;
497  os << " <xs:attribute name=\"scope\" type=\"xs:string\" use=\"optional\"/>" << endl;
498  os << " </xs:extension>" << endl;
499  os << " </xs:complexContent>" << endl;
500  os << " </xs:complexType>" << endl;
501  os << " <xs:element name=\"multi_polygon\" "
502  "type=\"ogr:multi_polygon_Type\" "
503  "substitutionGroup=\"gml:_Feature\"/>" << endl;
504  os << " <xs:complexType name=\"multi_polygon_Type\">" << endl;
505  os << " <xs:complexContent>" << endl;
506  os << " <xs:extension base=\"gml:AbstractFeatureType\">" << endl;
507  os << " <xs:sequence>" << endl;
508  os << " <xs:element name=\"geometryProperty\" "
509  "type=\"gml:MultiPolygonPropertyType\" "
510  "nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\"/>" << endl;
511  os << " <xs:element name=\"fid\" nillable=\"true\" minOccurs=\"0\" "
512  "maxOccurs=\"1\">" << endl;
513  os << " <xs:simpleType>" << endl;
514  os << " <xs:restriction base=\"xs:string\">" << endl;
515  os << " </xs:restriction>" << endl;
516  os << " </xs:simpleType>" << endl;
517  os << " </xs:element>" << endl;
518  os << " <xs:element name=\"ID\" nillable=\"true\" minOccurs=\"0\" "
519  "maxOccurs=\"1\">" << endl;
520  os << " <xs:simpleType>" << endl;
521  os << " <xs:restriction base=\"xs:integer\">" << endl;
522  os << " <xs:totalDigits value=\"16\"/>" << endl;
523  os << " </xs:restriction>" << endl;
524  os << " </xs:simpleType>" << endl;
525  os << " </xs:element>" << endl;
526  os << " </xs:sequence>" << endl;
527  os << " </xs:extension>" << endl;
528  os << " </xs:complexContent>" << endl;
529  os << " </xs:complexType>" << endl;
530  os << "</xs:schema>";
531 
532  return os.str().c_str();
533  }
534 
535 
543  geos::geom::MultiPolygon *PolygonTools::To180(geos::geom::MultiPolygon *poly360) {
544  try {
545  // Let's take the 360 pieces that sit between 180 and 360 and move them
546  // to -180 to 0. To accomplish this, make a poly that fits 180 -> 360
547  // degrees longitude and intersect (this gives us the pieces that sit
548  // >180). Move this intersection to the left. Then make a poly that fits
549  // 0 to 180 and intersect with the original. These two combined are the
550  // result.
551  geos::geom::CoordinateArraySequence *leftOf180Pts =
552  new geos::geom::CoordinateArraySequence();
553  leftOf180Pts->add(geos::geom::Coordinate(0, -90));
554  leftOf180Pts->add(geos::geom::Coordinate(0, 90));
555  leftOf180Pts->add(geos::geom::Coordinate(180, 90));
556  leftOf180Pts->add(geos::geom::Coordinate(180, -90));
557  leftOf180Pts->add(geos::geom::Coordinate(0, -90));
558 
559  geos::geom::LinearRing *leftOf180Geom =
560  globalFactory.createLinearRing(leftOf180Pts);
561 
562  geos::geom::Polygon *leftOf180Poly =
563  globalFactory.createPolygon(leftOf180Geom, NULL);
564 
565  geos::geom::CoordinateArraySequence *rightOf180Pts =
566  new geos::geom::CoordinateArraySequence();
567  rightOf180Pts->add(geos::geom::Coordinate(180, -90));
568  rightOf180Pts->add(geos::geom::Coordinate(180, 90));
569  rightOf180Pts->add(geos::geom::Coordinate(360, 90));
570  rightOf180Pts->add(geos::geom::Coordinate(360, -90));
571  rightOf180Pts->add(geos::geom::Coordinate(180, -90));
572 
573  geos::geom::LinearRing *rightOf180Geom =
574  globalFactory.createLinearRing(rightOf180Pts);
575 
576  geos::geom::Polygon *rightOf180Poly =
577  globalFactory.createPolygon(rightOf180Geom, NULL);
578 
579  geos::geom::Geometry *preserved = Intersect(leftOf180Poly, poly360);
580  geos::geom::Geometry *moving = Intersect(rightOf180Poly, poly360);
581 
582  geos::geom::CoordinateSequence *movingPts = moving->getCoordinates();
583  geos::geom::CoordinateSequence *movedPts =
584  new geos::geom::CoordinateArraySequence();
585 
586  for(unsigned int i = 0; i < movingPts->getSize(); i ++) {
587  movedPts->add(geos::geom::Coordinate(movingPts->getAt(i).x - 360.0,
588  movingPts->getAt(i).y));
589  }
590 
591  if(movedPts->getSize()) {
592  movedPts->add(geos::geom::Coordinate(movedPts->getAt(0).x,
593  movedPts->getAt(0).y));
594  }
595 
596  geos::geom::Geometry *moved = globalFactory.createPolygon(
597  globalFactory.createLinearRing(movedPts), NULL);
598 
599  std::vector<geos::geom::Geometry *> *geomsForCollection = new
600  std::vector<geos::geom::Geometry *>;
601  geomsForCollection->push_back(preserved);
602  geomsForCollection->push_back(moved);
603 
604  geos::geom::GeometryCollection *the180Polys =
605  Isis::globalFactory.createGeometryCollection(geomsForCollection);
606 
607  geos::geom::MultiPolygon *result = MakeMultiPolygon(the180Polys);
608  delete the180Polys;
609  the180Polys = NULL;
610 
611  geos::geom::MultiPolygon *fixedResult = FixSeam(result);
612  delete result;
613  result = NULL;
614 
615  return fixedResult;
616  }
617  catch(geos::util::GEOSException *exc) {
618  IString msg = "Conversion to 180 failed. The reason given was [" +
619  IString(exc->what()) + "]";
620  delete exc;
621  throw IException(IException::Programmer, msg, _FILEINFO_);
622  }
623  catch(...) {
624  IString msg = "Conversion to 180 failed. Could not determine the reason";
625  throw IException(IException::Programmer, msg, _FILEINFO_);
626  }
627  }
628 
629 
640  double PolygonTools::Thickness(const geos::geom::MultiPolygon *mpolygon) {
641  const geos::geom::Envelope *envelope = mpolygon->getEnvelopeInternal();
642 
643  double x = fabs(envelope->getMaxX() - envelope->getMinX());
644  double y = fabs(envelope->getMaxY() - envelope->getMinY());
645  double extent = max(x, y);
646 
647  return mpolygon->getArea() / (extent * extent);
648  }
649 
650 
662  geos::geom::MultiPolygon *PolygonTools::Despike(const geos::geom::Geometry *geom) {
663  geos::geom::MultiPolygon *spiked = MakeMultiPolygon(geom);
664  geos::geom::MultiPolygon *despiked = Despike(spiked);
665 
666  delete spiked;
667  spiked = NULL;
668 
669  return despiked;
670  }
671 
672 
683  geos::geom::MultiPolygon *PolygonTools::Despike(const geos::geom::MultiPolygon *multiPoly) {
684  // Despike each polygon in the multipolygon
685  vector<geos::geom::Geometry *> *newPolys = new vector<geos::geom::Geometry *>;
686  for(unsigned int g = 0; g < multiPoly->getNumGeometries(); ++g) {
687  const geos::geom::Polygon *poly =
688  dynamic_cast<const geos::geom::Polygon *>(multiPoly->getGeometryN(g));
689 
690  // Despike each hole inside this polygon
691  vector<geos::geom::Geometry *> *holes = new vector<geos::geom::Geometry *>;
692  for(unsigned int h = 0; h < poly->getNumInteriorRing(); ++h) {
693  const geos::geom::LineString *ls = poly->getInteriorRingN(h);
694  geos::geom::LinearRing *lr;
695 
696  // If the hole is not valid fix it
697  // If the hole is NOT valid despike it
698  lr = Despike(ls);
699 
700  if(!lr->isValid()) {
701  geos::geom::LinearRing *fixed = FixGeometry(lr);
702  delete lr;
703  lr = fixed;
704  }
705 
706  // Save this hole if it is not too small
707  if(!lr->isEmpty()) {
708  holes->push_back(lr);
709  }
710  else {
711  delete lr;
712  }
713  } // End holes loop
714 
715  // Work on the main polygon
716  const geos::geom::LineString *ls = poly->getExteriorRing();
717  geos::geom::LinearRing *lr;
718 
719  lr = Despike(ls);
720 
721  try {
722  if(!lr->isValid()) {
723  geos::geom::LinearRing *fixed = FixGeometry(lr);
724  delete lr;
725  lr = fixed;
726  }
727  }
728  catch(IException &e) {
729  // Sometimes despike and fix fail, but the input is really valid. We can just go
730  // with the non-despiked polygon.
731  if(ls->isValid() && ls->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
732  lr = dynamic_cast<geos::geom::LinearRing *>(ls->clone());
733  }
734  else {
735  throw;
736  }
737  }
738 
739  // Create a new polygon with the holes and save it
740  if(!lr->isEmpty()) {
741  geos::geom::Polygon *tp = Isis::globalFactory.createPolygon(lr, holes);
742 
743  if(tp->isEmpty() || !tp->isValid()) {
744  delete tp;
745  newPolys->push_back(poly->clone());
746  }
747  else {
748  newPolys->push_back(tp);
749  }
750  }
751  } // End polygons loop
752 
753  // Create a new multipoly from the polygon(s)
754  geos::geom::MultiPolygon *mp = Isis::globalFactory.createMultiPolygon(newPolys);
755 
756  if(!mp->isValid() || mp->isEmpty()) {
757  delete mp;
758  mp = NULL;
759  IString msg = "Despike failed to correct the polygon";
760  throw IException(IException::Programmer, msg, _FILEINFO_);
761  }
762 
763  // if multipoly area changes more than 25% we did something bad to the multipolygon
764  if(fabs((mp->getArea() / multiPoly->getArea()) - 1.0) > 0.50) {
765  IString msg = "Despike failed to correct the polygon " + mp->toString();
766  delete mp;
767 
768  throw IException(IException::Programmer, msg, _FILEINFO_);
769  }
770 
771  return mp;
772  }
773 
774 
788  geos::geom::LinearRing *PolygonTools::Despike(const geos::geom::LineString *lineString) {
789  geos::geom::CoordinateSequence *vertices = lineString->getCoordinates();
790 
791  // We need a full polygon to despike = 3 points (+1 for end==first) = at least 4 points
792  if(vertices->getSize() < 4) {
793  delete vertices;
794  return Isis::globalFactory.createLinearRing(geos::geom::CoordinateArraySequence());
795  }
796 
797  // delete one of the duplicate first/end coordinates,
798  // spikes can occur here and the duplicate points throws off the test
799  vertices->deleteAt(vertices->getSize() - 1);
800 
801  // Index will become negative if our first points are spiked, so we need
802  // to make it big enough to encapsulate the (unsigned int) from getSize()
803  // and allow negative numbers.
804  for(long index = 0; index < (long)vertices->getSize(); index++) {
805  // If we're under 3 vertices, we've despiked all the points out of the polygon :(
806  if(vertices->getSize() < 3) {
807  break;
808  }
809 
810  // These are the array indices that we're going to test for a spike,
811  // the middle one is the one that's spiked if IsSpiked(...) is true.
812  long testCoords[3] = {
813  index - 1,
814  index,
815  index + 1
816  };
817 
818  // Make sure the index is inside of our coordinates array (we'll have both too small/too
819  // big of an index)
820  for(int j = 0; j < 3; j++) {
821  while(testCoords[j] < 0) {
822  testCoords[j] += vertices->getSize();
823  }
824 
825  while(testCoords[j] >= (long)vertices->getSize()) {
826  testCoords[j] -= vertices->getSize();
827  }
828  }
829 
830  // Test the middle point for a spike
831  if(IsSpiked(vertices->getAt(testCoords[0]),
832  vertices->getAt(testCoords[1]),
833  vertices->getAt(testCoords[2]))) {
834  // It's spiked, delete it
835  vertices->deleteAt(testCoords[1]);
836 
837  // Back up to the first test that is affected by this change
838  index -= 2;
839  }
840  }
841 
842  if(vertices->getSize() < 3) {
843  delete vertices;
844  vertices = NULL;
845 
846  return Isis::globalFactory.createLinearRing(geos::geom::CoordinateArraySequence());
847  }
848  else {
849  // Duplicate the first vertex as the last to close the polygon
850  vertices->add(vertices->getAt(0));
851  return Isis::globalFactory.createLinearRing(vertices);
852  }
853  }
854 
864  bool PolygonTools::IsSpiked(geos::geom::Coordinate first,
865  geos::geom::Coordinate middle,
866  geos::geom::Coordinate last) {
867  return TestSpiked(first, middle, last) || TestSpiked(last, middle, first);
868  }
869 
898  bool PolygonTools::TestSpiked(geos::geom::Coordinate first, geos::geom::Coordinate middle,
899  geos::geom::Coordinate last) {
900  geos::geom::Point *firstPt = Isis::globalFactory.createPoint(first);
901  geos::geom::Point *middlePt = Isis::globalFactory.createPoint(middle);
902  geos::geom::Point *lastPt = Isis::globalFactory.createPoint(last);
903 
904  geos::geom::CoordinateSequence *coords = new geos::geom::CoordinateArraySequence();
905  coords->add(first);
906  coords->add(middle);
907  geos::geom::LineString *line = Isis::globalFactory.createLineString(coords); // line takes ownership
908 
909  // The lower the tolerance, the less this algorithm removes and thus
910  // the better chance of success in findimageoverlaps. However, if you
911  // lower the tolerance then there is also a greater chance of programs
912  // such as autoseed failing. 1% is the current tolerance.
913  double tolerance = line->getLength() / 100.0;
914 
915  bool spiked = true;
916 
917  double distanceLastMiddle = geos::operation::distance::DistanceOp::distance(lastPt, middlePt);
918  double distanceLastLine = geos::operation::distance::DistanceOp::distance(lastPt, line);
919 
920  if(distanceLastMiddle == 0.0) return true; // get rid of same point
921 
922  // Checks the ratio of the distance between the last point and the line, and the last point
923  // and the middle point if the ratio is very small then there is a spike
924  if(distanceLastLine / distanceLastMiddle >= .05) {
925  spiked = false;
926  }
927 
928  // If the point is away from the line, keep it
929  if(spiked && distanceLastLine > tolerance) {
930  spiked = false;
931  }
932 
933  if(!spiked) {
934  geos::geom::CoordinateSequence *coords = new geos::geom::CoordinateArraySequence();
935  coords->add(first);
936  coords->add(middle);
937  coords->add(last);
938  coords->add(first);
939 
940  // shell takes ownership of coords
941  geos::geom::LinearRing *shell = Isis::globalFactory.createLinearRing(coords);
942  std::vector<geos::geom::Geometry *> *empty = new std::vector<geos::geom::Geometry *>;
943 
944  // poly takes ownership of shell and empty
945  geos::geom::Polygon *poly = Isis::globalFactory.createPolygon(shell, empty);
946 
947 
948  // if these 3 points define a straight line then the middle is worthless (defines nothing)
949  // or problematic
950  if(poly->getArea() < 1.0e-10) {
951  spiked = true;
952  }
953 
954  delete poly;
955  }
956 
957 
958  delete firstPt;
959  delete middlePt;
960  delete lastPt;
961  delete line;
962 
963  return spiked;
964  }
965 
966 
980  geos::geom::Geometry *PolygonTools::Intersect(const geos::geom::Geometry *geom1,
981  const geos::geom::Geometry *geom2) {
982  try {
983  return Operate(geom1, geom2, (unsigned int)geos::operation::overlay::OverlayOp::opINTERSECTION);
984  }
985  catch(geos::util::GEOSException *exc) {
986  IString msg = "Intersect operation failed. The reason given was [" + IString(exc->what()) + "]";
987  delete exc;
988  throw IException(IException::Programmer, msg, _FILEINFO_);
989  }
990  catch(IException &e) {
991  IString msg = "Intersect operation failed";
992  throw IException(e, IException::Programmer, msg, _FILEINFO_);
993  }
994  catch(...) {
995  IString msg = "Intersect operation failed for an unknown reason";
996  throw IException(IException::Programmer, msg, _FILEINFO_);
997  }
998  }
999 
1000 
1001  geos::geom::Geometry *PolygonTools::Operate(const geos::geom::Geometry *geom1,
1002  const geos::geom::Geometry *geom2,
1003  unsigned int opcode) {
1004 
1005  geos::operation::overlay::OverlayOp::OpCode code =
1006  (geos::operation::overlay::OverlayOp::OpCode)opcode;
1007 
1008  geos::geom::Geometry *result = NULL;
1009  bool failed = true;
1010  geos::geom::Geometry *geomFirst = MakeMultiPolygon(geom1);
1011  geos::geom::Geometry *geomSecond = MakeMultiPolygon(geom2);
1012 
1013  geos::operation::overlay::snap::GeometrySnapper snap(*geomFirst);
1014  geos::geom::Geometry *geomSnapped = snap.snapTo(*geomSecond, 1.0e-10)->clone();
1015  if(!geomSnapped->isValid()) {
1016  delete geomSnapped;
1017  }
1018  else {
1019  delete geomFirst;
1020  geomFirst = geomSnapped;
1021  }
1022 
1023  unsigned int precision = 15;
1024  unsigned int minPrecision = 13;
1025  while(failed) {
1026  try {
1027  // C++11: the geos BinaryOp returns an auto_ptr, we use release() to create a unique_ptr.
1028  std::unique_ptr< geos::geom::Geometry > resultAuto(
1029  BinaryOp(geomFirst, geomSecond, geos::operation::overlay::overlayOp(code)).release());
1030  failed = false;
1031  result = resultAuto->clone();
1032  }
1033  catch(geos::util::GEOSException *exc) {
1034  // Just in case the clone failed....
1035  if(!failed || precision == minPrecision) throw;
1036 
1037  delete exc;
1038  }
1039  catch(...) {
1040  if(precision == minPrecision) {
1041  IString msg = "An unknown geos error occurred";
1042  throw IException(IException::Programmer, msg, _FILEINFO_);
1043  }
1044 
1045  if(!failed) {
1046  IString msg = "An unknown geos error occurred when attempting to clone a geometry";
1047  throw IException(IException::Programmer, msg, _FILEINFO_);
1048  }
1049  }
1050 
1051  // try reducing the precision
1052  if(failed) {
1053  precision --;
1054  geos::geom::Geometry *tmp = ReducePrecision(geomFirst, precision);
1055  delete geomFirst;
1056  geomFirst = tmp;
1057 
1058  tmp = ReducePrecision(geomSecond, precision);
1059  delete geomSecond;
1060  geomSecond = tmp;
1061  }
1062  }
1063 
1064  delete geomFirst;
1065  geomFirst = NULL;
1066 
1067  delete geomSecond;
1068  geomSecond = NULL;
1069 
1070  if(result && !result->isValid()) {
1071  try {
1072  geos::geom::Geometry *newResult = FixGeometry(result);
1073 
1074  if(fabs(newResult->getArea() / result->getArea() - 1.0) > 0.50) {
1075  delete newResult;
1076  delete result;
1077 
1078  IString msg = "Operation [" + IString((int)opcode) + "] failed";
1079  throw IException(IException::Programmer, msg, _FILEINFO_);
1080  }
1081 
1082  delete result;
1083  result = newResult;
1084  }
1085  catch(IException &e) {
1086  IString msg = "Operation [" + IString((int)opcode) + "] failed";
1087  throw IException(IException::Programmer, msg, _FILEINFO_);
1088  }
1089  }
1090 
1091  if(result == NULL) {
1092  IString msg = "Operation [" + IString((int)opcode) + " failed";
1093  throw IException(IException::Programmer, msg, _FILEINFO_);
1094  }
1095 
1096  return result;
1097  }
1098 
1109  geos::geom::Geometry *PolygonTools::FixGeometry(const geos::geom::Geometry *geom) {
1110  if(geom->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
1111  return FixGeometry(dynamic_cast<const geos::geom::MultiPolygon *>(geom));
1112  }
1113  if(geom->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
1114  return FixGeometry(dynamic_cast<const geos::geom::LinearRing *>(geom));
1115  }
1116  if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
1117  return FixGeometry(dynamic_cast<const geos::geom::Polygon *>(geom));
1118  }
1119  if(geom->getGeometryTypeId() == geos::geom::GEOS_GEOMETRYCOLLECTION) {
1120  return FixGeometry(MakeMultiPolygon(geom));
1121  }
1122  else {
1123  IString msg = "PolygonTools::FixGeometry does not support [" + GetGeometryName(geom) + "]";
1124  throw IException(IException::Programmer, msg, _FILEINFO_);
1125  }
1126  }
1127 
1137  geos::geom::MultiPolygon *PolygonTools::FixGeometry(const geos::geom::MultiPolygon *poly) {
1138  // Maybe two points are on top of each other
1139  vector<geos::geom::Geometry *> *newPolys = new vector<geos::geom::Geometry *>;
1140 
1141  // Convert each polygon in this multi-polygon
1142  for(unsigned int geomIndex = 0; geomIndex < poly->getNumGeometries(); geomIndex ++) {
1143  geos::geom::Polygon *fixedpoly = FixGeometry(
1144  dynamic_cast<const geos::geom::Polygon *>(
1145  poly->getGeometryN(geomIndex)));
1146  if(fixedpoly->isValid()) {
1147  newPolys->push_back(fixedpoly);
1148  }
1149  else {
1150  delete fixedpoly;
1151  }
1152  fixedpoly = NULL;
1153  }
1154 
1155  geos::geom::MultiPolygon *mp = Isis::globalFactory.createMultiPolygon(newPolys);
1156  return mp;
1157  }
1158 
1159 
1160  geos::geom::Polygon *PolygonTools::FixGeometry(const geos::geom::Polygon *poly) {
1161 
1162  // Convert each hole inside this polygon
1163  vector<geos::geom::Geometry *> *holes = new vector<geos::geom::Geometry *>;
1164  for(unsigned int holeIndex = 0; holeIndex < poly->getNumInteriorRing(); holeIndex ++) {
1165  const geos::geom::LineString *thisHole = poly->getInteriorRingN(holeIndex);
1166 
1167  // We hope they are all linear rings (closed/simple), but if not just leave it be
1168  if(thisHole->getGeometryTypeId() != geos::geom::GEOS_LINEARRING) {
1169  holes->push_back(thisHole->clone());
1170  continue;
1171  }
1172 
1173  try {
1174  geos::geom::LinearRing *newHole = FixGeometry((geos::geom::LinearRing *)thisHole);
1175  holes->push_back(newHole);
1176  }
1177  catch (IException &e) {
1178  IString msg = "Failed when attempting to fix interior ring of multipolygon";
1179  throw IException(IException::Programmer, msg, _FILEINFO_);
1180  }
1181  } // end num holes in polygon loop
1182 
1183 
1184  const geos::geom::LineString *exterior = poly->getExteriorRing();
1185 
1186  try {
1187  geos::geom::LinearRing *newExterior = NULL;
1188 
1189  if(exterior->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
1190  newExterior = FixGeometry((geos::geom::LinearRing *)exterior);
1191  }
1192  else {
1193  IString msg = "Failed when attempting to fix exterior ring of polygon. The exterior "
1194  "ring is not simple and closed";
1195  throw IException(IException::Programmer, msg, _FILEINFO_);
1196  }
1197 
1198  return globalFactory.createPolygon(newExterior, holes);
1199  }
1200  catch (IException &e) {
1201  IString msg = "Failed when attempting to fix exterior ring of polygon";
1202  throw IException(e, IException::Programmer, msg, _FILEINFO_);
1203  }
1204  }
1205 
1206 
1222  geos::geom::LinearRing *PolygonTools::FixGeometry(const geos::geom::LinearRing *ring) {
1223 
1224  geos::geom::CoordinateSequence *coords = ring->getCoordinates();
1225 
1226  // Check this, just in case
1227  if(coords->getSize() < 4) {
1228  return globalFactory.createLinearRing(new geos::geom::DefaultCoordinateSequence());
1229  }
1230 
1231  geos::geom::CoordinateSequence *newCoords = new geos::geom::DefaultCoordinateSequence();
1232  const geos::geom::Coordinate *lastCoordinate = &coords->getAt(0);
1233  newCoords->add(*lastCoordinate);
1234 
1235  // Convert each coordinate in this hole
1236  for(unsigned int coordIndex = 1; coordIndex < coords->getSize() - 1; coordIndex ++) {
1237  const geos::geom::Coordinate *thisCoordinate = &coords->getAt(coordIndex);
1238 
1239  // we're going to compare the decimal place of the current point to the decimal place
1240  // of the difference, if they are drastically different then geos might not be seeing them
1241  // correctly.
1242  double difference[2] = {
1243  lastCoordinate->x - thisCoordinate->x,
1244  lastCoordinate->y - thisCoordinate->y,
1245  };
1246 
1247  // geos isnt differentiating between points this close
1248  double minDiff = fabs(DecimalPlace(thisCoordinate->x) - DecimalPlace(difference[0]));
1249 
1250  minDiff = min(minDiff, fabs(DecimalPlace(thisCoordinate->y) - DecimalPlace(difference[1])));
1251 
1252  // Cases where the difference in one direction is exactly zero, and the other direction is
1253  // next to zero appear often enough (especially in despike).
1254  if(difference[0] == 0.0 && difference[1] != 0.0) {
1255  // subtracted the two points, got deltaX = 0.0, use the y difference decimal place
1256  minDiff = fabs(DecimalPlace(thisCoordinate->y) - DecimalPlace(difference[1]));
1257  }
1258  else if(difference[1] == 0.0 && difference[0] != 0.0) {
1259  // subtracted the two points, got deltaY = 0.0, use the x difference decimal place
1260  minDiff = fabs(DecimalPlace(thisCoordinate->x) - DecimalPlace(difference[0]));
1261  }
1262  else if(difference[0] == 0.0 && difference[1] == 0.0) {
1263  // subtracted the two points, got 0.0, so it's same point... make sure it gets ignored!
1264  minDiff = 1E99;
1265  }
1266 
1267  // geos has a hard time differentiating when points get too close...
1268  if(minDiff <= 15) {
1269  newCoords->add(*thisCoordinate);
1270  lastCoordinate = thisCoordinate;
1271  }
1272  } // end num coords in hole loop
1273 
1274  newCoords->add(geos::geom::Coordinate(newCoords->getAt(0).x, newCoords->getAt(0).y));
1275  geos::geom::LinearRing *newRing = NULL;
1276 
1277  // Now that we've weeded out any bad coordinates, let's rebuild the geometry
1278  try {
1279  if(newCoords->getSize() > 3) {
1280  newRing = globalFactory.createLinearRing(newCoords);
1281  }
1282  else {
1283  delete newCoords;
1284  newCoords = NULL;
1285  }
1286  }
1287  catch(geos::util::GEOSException *exc) {
1288  delete exc;
1289  exc = NULL;
1290 
1291  IString msg = "Error when attempting to fix linear ring";
1292  throw IException(IException::Programmer, msg, _FILEINFO_);
1293  }
1294 
1295  if(newRing && !newRing->isValid() && ring->isValid()) {
1296  IString msg = "Failed when attempting to fix linear ring";
1297  throw IException(IException::Programmer, msg, _FILEINFO_);
1298  }
1299  else if(!newRing || !newRing->isValid()) {
1300  if(newRing) {
1301  delete newRing;
1302  }
1303 
1304  newRing = dynamic_cast<geos::geom::LinearRing *>(ring->clone());
1305  }
1306 
1307  return newRing;
1308  }
1309 
1310 
1322  int PolygonTools::DecimalPlace(double num) {
1323  // 0.0 = decimal place 0
1324  if(num == 0.0) return 0;
1325 
1326  num = fabs(num);
1327 
1328  int decimalPlace = 1;
1329  while(num < 1.0) {
1330  num *= 10.0;
1331  decimalPlace --;
1332  }
1333 
1334  while(num > 10.0) {
1335  num /= 10.0;
1336  decimalPlace ++;
1337  }
1338 
1339  return decimalPlace;
1340  }
1341 
1350  geos::geom::Geometry *PolygonTools::Difference(const geos::geom::Geometry *geom1,
1351  const geos::geom::Geometry *geom2) {
1352  try {
1353  return Operate(geom1, geom2, (unsigned int)geos::operation::overlay::OverlayOp::opDIFFERENCE);
1354  }
1355  catch(geos::util::GEOSException *exc) {
1356  IString msg = "Difference operation failed. The reason given was [" +
1357  IString(exc->what()) + "]";
1358  delete exc;
1359  throw IException(IException::Programmer, msg, _FILEINFO_);
1360  }
1361  catch(IException &e) {
1362  IString msg = "Difference operation failed";
1363  throw IException(e, IException::Programmer, msg, _FILEINFO_);
1364  }
1365  catch(...) {
1366  IString msg = "Difference operation failed for an unknown reason";
1367  throw IException(IException::Programmer, msg, _FILEINFO_);
1368  }
1369  }
1370 
1371 
1385  geos::geom::MultiPolygon *PolygonTools::MakeMultiPolygon(const geos::geom::Geometry *geom) {
1386  // The area of the geometry is too small, so just ignore it.
1387  if(geom->isEmpty()) {
1388  return Isis::globalFactory.createMultiPolygon();
1389  }
1390 
1391  else if(geom->getArea() - DBL_EPSILON <= DBL_EPSILON) {
1392  return Isis::globalFactory.createMultiPolygon();
1393  }
1394 
1395  else if(geom->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
1396  return dynamic_cast<geos::geom::MultiPolygon *>(geom->clone());
1397  }
1398 
1399  else if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
1400  vector<geos::geom::Geometry *> *polys = new vector<geos::geom::Geometry *>;
1401  polys->push_back(geom->clone());
1402  geos::geom::MultiPolygon *mp = Isis::globalFactory.createMultiPolygon(polys);
1403  return mp;
1404  }
1405 
1406  else if(geom->getGeometryTypeId() == geos::geom::GEOS_GEOMETRYCOLLECTION) {
1407  vector<geos::geom::Geometry *> * polys =
1408  new vector<geos::geom::Geometry *>;
1409  const geos::geom::GeometryCollection *gc =
1410  dynamic_cast<const geos::geom::GeometryCollection *>(geom);
1411  for(unsigned int i = 0; i < gc->getNumGeometries(); ++i) {
1412  geos::geom::MultiPolygon *subMultiPoly =
1413  MakeMultiPolygon(gc->getGeometryN(i));
1414 
1415  for(unsigned int subPoly = 0;
1416  subPoly < subMultiPoly->getNumGeometries();
1417  subPoly ++) {
1418  const geos::geom::Polygon *poly =
1419  dynamic_cast<const geos::geom::Polygon *>(
1420  subMultiPoly->getGeometryN(subPoly));
1421  polys->push_back(dynamic_cast<geos::geom::Polygon *>(poly->clone()));
1422  }
1423  }
1424 
1425  geos::geom::MultiPolygon *mp =
1426  Isis::globalFactory.createMultiPolygon(polys);
1427  if(mp->getArea() - DBL_EPSILON <= DBL_EPSILON) {
1428  delete mp;
1429  mp = Isis::globalFactory.createMultiPolygon();
1430  }
1431 
1432  return mp;
1433  }
1434 
1435  // All other geometry types are invalid so ignore them
1436  else {
1437  return Isis::globalFactory.createMultiPolygon();
1438  }
1439  }
1440 
1441 
1442  geos::geom::MultiPolygon *PolygonTools::FixSeam(
1443  const geos::geom::Polygon *polyA, const geos::geom::Polygon *polyB) {
1444  geos::geom::CoordinateSequence *polyAPoints = polyA->getCoordinates();
1445  geos::geom::CoordinateSequence *polyBPoints = polyB->getCoordinates();
1446 
1447  unsigned int aIntersectionBegin = 0;
1448  unsigned int aIntersectionEnd = 0;
1449  unsigned int bIntersectionBegin = 0;
1450  unsigned int bIntersectionEnd = 0;
1451 
1452  bool intersectionStarted = false;
1453  bool intersectionEnded = false;
1454 
1455  unsigned int lastBMatch = 0;
1456  for (unsigned int i = 0;
1457  !intersectionEnded && i < polyAPoints->getSize();
1458  i++) {
1459 
1460  bool foundEquivalent = false;
1461 
1462  geos::geom::Coordinate coordA = polyAPoints->getAt(i);
1463  coordA = *ReducePrecision(&coordA, 13);
1464 
1465  for (unsigned int j = 0;
1466  !foundEquivalent && j < polyBPoints->getSize();
1467  j++) {
1468  geos::geom::Coordinate coordB = polyBPoints->getAt(j);
1469  coordB = *ReducePrecision(&coordB, 13);
1470 
1471  foundEquivalent = coordA.equals(coordB);
1472 
1473  if (foundEquivalent) lastBMatch = j;
1474 
1475  if (foundEquivalent && !intersectionStarted) {
1476  intersectionStarted = true;
1477  aIntersectionBegin = i;
1478  bIntersectionBegin = j;
1479  }
1480  }
1481 
1482  if (!foundEquivalent && intersectionStarted && !intersectionEnded) {
1483  intersectionEnded = true;
1484  aIntersectionEnd = i;
1485  bIntersectionEnd = lastBMatch;
1486  }
1487  }
1488 
1489  geos::geom::MultiPolygon * result = NULL;
1490  if (intersectionStarted && intersectionEnded) {
1491  geos::geom::CoordinateSequence *merged =
1492  new geos::geom::CoordinateArraySequence;
1493 
1494  unsigned int i = 0;
1495  for (i = 0; i < aIntersectionBegin; i ++) {
1496  merged->add(polyAPoints->getAt(i));
1497  }
1498 
1499  i = bIntersectionBegin;
1500  while (i != bIntersectionEnd) {
1501  merged->add(polyBPoints->getAt(i));
1502  i++;
1503  if (i >= polyBPoints->getSize()) i = 0;
1504  }
1505 
1506  for (i = aIntersectionEnd; i < polyAPoints->getSize() - 1; i++) {
1507  merged->add(polyAPoints->getAt(i));
1508  }
1509 
1510  merged->add(merged->getAt(0));
1511  result = MakeMultiPolygon(globalFactory.createPolygon(
1512  globalFactory.createLinearRing(merged), NULL));
1513  }
1514 
1515  return result;
1516  }
1517 
1518 
1519  geos::geom::MultiPolygon *PolygonTools::FixSeam(
1520  const geos::geom::MultiPolygon *poly) {
1521 
1522  std::vector<geos::geom::Geometry *> *polys =
1523  new std::vector<geos::geom::Geometry *>;
1524 
1525 
1526  for(unsigned int copyIndex = 0;
1527  copyIndex < poly->getNumGeometries();
1528  copyIndex ++) {
1529  polys->push_back(poly->getGeometryN(copyIndex)->clone());
1530  }
1531 
1532  unsigned int outerPolyIndex = 0;
1533 
1534  while(outerPolyIndex + 1 < polys->size()) {
1535  unsigned int innerPolyIndex = outerPolyIndex + 1;
1536 
1537  while(innerPolyIndex < polys->size()) {
1538  geos::geom::MultiPolygon *fixedPair = FixSeam(
1539  dynamic_cast<geos::geom::Polygon *>(polys->at(outerPolyIndex)),
1540  dynamic_cast<geos::geom::Polygon *>(polys->at(innerPolyIndex)));
1541 
1542  if(fixedPair != NULL) {
1543  geos::geom::Geometry *oldInnerPoly = polys->at(innerPolyIndex);
1544  geos::geom::Geometry *oldOuterPoly = polys->at(outerPolyIndex);
1545 
1546  polys->erase(polys->begin() + innerPolyIndex);
1547  (*polys)[outerPolyIndex] = fixedPair->getGeometryN(0)->clone();
1548  innerPolyIndex = outerPolyIndex + 1;
1549 
1550  delete fixedPair;
1551  fixedPair = NULL;
1552 
1553  delete oldInnerPoly;
1554  oldInnerPoly = NULL;
1555 
1556  delete oldOuterPoly;
1557  oldOuterPoly = NULL;
1558  }
1559  else {
1560  innerPolyIndex ++;
1561  }
1562  }
1563 
1564  outerPolyIndex ++;
1565  }
1566 
1567  return globalFactory.createMultiPolygon(polys);
1568  }
1569 
1570 
1580  geos::geom::Geometry *PolygonTools::ReducePrecision(const geos::geom::Geometry *geom,
1581  unsigned int precision) {
1582  if(geom->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
1583  return ReducePrecision(
1584  dynamic_cast<const geos::geom::MultiPolygon *>(geom), precision);
1585  }
1586  if(geom->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
1587  return ReducePrecision(
1588  dynamic_cast<const geos::geom::LinearRing *>(geom), precision);
1589  }
1590  if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
1591  return ReducePrecision(
1592  dynamic_cast<const geos::geom::Polygon *>(geom), precision);
1593  }
1594  if(geom->getGeometryTypeId() == geos::geom::GEOS_GEOMETRYCOLLECTION) {
1595  return ReducePrecision(MakeMultiPolygon(geom), precision);
1596  }
1597  else {
1598  IString msg = "PolygonTools::ReducePrecision does not support [" +
1599  GetGeometryName(geom) + "]";
1600  throw IException(IException::Programmer, msg, _FILEINFO_);
1601  }
1602  }
1603 
1604 
1614  geos::geom::MultiPolygon *PolygonTools::ReducePrecision(const geos::geom::MultiPolygon *poly,
1615  unsigned int precision) {
1616  // Maybe two points are on top of each other
1617  vector<geos::geom::Geometry *> *newPolys = new vector<geos::geom::Geometry *>;
1618 
1619  // Convert each polygon in this multi-polygon
1620  for(unsigned int geomIndex = 0; geomIndex < poly->getNumGeometries(); geomIndex ++) {
1621  geos::geom::Geometry *lowerPrecision = ReducePrecision(
1622  dynamic_cast<const geos::geom::Polygon *>(
1623  poly->getGeometryN(geomIndex)),
1624  precision);
1625 
1626  if(!lowerPrecision->isEmpty()) {
1627  newPolys->push_back(lowerPrecision);
1628  }
1629  else {
1630  delete lowerPrecision;
1631  }
1632  }
1633 
1634  geos::geom::MultiPolygon *mp = Isis::globalFactory.createMultiPolygon(newPolys);
1635  return mp;
1636  }
1637 
1638 
1648  geos::geom::Polygon *PolygonTools::ReducePrecision(const geos::geom::Polygon *poly,
1649  unsigned int precision) {
1650  // Convert each hole inside this polygon
1651  vector<geos::geom::Geometry *> *holes = new vector<geos::geom::Geometry *>;
1652  for(unsigned int holeIndex = 0; holeIndex < poly->getNumInteriorRing(); holeIndex ++) {
1653  const geos::geom::LineString *thisHole = poly->getInteriorRingN(holeIndex);
1654 
1655  // We hope they are all linear rings (closed/simple), but if not just leave it be
1656  if(thisHole->getGeometryTypeId() != geos::geom::GEOS_LINEARRING) {
1657  holes->push_back(thisHole->clone());
1658  continue;
1659  }
1660 
1661  try {
1662  geos::geom::LinearRing *newHole = ReducePrecision((geos::geom::LinearRing *)thisHole,
1663  precision);
1664 
1665  if(!newHole->isEmpty()) {
1666  holes->push_back(newHole);
1667  }
1668  else {
1669  delete newHole;
1670  }
1671 
1672  }
1673  catch(IException &e) {
1674  IString msg = "Failed when attempting to fix interior ring of multipolygon";
1675  throw IException(e, IException::Programmer, msg, _FILEINFO_);
1676  }
1677  } // end num holes in polygon loop
1678 
1679 
1680  const geos::geom::LineString *exterior = poly->getExteriorRing();
1681 
1682  try {
1683  geos::geom::LinearRing *newExterior = NULL;
1684 
1685  if(exterior->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
1686  newExterior = ReducePrecision((geos::geom::LinearRing *)exterior, precision);
1687  }
1688  else {
1689  IString msg = "Failed when attempting to fix exterior ring of polygon. The exterior "
1690  "ring is not simple and closed";
1691  throw IException(IException::Programmer, msg, _FILEINFO_);
1692  }
1693 
1694  return globalFactory.createPolygon(newExterior, holes);
1695  }
1696  catch(IException &e) {
1697  IString msg = "Failed when attempting to fix exterior ring of polygon";
1698  throw IException(e, IException::Programmer, msg, _FILEINFO_);
1699  }
1700  }
1701 
1702 
1712  geos::geom::LinearRing *PolygonTools::ReducePrecision(const geos::geom::LinearRing *ring,
1713  unsigned int precision) {
1714  geos::geom::CoordinateSequence *coords = ring->getCoordinates();
1715 
1716  // Check this, just in case
1717  if(coords->getSize() <= 0) return dynamic_cast<geos::geom::LinearRing *>(
1718  ring->clone());
1719 
1720  geos::geom::CoordinateSequence *newCoords = new geos::geom::DefaultCoordinateSequence();
1721  geos::geom::Coordinate *coord = ReducePrecision(&coords->getAt(0), precision);
1722  newCoords->add(*coord);
1723  delete coord;
1724  coord = NULL;
1725 
1726  // Convert each coordinate in this ring
1727  for(unsigned int coordIndex = 1; coordIndex < coords->getSize() - 1; coordIndex ++) {
1728  const geos::geom::Coordinate *thisCoordinate = &coords->getAt(coordIndex);
1729  coord = ReducePrecision(thisCoordinate, precision);
1730  newCoords->add(*coord);
1731  delete coord;
1732  coord = NULL;
1733  }
1734 
1735  newCoords->add(geos::geom::Coordinate(newCoords->getAt(0).x, newCoords->getAt(0).y));
1736  geos::geom::LinearRing *newRing = NULL;
1737 
1738  // Now that we've weeded out any bad coordinates, let's rebuild the geometry
1739  try {
1740  newRing = globalFactory.createLinearRing(newCoords);
1741  }
1742  catch(geos::util::GEOSException *exc) {
1743  delete exc;
1744  exc = NULL;
1745 
1746  IString msg = "Error when attempting to reduce precision of linear ring";
1747  throw IException(IException::Programmer, msg, _FILEINFO_);
1748  }
1749 
1750  // try to despike
1751  try {
1752  geos::geom::LinearRing *tmp = Despike(newRing);
1753  delete newRing;
1754  newRing = tmp;
1755  }
1756  catch(IException &e) {
1757  }
1758 
1759  if(!newRing->isValid()) {
1760  IString msg = "Failed when attempting to reduce precision of linear ring";
1761  throw IException(IException::Programmer, msg, _FILEINFO_);
1762  }
1763 
1764  return newRing;
1765  }
1766 
1767 
1777  geos::geom::Coordinate *PolygonTools::ReducePrecision(const geos::geom::Coordinate *coord,
1778  unsigned int precision) {
1779  return new geos::geom::Coordinate(
1780  ReducePrecision(coord->x, precision),
1781  ReducePrecision(coord->y, precision),
1782  ReducePrecision(coord->z, precision)
1783  );
1784  }
1785 
1786 
1796  double PolygonTools::ReducePrecision(double num, unsigned int precision) {
1797  double result = num;
1798 
1799  // If num == nan then this test will fail
1800  if (num == num) {
1801  int decimalPlace = DecimalPlace(num);
1802  double factor = pow(10.0, (int)decimalPlace);
1803 
1804  // reduced num is in the form 0.nnnnnnnnnn...
1805  double reducedNum = num / factor;
1806 
1807  double cutoff = pow(10.0, (int)precision);
1808  double round_offset = (num < 0) ? -0.5 : 0.5;
1809 
1810  // cast off the digits past the precision's place
1811  reducedNum = ((long long)(reducedNum * cutoff + round_offset)) / cutoff;
1812  result = reducedNum * factor;
1813  }
1814 
1815  return result;
1816  }
1817 
1818 
1827  QString PolygonTools::GetGeometryName(const geos::geom::Geometry *geom) {
1828  switch(geom->getGeometryTypeId()) {
1829  case geos::geom::GEOS_POINT:
1830  return "Point";
1831  case geos::geom::GEOS_LINESTRING:
1832  return "Line String";
1833  case geos::geom::GEOS_LINEARRING:
1834  return "Linear Ring";
1835  case geos::geom::GEOS_POLYGON:
1836  return "Polygon";
1837  case geos::geom::GEOS_MULTIPOINT:
1838  return "Multi Point";
1839  case geos::geom::GEOS_MULTILINESTRING:
1840  return "Multi Line String";
1841  case geos::geom::GEOS_MULTIPOLYGON:
1842  return "Multi Polygon";
1843  case geos::geom::GEOS_GEOMETRYCOLLECTION:
1844  return "Geometry Collection";
1845  default:
1846  return "UNKNOWN";
1847  }
1848  }
1849 
1850 
1851  bool PolygonTools::Equal(const geos::geom::MultiPolygon *poly1,
1852  const geos::geom::MultiPolygon *poly2) {
1853 
1854  vector<const geos::geom::Polygon *> polys1;
1855  vector<const geos::geom::Polygon *> polys2;
1856 
1857  if(poly1->getNumGeometries() != poly2->getNumGeometries()) return false;
1858 
1859  // Convert each polygon in this multi-polygon
1860  for(unsigned int geomIndex = 0; geomIndex < poly1->getNumGeometries(); geomIndex ++) {
1861  polys1.push_back(dynamic_cast<const geos::geom::Polygon *>(
1862  poly1->getGeometryN(geomIndex)));
1863  polys2.push_back(dynamic_cast<const geos::geom::Polygon *>(
1864  poly2->getGeometryN(geomIndex)));
1865  }
1866 
1867  for(int p1 = polys1.size() - 1; (p1 >= 0) && polys1.size(); p1 --) {
1868  for(int p2 = polys2.size() - 1; (p2 >= 0) && polys2.size(); p2 --) {
1869  if(Equal(polys1[p1], polys2[p2])) {
1870  // Delete polys1[p1] by replacing it with the last Polygon in polys1
1871  polys1[p1] = polys1[polys1.size()-1];
1872  polys1.resize(polys1.size() - 1);
1873  // Delete polys2[p2] by replacing it with the last Polygon in polys2
1874  polys2[p2] = polys2[polys2.size()-1];
1875  polys2.resize(polys2.size() - 1);
1876  }
1877  }
1878  }
1879 
1880  return (polys1.size() == 0) && (polys2.size() == 0);
1881  }
1882 
1883 
1884  bool PolygonTools::Equal(const geos::geom::Polygon *poly1, const geos::geom::Polygon *poly2) {
1885  vector<const geos::geom::LineString *> holes1;
1886  vector<const geos::geom::LineString *> holes2;
1887 
1888  if(poly1->getNumInteriorRing() != poly2->getNumInteriorRing()) return false;
1889 
1890  if(!Equal(poly1->getExteriorRing(), poly2->getExteriorRing())) return false;
1891 
1892  // Convert each hole inside this polygon
1893  for(unsigned int holeIndex = 0; holeIndex < poly1->getNumInteriorRing(); holeIndex ++) {
1894 
1895  // We hope they are all linear rings (closed/simple), but if not just leave it be
1896  if(poly1->getInteriorRingN(holeIndex)->getGeometryTypeId() == geos::geom::GEOS_LINESTRING) {
1897  holes1.push_back(poly1->getInteriorRingN(holeIndex));
1898  }
1899 
1900  if(poly2->getInteriorRingN(holeIndex)->getGeometryTypeId() == geos::geom::GEOS_LINESTRING) {
1901  holes2.push_back(poly2->getInteriorRingN(holeIndex));
1902  }
1903 
1904  }
1905 
1906  if(holes1.size() != holes2.size()) return false;
1907 
1908  for(int h1 = holes1.size() - 1; (h1 >= 0) && holes1.size(); h1 --) {
1909  for(int h2 = holes2.size() - 1; (h2 >= 0) && holes2.size(); h2 --) {
1910  if(Equal(holes1[h1], holes2[h2])) {
1911  // Delete holes1[h1] by replacing it with the last Polygon in holes1
1912  holes1[h1] = holes1[holes1.size()-1];
1913  holes1.resize(holes1.size() - 1);
1914  // Delete holes2[h2] by replacing it with the last Polygon in holes2
1915  holes2[h2] = holes2[holes2.size()-1];
1916  holes2.resize(holes2.size() - 1);
1917  }
1918  }
1919  }
1920 
1921  return (holes1.size() == 0) && (holes2.size() == 0);
1922  }
1923 
1924 
1925  bool PolygonTools::Equal(const geos::geom::LineString *lineString1,
1926  const geos::geom::LineString *lineString2) {
1927 
1928  geos::geom::CoordinateSequence *coords1 = lineString1->getCoordinates();
1929  geos::geom::CoordinateSequence *coords2 = lineString2->getCoordinates();
1930  bool isEqual = true;
1931 
1932  if(coords1->getSize() != coords2->getSize()) isEqual = false;
1933 
1934  unsigned int index1 = 0;
1935  unsigned int index2 = 0;
1936 
1937  // -1 extra for dupicate start/end coordinates
1938  for(; index2 < coords2->getSize() - 1 && isEqual; index2 ++) {
1939  if(Equal(coords1->getAt(index1), coords2->getAt(index2))) break;
1940  }
1941 
1942  if(index2 == coords2->getSize() - 1) isEqual = false;
1943 
1944  for(; index1 < coords1->getSize() - 1 && isEqual; index1 ++, index2 ++) {
1945  if(!Equal(coords1->getAt(index1), coords2->getAt(index2 % (coords2->getSize() - 1)))) {
1946  isEqual = false;
1947  }
1948  }
1949 
1950  delete coords1;
1951  delete coords2;
1952  return isEqual;
1953  }
1954 
1955 
1956  bool PolygonTools::Equal(const geos::geom::Coordinate &coord1,
1957  const geos::geom::Coordinate &coord2) {
1958 
1959  if(!Equal(coord1.x, coord2.x)) return false;
1960  if(!Equal(coord1.y, coord2.y)) return false;
1961  if(!Equal(coord1.y, coord2.y)) return false;
1962 
1963  return true;
1964  }
1965 
1966 
1967  bool PolygonTools::Equal(const double d1, const double d2) {
1968  const double cutoff = 1e15;
1969 
1970  if(DecimalPlace(d1) != DecimalPlace(d2)) return false;
1971 
1972  int decimalPlace = DecimalPlace(d1);
1973  double factor = pow(10.0, (int)decimalPlace);
1974 
1975  // reduced num is in the form 0.nnnnnnnnnn...
1976  double reducedNum = d1 / factor;
1977 
1978  double round_offset = (d1 < 0) ? -0.5 : 0.5;
1979 
1980  // cast off the digits past the precision's place
1981  long long num1 = ((long long)(reducedNum * cutoff + round_offset));
1982 
1983  factor = pow(10.0, (int)decimalPlace);
1984 
1985  // reduced num is in the form 0.nnnnnnnnnn...
1986  reducedNum = d2 / factor;
1987 
1988  round_offset = (d2 < 0) ? -0.5 : 0.5;
1989 
1990  // cast off the digits past the precision's place
1991  long long num2 = ((long long)(reducedNum * cutoff + round_offset));
1992 
1993 
1994  return (num1 == num2);
1995  }
1996 
1997 
2012  geos::geom::MultiPolygon *PolygonTools::SplitPolygonOn360(const geos::geom::Polygon *inPoly) {
2013  bool convertLon = false;
2014  bool negAdjust = false;
2015  bool newCoords = false; // coordinates have been adjusted
2016  geos::geom::CoordinateSequence *newLonLatPts = new geos::geom::CoordinateArraySequence();
2017  double lon, lat;
2018  double lonOffset = 0;
2019  geos::geom::CoordinateSequence *inPolyCoords = inPoly->getCoordinates();
2020  double prevLon = inPolyCoords->getAt(0).x;
2021  double prevLat = inPolyCoords->getAt(0).y;
2022 
2023  newLonLatPts->add(geos::geom::Coordinate(prevLon, prevLat));
2024  double dist = 0.0;
2025  for (unsigned int i = 1; i < inPolyCoords->getSize(); i++) {
2026  lon = inPolyCoords->getAt(i).x;
2027  lat = inPolyCoords->getAt(i).y;
2028  // check to see if you just crossed the Meridian
2029  if (abs(lon - prevLon) > 180 && (prevLat != 90 && prevLat != -90)) {
2030  newCoords = true;
2031  // if you were already converting then stop (crossed Meridian even number of times)
2032  if (convertLon) {
2033  convertLon = false;
2034  lonOffset = 0;
2035  }
2036  else { // Need to start converting again, deside how to adjust coordinates
2037  if ((lon - prevLon) > 0) {
2038  lonOffset = -360.;
2039  negAdjust = true;
2040  }
2041  else if ((lon - prevLon) < 0) {
2042  lonOffset = 360.;
2043  negAdjust = false;
2044  }
2045  convertLon = true;
2046  }
2047  }
2048 
2049  // Change to a minimum calculation
2050  if (newCoords && dist == 0.0) {
2051  double longitude = (lon + lonOffset) - prevLon;
2052  double latitude = lat - prevLat;
2053  dist = std::sqrt((longitude * longitude) + (latitude * latitude));
2054  }
2055 
2056  // add coord
2057  newLonLatPts->add(geos::geom::Coordinate(lon + lonOffset, lat));
2058 
2059  // set current to old
2060  prevLon = lon;
2061  prevLat = lat;
2062  }
2063 
2064  delete inPolyCoords;
2065 
2066  // Nothing was done so return
2067  if (!newCoords) {
2068  geos::geom::Polygon *newPoly = globalFactory.createPolygon
2069  (globalFactory.createLinearRing(newLonLatPts), NULL);
2070  geos::geom::MultiPolygon *multi_polygon = PolygonTools::MakeMultiPolygon(newPoly);
2071  delete newLonLatPts;
2072  return multi_polygon;
2073  }
2074 
2075  // bisect into seperate polygons
2076  try {
2077  geos::geom::Polygon *newPoly = globalFactory.createPolygon
2078  (globalFactory.createLinearRing(newLonLatPts), NULL);
2079 
2080  geos::geom::CoordinateSequence *pts = new geos::geom::CoordinateArraySequence();
2081  geos::geom::CoordinateSequence *pts2 = new geos::geom::CoordinateArraySequence();
2082 
2083  // Depending on direction of compensation bound accordingly
2084  //***************************************************
2085 
2086  // please verify correct if you change these values
2087  //***************************************************
2088  if (negAdjust) {
2089  pts->add(geos::geom::Coordinate(0., 90.));
2090  pts->add(geos::geom::Coordinate(-360., 90.));
2091  pts->add(geos::geom::Coordinate(-360., -90.));
2092  pts->add(geos::geom::Coordinate(0., -90.));
2093  for (double lat = -90.0 + dist; lat < 90.0; lat += dist) {
2094  pts->add(geos::geom::Coordinate(0.0, lat));
2095  }
2096  pts->add(geos::geom::Coordinate(0., 90.));
2097  pts2->add(geos::geom::Coordinate(0., 90.));
2098  pts2->add(geos::geom::Coordinate(360., 90.));
2099  pts2->add(geos::geom::Coordinate(360., -90.));
2100  pts2->add(geos::geom::Coordinate(0., -90.));
2101  for (double lat = -90.0 + dist; lat < 90.0; lat += dist) {
2102  pts2->add(geos::geom::Coordinate(0.0, lat));
2103  }
2104  pts2->add(geos::geom::Coordinate(0., 90.));
2105  }
2106  else {
2107  pts->add(geos::geom::Coordinate(360., 90.));
2108  pts->add(geos::geom::Coordinate(720., 90.));
2109  pts->add(geos::geom::Coordinate(720., -90.));
2110  pts->add(geos::geom::Coordinate(360., -90.));
2111  for (double lat = -90.0 + dist; lat < 90.0; lat += dist) {
2112  pts->add(geos::geom::Coordinate(360.0, lat));
2113  }
2114  pts->add(geos::geom::Coordinate(360., 90.));
2115  pts2->add(geos::geom::Coordinate(360., 90.));
2116  pts2->add(geos::geom::Coordinate(0., 90.));
2117  pts2->add(geos::geom::Coordinate(0., -90.));
2118  pts2->add(geos::geom::Coordinate(360., -90.));
2119  for (double lat = -90.0 + dist; lat < 90.0; lat += dist) {
2120  pts2->add(geos::geom::Coordinate(360.0, lat));
2121  }
2122  pts2->add(geos::geom::Coordinate(360., 90.));
2123  }
2124 
2125  geos::geom::Polygon *boundaryPoly = globalFactory.createPolygon
2126  (globalFactory.createLinearRing(pts), NULL);
2127  geos::geom::Polygon *boundaryPoly2 = globalFactory.createPolygon
2128  (globalFactory.createLinearRing(pts2), NULL);
2129  /*------------------------------------------------------------------------
2130  / Intersecting the original polygon (converted coordinates) with the
2131  / boundary polygons will create the multi polygons with the converted coordinates.
2132  / These will need to be converted back to the original coordinates.
2133  /-----------------------------------------------------------------------*/
2134  geos::geom::Geometry *intersection = PolygonTools::Intersect(newPoly, boundaryPoly);
2135  geos::geom::MultiPolygon *convertPoly = PolygonTools::MakeMultiPolygon(intersection);
2136  delete intersection;
2137 
2138  intersection = PolygonTools::Intersect(newPoly, boundaryPoly2);
2139  geos::geom::MultiPolygon *convertPoly2 = PolygonTools::MakeMultiPolygon(intersection);
2140  delete intersection;
2141 
2142  /*------------------------------------------------------------------------
2143  / Adjust points created in the negative space or >360 space to be back in
2144  / the 0-360 world. This will always only need to be done on convertPoly.
2145  / Then add geometries to finalpolys.
2146  /-----------------------------------------------------------------------*/
2147  vector<geos::geom::Geometry *> *finalpolys = new vector<geos::geom::Geometry *>;
2148  geos::geom::Geometry *newGeom = NULL;
2149 
2150  for (unsigned int i = 0; i < convertPoly->getNumGeometries(); i++) {
2151  newGeom = (convertPoly->getGeometryN(i))->clone();
2152  pts = convertPoly->getGeometryN(i)->getCoordinates();
2153  geos::geom::CoordinateSequence *newLonLatPts = new geos::geom::CoordinateArraySequence();
2154  // fix the points
2155 
2156  for (unsigned int k = 0; k < pts->getSize() ; k++) {
2157  double lon = pts->getAt(k).x;
2158  double lat = pts->getAt(k).y;
2159  if (negAdjust) {
2160  lon = lon + 360;
2161  }
2162  else {
2163  lon = lon - 360;
2164  }
2165  newLonLatPts->add(geos::geom::Coordinate(lon, lat), k);
2166 
2167  }
2168  // Add the points to polys
2169  finalpolys->push_back(globalFactory.createPolygon
2170  (globalFactory.createLinearRing(newLonLatPts), NULL));
2171  }
2172 
2173  // This loop is over polygons that will always be in 0-360 space no need to convert
2174  for (unsigned int i = 0; i < convertPoly2->getNumGeometries(); i++) {
2175  newGeom = (convertPoly2->getGeometryN(i))->clone();
2176  finalpolys->push_back(newGeom);
2177  }
2178 
2179  geos::geom::MultiPolygon *multi_polygon = globalFactory.createMultiPolygon(*finalpolys);
2180 
2181  delete finalpolys;
2182  delete newGeom;
2183  delete newLonLatPts;
2184  delete pts;
2185  delete pts2;
2186  return multi_polygon;
2187  }
2188  catch(geos::util::IllegalArgumentException *geosIll) {
2189  std::string msg = "Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
2190  msg += "geos illegal argument [" + IString(geosIll->what()) + "]";
2191  delete geosIll;
2192  throw IException(IException::Unknown, msg, _FILEINFO_);
2193  }
2194  catch(geos::util::GEOSException *geosExc) {
2195  std::string msg = "Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
2196  msg += "geos exception [" + IString(geosExc->what()) + "]";
2197  delete geosExc;
2198  throw IException(IException::Unknown, msg, _FILEINFO_);
2199  }
2200  catch(IException &e) {
2201  std::string msg = "Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
2202  msg += "isis operation exception [" + IString(e.what()) + "]";
2203  throw IException(e, IException::Unknown, msg, _FILEINFO_);
2204  }
2205  catch(...) {
2206  std::string msg = "Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
2207  msg += "unknown exception";
2208  throw IException(IException::Unknown, msg, _FILEINFO_);
2209  }
2210  }
2211 } // end namespace isis
2212 
const char * what() const
Returns a string representation of this exception in its current state.
Definition: IException.cpp:391
double Line() const
Returns the current line value of the camera model or projection.
Universal Ground Map.
Base class for Map TProjections.
Definition: TProjection.h:182
Namespace for the standard library.
double XCoord() const
This returns the projection X provided SetGround, SetCoordinate, SetUniversalGround, or SetWorld returned with success.
Definition: Projection.cpp:402
double Longitude() const
This returns a longitude with correct longitude direction and domain as specified in the label object...
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
virtual bool SetGround(const double lat, const double lon)
This method is used to set the latitude/longitude (assumed to be of the correct LatitudeType, LongitudeDirection, and LongitudeDomain.
double YCoord() const
This returns the projection Y provided SetGround, SetCoordinate, SetUniversalGround, or SetWorld returned with success.
Definition: Projection.cpp:415
bool SetWorld(const double x, const double y)
This method is used to set a world coordinate.
Definition: Projection.cpp:512
double Latitude() const
This returns a latitude with correct latitude type as specified in the label object.
Isis exception class.
Definition: IException.h:107
Adds specific functionality to C++ strings.
Definition: IString.h:181
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
bool SetUniversalGround(double lat, double lon)
Returns whether the lat/lon position was set successfully in the camera model or projection.
double Sample() const
Returns the current line value of the camera model or projection.