16 #include "geos/geom/BinaryOp.h" 
   17 #include "geos/geom/CoordinateArraySequence.h" 
   18 #include "geos/geom/CoordinateSequence.h" 
   19 #include "geos/geom/LinearRing.h" 
   20 #include "geos/geom/Point.h" 
   21 #include "geos/geom/Polygon.h" 
   22 #include "geos/operation/distance/DistanceOp.h" 
   23 #include "geos/opOverlay.h" 
   24 #include "geos/operation/overlay/snap/GeometrySnapper.h" 
   26 #include "SpecialPixel.h" 
   27 #include "PolygonTools.h" 
   28 #include "TProjection.h" 
   29 #include "ProjectionFactory.h" 
   30 #include "UniversalGroundMap.h" 
   47   geos::geom::MultiPolygon *PolygonTools::LatLonToXY(
 
   48       const geos::geom::MultiPolygon &lonLatPolygon, 
TProjection *projection) {
 
   49     if (projection == NULL) {
 
   50       string msg = 
"Unable to convert Lon/Lat polygon to X/Y. ";
 
   51       msg += 
"No projection has was supplied";
 
   52       throw IException(IException::Programmer, msg, _FILEINFO_);
 
   56     if(lonLatPolygon.isEmpty()) {
 
   57       return globalFactory->createMultiPolygon();
 
   60       vector<geos::geom::Geometry *> *xyPolys = 
new vector<geos::geom::Geometry *>;
 
   62       for(
unsigned int g = 0; g < lonLatPolygon.getNumGeometries(); ++g) {
 
   63         const geos::geom::Polygon *poly =
 
   64             dynamic_cast<const geos::geom::Polygon *
>(
 
   65               lonLatPolygon.getGeometryN(g));
 
   68         vector<geos::geom::Geometry *> *holes = 
new vector<geos::geom::Geometry *>;
 
   69         for(
unsigned int h = 0; h < poly->getNumInteriorRing(); ++h) {
 
   70           geos::geom::CoordinateSequence *xycoords = 
new geos::geom::CoordinateArraySequence();
 
   71           geos::geom::CoordinateSequence *llcoords =
 
   72             poly->getInteriorRingN(h)->getCoordinates();
 
   75           for(
unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
 
   76             projection->
SetGround(llcoords->getAt(cord).y,
 
   77                                   llcoords->getAt(cord).x);
 
   78             xycoords->add(geos::geom::Coordinate(projection->
XCoord(),
 
   82           geos::geom::LinearRing *hole = globalFactory->createLinearRing(xycoords);
 
   84           if(hole->isValid() && !hole->isEmpty()) {
 
   85             holes->push_back(hole);
 
   93         geos::geom::CoordinateSequence *xycoords = 
new geos::geom::CoordinateArraySequence();
 
   94         geos::geom::CoordinateSequence *llcoords =
 
   95           poly->getExteriorRing()->getCoordinates();
 
   98         for (
unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
 
   99           projection->
SetGround(llcoords->getAt(cord).y,
 
  100                                 llcoords->getAt(cord).x);
 
  101           xycoords->add(geos::geom::Coordinate(projection->
XCoord(),
 
  105         geos::geom::Polygon *newPoly = globalFactory->createPolygon(
 
  106                                          globalFactory->createLinearRing(xycoords), holes);
 
  108         if(newPoly->isValid() && !newPoly->isEmpty() && newPoly->getArea() > 1.0e-14) {
 
  109           xyPolys->push_back(newPoly);
 
  117       geos::geom::MultiPolygon *spikedPoly = globalFactory->createMultiPolygon(xyPolys);
 
  119       if(spikedPoly->isValid() && !spikedPoly->isEmpty()) {
 
  124           geos::geom::MultiPolygon *despikedPoly = Despike(spikedPoly);
 
  132           IString msg = 
"Unable to convert polygon from Lat/Lon to X/Y";
 
  133           throw IException(IException::Programmer, msg, _FILEINFO_);
 
  153   geos::geom::MultiPolygon *PolygonTools::XYToLatLon(
 
  154     const geos::geom::MultiPolygon &xYPolygon, 
TProjection *projection) {
 
  156     if(projection == NULL) {
 
  157       string msg = 
"Unable to convert X/Y polygon to Lon/Lat. ";
 
  158       msg += 
"No projection was supplied";
 
  159       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  163     if(xYPolygon.isEmpty()) {
 
  164       return globalFactory->createMultiPolygon();
 
  167       vector<geos::geom::Geometry *> *llPolys = 
new vector<geos::geom::Geometry *>;
 
  169       for(
unsigned int g = 0; g < xYPolygon.getNumGeometries(); ++g) {
 
  170         const geos::geom::Polygon *poly =
 
  171             dynamic_cast<const geos::geom::Polygon *
>(
 
  172               xYPolygon.getGeometryN(g));
 
  175         vector<geos::geom::Geometry *> *holes = 
new vector<geos::geom::Geometry *>;
 
  176         for(
unsigned int h = 0; h < poly->getNumInteriorRing(); ++h) {
 
  177           geos::geom::CoordinateSequence *llcoords = 
new geos::geom::CoordinateArraySequence();
 
  178           geos::geom::CoordinateSequence *xycoords =
 
  179             poly->getInteriorRingN(h)->getCoordinates();
 
  182           for(
unsigned int cord = 0; cord < xycoords->getSize(); ++cord) {
 
  183             projection->
SetWorld(xycoords->getAt(cord).x,
 
  184                                  xycoords->getAt(cord).y);
 
  185             llcoords->add(geos::geom::Coordinate(projection->
Longitude(),
 
  188           holes->push_back(globalFactory->createLinearRing(llcoords));
 
  192         geos::geom::CoordinateSequence *llcoords = 
new geos::geom::DefaultCoordinateSequence();
 
  193         geos::geom::CoordinateSequence *xycoords =
 
  194           poly->getExteriorRing()->getCoordinates();
 
  197         for(
unsigned int cord = 0; cord < xycoords->getSize(); ++cord) {
 
  198           projection->
SetWorld(xycoords->getAt(cord).x,
 
  199                                xycoords->getAt(cord).y);
 
  200           llcoords->add(geos::geom::Coordinate(projection->
Longitude(),
 
  204         llPolys->push_back(globalFactory->createPolygon(
 
  205                              globalFactory->createLinearRing(llcoords), holes));
 
  210       geos::geom::MultiPolygon *spikedPoly = globalFactory->createMultiPolygon(llPolys);
 
  212       if(spikedPoly->isValid() && !spikedPoly->isEmpty()) {
 
  217           geos::geom::MultiPolygon *despikedPoly = Despike(spikedPoly);
 
  225           IString msg = 
"Unable to convert polygon from X/Y to Lat/Lon";
 
  226           throw IException(IException::Programmer, msg, _FILEINFO_);
 
  245   geos::geom::MultiPolygon *PolygonTools::LatLonToSampleLine(
 
  249       string msg = 
"Unable to convert Lon/Lat polygon to Sample/Line. ";
 
  250       msg += 
"No UniversalGroundMap was supplied";
 
  251       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  255     if(lonLatPolygon.isEmpty()) {
 
  256       return globalFactory->createMultiPolygon();
 
  259       vector<geos::geom::Geometry *> *slPolys = 
new vector<geos::geom::Geometry *>;
 
  261       for(
unsigned int g = 0; g < lonLatPolygon.getNumGeometries(); g++) {
 
  262         const geos::geom::Polygon *poly =
 
  263             dynamic_cast<const geos::geom::Polygon *
>(
 
  264               lonLatPolygon.getGeometryN(g));
 
  267         vector<geos::geom::Geometry *> *holes = 
new vector<geos::geom::Geometry *>;
 
  268         for(
unsigned int h = 0; h < poly->getNumInteriorRing(); ++h) {
 
  269           geos::geom::CoordinateSequence *slcoords = 
new geos::geom::DefaultCoordinateSequence();
 
  270           geos::geom::CoordinateSequence *llcoords =
 
  271             poly->getInteriorRingN(h)->getCoordinates();
 
  274           for(
unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
 
  276                                     llcoords->getAt(cord).x);
 
  277             slcoords->add(geos::geom::Coordinate(ugm->
Sample(),
 
  280           holes->push_back(globalFactory->createLinearRing(slcoords));
 
  286         geos::geom::CoordinateSequence *slcoords = 
new geos::geom::CoordinateArraySequence();
 
  287         geos::geom::CoordinateSequence *llcoords =
 
  288           poly->getExteriorRing()->getCoordinates();
 
  291         for(
unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
 
  293                                       llcoords->getAt(cord).x)) {
 
  294             slcoords->add(geos::geom::Coordinate(ugm->
Sample(),
 
  300         if (slcoords->getSize() > 0 && !slcoords->front().equals(slcoords->back())) {
 
  301           slcoords->add(slcoords->front());
 
  305           slPolys->push_back(globalFactory->createPolygon(
 
  306                               globalFactory->createLinearRing(slcoords), holes));
 
  308         catch (std::exception &e) {
 
  310                            QObject::tr(
"Unable to convert polygon from Lat/Lon to Sample/Line. The " 
  311                                        "error given was [%1].").arg(e.what()),
 
  319       geos::geom::MultiPolygon *spikedPoly = globalFactory->createMultiPolygon(slPolys);
 
  321       if(spikedPoly->isValid() && !spikedPoly->isEmpty()) {
 
  326           geos::geom::MultiPolygon *despikedPoly = Despike(spikedPoly);
 
  334           IString msg = 
"Unable to convert polygon from Lat/Lon to Sample/Line";
 
  335           throw IException(IException::Programmer, msg, _FILEINFO_);
 
  354   geos::geom::MultiPolygon *PolygonTools::CopyMultiPolygon(
const geos::geom::MultiPolygon *mpolygon) {
 
  356     vector<geos::geom::Geometry *> *polys = 
new vector<geos::geom::Geometry *>;
 
  357     for(
unsigned int i = 0; i < mpolygon->getNumGeometries(); ++i) {
 
  358       polys->push_back((mpolygon->getGeometryN(i))->clone());
 
  360     return globalFactory->createMultiPolygon(polys);
 
  376   geos::geom::MultiPolygon *PolygonTools::CopyMultiPolygon(
const geos::geom::MultiPolygon &mpolygon) {
 
  378     vector<geos::geom::Geometry *> *polys = 
new vector<geos::geom::Geometry *>;
 
  379     for(
unsigned int i = 0; i < mpolygon.getNumGeometries(); ++i) {
 
  380       polys->push_back((mpolygon.getGeometryN(i))->clone());
 
  382     return globalFactory->createMultiPolygon(polys);
 
  394   QString PolygonTools::ToGML(
const geos::geom::MultiPolygon *mpolygon, QString idString,
 
  400     os << 
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" << endl;
 
  401     os << 
"<ogr:FeatureCollection" << endl;
 
  402     os << 
"    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" << endl;
 
  403     os << 
"    xsi:schemaLocation=\"http://ogr.maptools.org/ " << schema << 
"\"" << endl;
 
  404     os << 
"    xmlns:ogr=\"http://ogr.maptools.org/\"" << endl;
 
  405     os << 
"    xmlns:gml=\"http://www.opengis.net/gml\">" << endl;
 
  406     os << 
"  <gml:boundedBy>" << endl;
 
  407     os << 
"    <gml:Box>" << endl;
 
  408     os << 
"      <gml:coord>";
 
  410                        setprecision(15) << mpolygon->getEnvelopeInternal()->getMinX() << 
 
  413                        setprecision(15) << mpolygon->getEnvelopeInternal()->getMinY() << 
 
  415     os <<      
"</gml:coord>" << endl;
 
  416     os << 
"      <gml:coord>";
 
  418                        setprecision(15) << mpolygon->getEnvelopeInternal()->getMaxX() <<
 
  421                        setprecision(15) << mpolygon->getEnvelopeInternal()->getMaxY() << 
 
  423     os <<        
"</gml:coord>" << endl;
 
  424     os << 
"    </gml:Box>" << endl;
 
  425     os << 
"  </gml:boundedBy>" << endl << endl;
 
  426     os << 
"  <gml:featureMember>" << endl;
 
  427     os << 
"   <ogr:multi_polygon fid=\"0\">" << endl;
 
  428     os << 
"      <ogr:ID>" << idString << 
"</ogr:ID>" << endl;
 
  429     os << 
"      <ogr:geometryProperty><gml:MultiPolygon><gml:polygonMember>" <<
 
  430                      "<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>";
 
  433     for(
unsigned int polyN = 0; polyN < mpolygon->getNumGeometries(); polyN++) {
 
  434       geos::geom::CoordinateSequence *pts = mpolygon->getGeometryN(polyN)->getCoordinates();
 
  436       for(
unsigned int i = 0; i < pts->getSize(); i++) {
 
  437         double lon = pts->getAt(i).x;
 
  438         double lat = pts->getAt(i).y;
 
  440         os << setprecision(15) << lon << 
"," << setprecision(15) << lat << 
" ";
 
  444     os << 
"</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs>" <<
 
  445               "</gml:Polygon></gml:polygonMember></gml:MultiPolygon>" <<
 
  446               "</ogr:geometryProperty>" << endl;
 
  447     os << 
"    </ogr:multi_polygon>" << endl;
 
  448     os << 
"  </gml:featureMember>" << endl;
 
  449     os << 
"</ogr:FeatureCollection>";
 
  451     return os.str().c_str();
 
  461   QString PolygonTools::GMLSchema() {
 
  465     os << 
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
 
  466     os << 
"<xs:schema targetNamespace=\"http://ogr.maptools.org/\" " 
  467               "xmlns:ogr=\"http://ogr.maptools.org/\" " 
  468               "xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" " 
  469               "xmlns:gml=\"http://www.opengis.net/gml\" " 
  470               "elementFormDefault=\"qualified\" " 
  471               "version=\"1.0\">" << endl;
 
  472     os << 
"  <xs:import namespace=\"http://www.opengis.net/gml\" " 
  473               "schemaLocation=\"http://schemas.opengis.net/gml/2.1.2/feature.xsd\"/>" << endl;
 
  474     os << 
"  <xs:element name=\"FeatureCollection\" " 
  475                  "type=\"ogr:FeatureCollectionType\" " 
  476                  "substitutionGroup=\"gml:_FeatureCollection\"/>" << endl;
 
  477     os << 
"  <xs:complexType name=\"FeatureCollectionType\">" << endl;
 
  478     os << 
"    <xs:complexContent>" << endl;
 
  479     os << 
"      <xs:extension base=\"gml:AbstractFeatureCollectionType\">" << endl;
 
  480     os << 
"        <xs:attribute name=\"lockId\" type=\"xs:string\" use=\"optional\"/>" << endl;
 
  481     os << 
"        <xs:attribute name=\"scope\" type=\"xs:string\" use=\"optional\"/>" << endl;
 
  482     os << 
"      </xs:extension>" << endl;
 
  483     os << 
"    </xs:complexContent>" << endl;
 
  484     os << 
"  </xs:complexType>" << endl;
 
  485     os << 
"  <xs:element name=\"multi_polygon\" " 
  486                  "type=\"ogr:multi_polygon_Type\" " 
  487                  "substitutionGroup=\"gml:_Feature\"/>" << endl;
 
  488     os << 
"  <xs:complexType name=\"multi_polygon_Type\">" << endl;
 
  489     os << 
"    <xs:complexContent>" << endl;
 
  490     os << 
"      <xs:extension base=\"gml:AbstractFeatureType\">" << endl;
 
  491     os << 
"        <xs:sequence>" << endl;
 
  492     os << 
"          <xs:element name=\"geometryProperty\" " 
  493                          "type=\"gml:MultiPolygonPropertyType\" " 
  494                          "nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\"/>" << endl;
 
  495     os << 
"          <xs:element name=\"fid\" nillable=\"true\" minOccurs=\"0\" " 
  496                          "maxOccurs=\"1\">" << endl;
 
  497     os << 
"            <xs:simpleType>" << endl;
 
  498     os << 
"              <xs:restriction base=\"xs:string\">" << endl;
 
  499     os << 
"              </xs:restriction>" << endl;
 
  500     os << 
"            </xs:simpleType>" << endl;
 
  501     os << 
"          </xs:element>" << endl;
 
  502     os << 
"          <xs:element name=\"ID\" nillable=\"true\" minOccurs=\"0\" " 
  503                          "maxOccurs=\"1\">" << endl;
 
  504     os << 
"            <xs:simpleType>" << endl;
 
  505     os << 
"              <xs:restriction base=\"xs:integer\">" << endl;
 
  506     os << 
"                <xs:totalDigits value=\"16\"/>" << endl;
 
  507     os << 
"              </xs:restriction>" << endl;
 
  508     os << 
"            </xs:simpleType>" << endl;
 
  509     os << 
"          </xs:element>" << endl;
 
  510     os << 
"        </xs:sequence>" << endl;
 
  511     os << 
"      </xs:extension>" << endl;
 
  512     os << 
"    </xs:complexContent>" << endl;
 
  513     os << 
"  </xs:complexType>" << endl;
 
  514     os << 
"</xs:schema>";
 
  516     return os.str().c_str();
 
  527   geos::geom::MultiPolygon *PolygonTools::To180(geos::geom::MultiPolygon *poly360) {
 
  535       geos::geom::CoordinateArraySequence *leftOf180Pts =
 
  536           new geos::geom::CoordinateArraySequence();
 
  537       leftOf180Pts->add(geos::geom::Coordinate(0, -90));
 
  538       leftOf180Pts->add(geos::geom::Coordinate(0, 90));
 
  539       leftOf180Pts->add(geos::geom::Coordinate(180, 90));
 
  540       leftOf180Pts->add(geos::geom::Coordinate(180, -90));
 
  541       leftOf180Pts->add(geos::geom::Coordinate(0, -90));
 
  543       geos::geom::LinearRing *leftOf180Geom =
 
  544           globalFactory->createLinearRing(leftOf180Pts);
 
  546       geos::geom::Polygon *leftOf180Poly =
 
  547           globalFactory->createPolygon(leftOf180Geom, NULL);
 
  549       geos::geom::CoordinateArraySequence *rightOf180Pts =
 
  550           new geos::geom::CoordinateArraySequence();
 
  551       rightOf180Pts->add(geos::geom::Coordinate(180, -90));
 
  552       rightOf180Pts->add(geos::geom::Coordinate(180, 90));
 
  553       rightOf180Pts->add(geos::geom::Coordinate(360, 90));
 
  554       rightOf180Pts->add(geos::geom::Coordinate(360, -90));
 
  555       rightOf180Pts->add(geos::geom::Coordinate(180, -90));
 
  557       geos::geom::LinearRing *rightOf180Geom =
 
  558           globalFactory->createLinearRing(rightOf180Pts);
 
  560       geos::geom::Polygon *rightOf180Poly =
 
  561           globalFactory->createPolygon(rightOf180Geom, NULL);
 
  563       geos::geom::Geometry *preserved = Intersect(leftOf180Poly, poly360);
 
  564       geos::geom::Geometry *moving = Intersect(rightOf180Poly, poly360);
 
  566       geos::geom::CoordinateSequence *movingPts = moving->getCoordinates();
 
  567       geos::geom::CoordinateSequence *movedPts =
 
  568           new geos::geom::CoordinateArraySequence();
 
  570       for(
unsigned int i = 0; i < movingPts->getSize(); i ++) {
 
  571         movedPts->add(geos::geom::Coordinate(movingPts->getAt(i).x - 360.0,
 
  572                                              movingPts->getAt(i).y));
 
  575       if(movedPts->getSize()) {
 
  576         movedPts->add(geos::geom::Coordinate(movedPts->getAt(0).x,
 
  577                                             movedPts->getAt(0).y));
 
  580       geos::geom::Geometry *moved = globalFactory->createPolygon(
 
  581           globalFactory->createLinearRing(movedPts), NULL);
 
  583       std::vector<geos::geom::Geometry *> *geomsForCollection = 
new 
  584           std::vector<geos::geom::Geometry *>;
 
  585       geomsForCollection->push_back(preserved);
 
  586       geomsForCollection->push_back(moved);
 
  588       geos::geom::GeometryCollection *the180Polys =
 
  589           Isis::globalFactory->createGeometryCollection(geomsForCollection);
 
  591       geos::geom::MultiPolygon *result = MakeMultiPolygon(the180Polys);
 
  595       geos::geom::MultiPolygon *fixedResult = FixSeam(result);
 
  601     catch(geos::util::GEOSException *exc) {
 
  602       IString msg = 
"Conversion to 180 failed. The reason given was [" +
 
  605       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  608       IString msg = 
"Conversion to 180 failed. Could not determine the reason";
 
  609       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  624   double PolygonTools::Thickness(
const geos::geom::MultiPolygon *mpolygon) {
 
  625     const geos::geom::Envelope *envelope = mpolygon->getEnvelopeInternal();
 
  627     double x = fabs(envelope->getMaxX() - envelope->getMinX());
 
  628     double y = fabs(envelope->getMaxY() - envelope->getMinY());
 
  629     double extent = max(x, y);
 
  631     return mpolygon->getArea() / (extent * extent);
 
  646   geos::geom::MultiPolygon *PolygonTools::Despike(
const geos::geom::Geometry *geom) {
 
  647     geos::geom::MultiPolygon *spiked = MakeMultiPolygon(geom);
 
  648     geos::geom::MultiPolygon *despiked = Despike(spiked);
 
  667   geos::geom::MultiPolygon *PolygonTools::Despike(
const geos::geom::MultiPolygon *multiPoly) {
 
  669     vector<geos::geom::Geometry *> *newPolys = 
new vector<geos::geom::Geometry *>;
 
  670     for(
unsigned int g = 0; g < multiPoly->getNumGeometries(); ++g) {
 
  671       const geos::geom::Polygon *poly =
 
  672           dynamic_cast<const geos::geom::Polygon *
>(multiPoly->getGeometryN(g));
 
  675       vector<geos::geom::Geometry *> *holes = 
new vector<geos::geom::Geometry *>;
 
  676       for(
unsigned int h = 0; h < poly->getNumInteriorRing(); ++h) {
 
  677         const geos::geom::LineString *ls = poly->getInteriorRingN(h);
 
  678         geos::geom::LinearRing *lr;
 
  685           geos::geom::LinearRing *fixed = FixGeometry(lr);
 
  692           holes->push_back(lr);
 
  700       const geos::geom::LineString *ls = poly->getExteriorRing();
 
  701       geos::geom::LinearRing *lr;
 
  707           geos::geom::LinearRing *fixed = FixGeometry(lr);
 
  715         if(ls->isValid() && ls->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
  716           lr = 
dynamic_cast<geos::geom::LinearRing *
>(ls->clone());
 
  725         geos::geom::Polygon *tp = Isis::globalFactory->createPolygon(lr, holes);
 
  727         if(tp->isEmpty() || !tp->isValid()) {
 
  729           newPolys->push_back(poly->clone());
 
  732           newPolys->push_back(tp);
 
  738     geos::geom::MultiPolygon *mp = Isis::globalFactory->createMultiPolygon(newPolys);
 
  740     if(!mp->isValid() || mp->isEmpty()) {
 
  743       IString msg = 
"Despike failed to correct the polygon";
 
  744       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  748     if(fabs((mp->getArea() / multiPoly->getArea()) - 1.0) > 0.50) {
 
  749       IString msg = 
"Despike failed to correct the polygon " + mp->toString();
 
  752       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  772   geos::geom::LinearRing *PolygonTools::Despike(
const geos::geom::LineString *lineString) {
 
  773     geos::geom::CoordinateSequence *vertices = lineString->getCoordinates();
 
  776     if(vertices->getSize() < 4) {
 
  778       return Isis::globalFactory->createLinearRing(geos::geom::CoordinateArraySequence());
 
  783     vertices->deleteAt(vertices->getSize() - 1);
 
  788     for(
long index = 0; index < (long)vertices->getSize(); index++) {
 
  790       if(vertices->getSize() < 3) {
 
  796       long testCoords[3] = {
 
  804       for(
int j = 0; j < 3; j++) {
 
  805         while(testCoords[j] < 0) {
 
  806           testCoords[j] += vertices->getSize();
 
  809         while(testCoords[j] >= (
long)vertices->getSize()) {
 
  810           testCoords[j] -= vertices->getSize();
 
  815       if(IsSpiked(vertices->getAt(testCoords[0]),
 
  816                   vertices->getAt(testCoords[1]),
 
  817                   vertices->getAt(testCoords[2]))) {
 
  819         vertices->deleteAt(testCoords[1]);
 
  826     if(vertices->getSize() < 3) {
 
  830       return Isis::globalFactory->createLinearRing(geos::geom::CoordinateArraySequence());
 
  834       vertices->add(vertices->getAt(0));
 
  835       return Isis::globalFactory->createLinearRing(vertices);
 
  848   bool PolygonTools::IsSpiked(geos::geom::Coordinate first, 
 
  849                               geos::geom::Coordinate middle, 
 
  850                               geos::geom::Coordinate last) {
 
  851     return TestSpiked(first, middle, last) || TestSpiked(last, middle, first);
 
  882   bool PolygonTools::TestSpiked(geos::geom::Coordinate first, geos::geom::Coordinate middle, 
 
  883                                 geos::geom::Coordinate last) {
 
  884     geos::geom::Point *firstPt = Isis::globalFactory->createPoint(first);
 
  885     geos::geom::Point *middlePt = Isis::globalFactory->createPoint(middle);
 
  886     geos::geom::Point *lastPt = Isis::globalFactory->createPoint(last);
 
  888     geos::geom::CoordinateSequence *coords = 
new geos::geom::CoordinateArraySequence();
 
  891     geos::geom::LineString *line = Isis::globalFactory->createLineString(coords); 
 
  897     double tolerance = line->getLength() / 100.0;
 
  901     double distanceLastMiddle = geos::operation::distance::DistanceOp::distance(lastPt, middlePt);
 
  902     double distanceLastLine = geos::operation::distance::DistanceOp::distance(lastPt, line);
 
  904     if(distanceLastMiddle == 0.0) 
return true; 
 
  908     if(distanceLastLine / distanceLastMiddle >= .05) {
 
  913     if(spiked && distanceLastLine > tolerance) {
 
  918       geos::geom::CoordinateSequence *coords = 
new geos::geom::CoordinateArraySequence();
 
  925       geos::geom::LinearRing *shell = Isis::globalFactory->createLinearRing(coords);
 
  926       std::vector<geos::geom::Geometry *> *empty = 
new std::vector<geos::geom::Geometry *>;
 
  929       geos::geom::Polygon *poly = Isis::globalFactory->createPolygon(shell, empty);
 
  934       if(poly->getArea() < 1.0e-10) {
 
  964   geos::geom::Geometry *PolygonTools::Intersect(
const geos::geom::Geometry *geom1, 
 
  965                                                 const geos::geom::Geometry *geom2) {
 
  967       return Operate(geom1, geom2, (
unsigned int)geos::operation::overlay::OverlayOp::opINTERSECTION);
 
  969     catch(geos::util::GEOSException *exc) {
 
  970       IString msg = 
"Intersect operation failed. The reason given was [" + 
IString(exc->what()) + 
"]";
 
  972       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  975       IString msg = 
"Intersect operation failed";
 
  976       throw IException(e, IException::Programmer, msg, _FILEINFO_);
 
  979       IString msg = 
"Intersect operation failed for an unknown reason";
 
  980       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  985   geos::geom::Geometry *PolygonTools::Operate(
const geos::geom::Geometry *geom1,
 
  986                                               const geos::geom::Geometry *geom2, 
 
  987                                               unsigned int opcode) {
 
  989     geos::operation::overlay::OverlayOp::OpCode code =
 
  990       (geos::operation::overlay::OverlayOp::OpCode)opcode;
 
  992     geos::geom::Geometry *result = NULL;
 
  994     geos::geom::Geometry *geomFirst  = MakeMultiPolygon(geom1);
 
  995     geos::geom::Geometry *geomSecond = MakeMultiPolygon(geom2);
 
  997     geos::operation::overlay::snap::GeometrySnapper snap(*geomFirst);
 
  998     geos::geom::Geometry *geomSnapped = snap.snapTo(*geomSecond, 1.0e-10)->clone();
 
  999     if(!geomSnapped->isValid()) {
 
 1004       geomFirst = geomSnapped;
 
 1007     unsigned int precision = 15;
 
 1008     unsigned int minPrecision = 13;
 
 1012         std::unique_ptr< geos::geom::Geometry > resultAuto(
 
 1013           BinaryOp(geomFirst, geomSecond, geos::operation::overlay::overlayOp(code)).release());
 
 1015         result = resultAuto->clone();
 
 1017       catch(geos::util::GEOSException *exc) {
 
 1019         if(!failed || precision == minPrecision) 
throw;
 
 1024         if(precision == minPrecision) {
 
 1025           IString msg = 
"An unknown geos error occurred";
 
 1026           throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1030           IString msg = 
"An unknown geos error occurred when attempting to clone a geometry";
 
 1031           throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1038         geos::geom::Geometry *tmp = ReducePrecision(geomFirst, precision);
 
 1042         tmp = ReducePrecision(geomSecond, precision);
 
 1054     if(result && !result->isValid()) {
 
 1056         geos::geom::Geometry *newResult = FixGeometry(result);
 
 1058         if(fabs(newResult->getArea() / result->getArea() - 1.0) > 0.50) {
 
 1062           IString msg = 
"Operation [" + IString((
int)opcode) + 
"] failed";
 
 1063           throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1069       catch(IException &e) {
 
 1070         IString msg = 
"Operation [" + IString((
int)opcode) + 
"] failed";
 
 1071         throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1075     if(result == NULL) {
 
 1076       IString msg = 
"Operation [" + IString((
int)opcode) + 
" failed";
 
 1077       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1093   geos::geom::Geometry *PolygonTools::FixGeometry(
const geos::geom::Geometry *geom) {
 
 1094     if(geom->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
 
 1095       return FixGeometry(
dynamic_cast<const geos::geom::MultiPolygon *
>(geom));
 
 1097     if(geom->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
 1098       return FixGeometry(
dynamic_cast<const geos::geom::LinearRing *
>(geom));
 
 1100     if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
 
 1101       return FixGeometry(
dynamic_cast<const geos::geom::Polygon *
>(geom));
 
 1103     if(geom->getGeometryTypeId() == geos::geom::GEOS_GEOMETRYCOLLECTION) {
 
 1104       return FixGeometry(MakeMultiPolygon(geom));
 
 1107       IString msg = 
"PolygonTools::FixGeometry does not support [" + GetGeometryName(geom) + 
"]";
 
 1108       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1121   geos::geom::MultiPolygon *PolygonTools::FixGeometry(
const geos::geom::MultiPolygon *poly) {
 
 1123     vector<geos::geom::Geometry *> *newPolys = 
new vector<geos::geom::Geometry *>;
 
 1126     for(
unsigned int geomIndex = 0; geomIndex < poly->getNumGeometries(); geomIndex ++) {
 
 1127       geos::geom::Polygon *fixedpoly = FixGeometry(
 
 1128           dynamic_cast<const geos::geom::Polygon *
>(
 
 1129             poly->getGeometryN(geomIndex)));
 
 1130       if(fixedpoly->isValid()) {
 
 1131         newPolys->push_back(fixedpoly);
 
 1139     geos::geom::MultiPolygon *mp = Isis::globalFactory->createMultiPolygon(newPolys);
 
 1144   geos::geom::Polygon *PolygonTools::FixGeometry(
const geos::geom::Polygon *poly) {
 
 1147     vector<geos::geom::Geometry *> *holes = 
new vector<geos::geom::Geometry *>;
 
 1148     for(
unsigned int holeIndex = 0; holeIndex < poly->getNumInteriorRing(); holeIndex ++) {
 
 1149       const geos::geom::LineString *thisHole = poly->getInteriorRingN(holeIndex);
 
 1152       if(thisHole->getGeometryTypeId() != geos::geom::GEOS_LINEARRING) {
 
 1153         holes->push_back(thisHole->clone());
 
 1158         geos::geom::LinearRing *newHole = FixGeometry((geos::geom::LinearRing *)thisHole);
 
 1159         holes->push_back(newHole);
 
 1161       catch (IException &e) {
 
 1162         IString msg = 
"Failed when attempting to fix interior ring of multipolygon";
 
 1163         throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1168     const geos::geom::LineString *exterior = poly->getExteriorRing();
 
 1171       geos::geom::LinearRing *newExterior = NULL;
 
 1173       if(exterior->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
 1174         newExterior = FixGeometry((geos::geom::LinearRing *)exterior);
 
 1177         IString msg = 
"Failed when attempting to fix exterior ring of polygon. The exterior " 
 1178                       "ring is not simple and closed";
 
 1179         throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1182       return globalFactory->createPolygon(newExterior, holes);
 
 1184     catch (IException &e) {
 
 1185       IString msg = 
"Failed when attempting to fix exterior ring of polygon";
 
 1186       throw IException(e, IException::Programmer, msg, _FILEINFO_);
 
 1206   geos::geom::LinearRing *PolygonTools::FixGeometry(
const geos::geom::LinearRing *ring) {
 
 1208     geos::geom::CoordinateSequence *coords = ring->getCoordinates();
 
 1211     if(coords->getSize() < 4) {
 
 1212       return globalFactory->createLinearRing(
new geos::geom::DefaultCoordinateSequence());
 
 1215     geos::geom::CoordinateSequence *newCoords = 
new geos::geom::DefaultCoordinateSequence();
 
 1216     const geos::geom::Coordinate *lastCoordinate = &coords->getAt(0);
 
 1217     newCoords->add(*lastCoordinate);
 
 1220     for(
unsigned int coordIndex = 1; coordIndex < coords->getSize() - 1; coordIndex ++) {
 
 1221       const geos::geom::Coordinate *thisCoordinate = &coords->getAt(coordIndex);
 
 1226       double difference[2] = {
 
 1227         lastCoordinate->x - thisCoordinate->x,
 
 1228         lastCoordinate->y - thisCoordinate->y,
 
 1232       double minDiff = fabs(DecimalPlace(thisCoordinate->x) - DecimalPlace(difference[0]));
 
 1234       minDiff = min(minDiff, fabs(DecimalPlace(thisCoordinate->y) - DecimalPlace(difference[1])));
 
 1238       if(difference[0] == 0.0 && difference[1] != 0.0) {
 
 1240         minDiff = fabs(DecimalPlace(thisCoordinate->y) - DecimalPlace(difference[1]));
 
 1242       else if(difference[1] == 0.0 && difference[0] != 0.0) {
 
 1244         minDiff = fabs(DecimalPlace(thisCoordinate->x) - DecimalPlace(difference[0]));
 
 1246       else if(difference[0] == 0.0 && difference[1] == 0.0) {
 
 1253         newCoords->add(*thisCoordinate);
 
 1254         lastCoordinate = thisCoordinate;
 
 1258     newCoords->add(geos::geom::Coordinate(newCoords->getAt(0).x, newCoords->getAt(0).y));
 
 1259     geos::geom::LinearRing *newRing = NULL;
 
 1263       if(newCoords->getSize() > 3) {
 
 1264         newRing = globalFactory->createLinearRing(newCoords);
 
 1271     catch(geos::util::GEOSException *exc) {
 
 1275       IString msg = 
"Error when attempting to fix linear ring";
 
 1276       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1279     if(newRing && !newRing->isValid() && ring->isValid()) {
 
 1280       IString msg = 
"Failed when attempting to fix linear ring";
 
 1281       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1283     else if(!newRing || !newRing->isValid()) {
 
 1288       newRing = 
dynamic_cast<geos::geom::LinearRing *
>(ring->clone());
 
 1306   int PolygonTools::DecimalPlace(
double num) {
 
 1308     if(num == 0.0) 
return 0;
 
 1312     int decimalPlace = 1;
 
 1323     return decimalPlace;
 
 1334   geos::geom::Geometry *PolygonTools::Difference(
const geos::geom::Geometry *geom1, 
 
 1335                                                  const geos::geom::Geometry *geom2) {
 
 1337       return Operate(geom1, geom2, (
unsigned int)geos::operation::overlay::OverlayOp::opDIFFERENCE);
 
 1339     catch(geos::util::GEOSException *exc) {
 
 1340       IString msg = 
"Difference operation failed. The reason given was [" + 
 
 1343       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1346       IString msg = 
"Difference operation failed";
 
 1347       throw IException(e, IException::Programmer, msg, _FILEINFO_);
 
 1350       IString msg = 
"Difference operation failed for an unknown reason";
 
 1351       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1369   geos::geom::MultiPolygon *PolygonTools::MakeMultiPolygon(
const geos::geom::Geometry *geom) {
 
 1371     if(geom->isEmpty()) {
 
 1372       return Isis::globalFactory->createMultiPolygon();
 
 1375     else if(geom->getArea() - DBL_EPSILON <= DBL_EPSILON) {
 
 1376       return Isis::globalFactory->createMultiPolygon();
 
 1379     else if(geom->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
 
 1380       return dynamic_cast<geos::geom::MultiPolygon *
>(geom->clone());
 
 1383     else if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
 
 1384       vector<geos::geom::Geometry *> *polys = 
new vector<geos::geom::Geometry *>;
 
 1385       polys->push_back(geom->clone());
 
 1386       geos::geom::MultiPolygon *mp = Isis::globalFactory->createMultiPolygon(polys);
 
 1390     else if(geom->getGeometryTypeId() == geos::geom::GEOS_GEOMETRYCOLLECTION) {
 
 1391       vector<geos::geom::Geometry *> * polys =
 
 1392           new vector<geos::geom::Geometry *>;
 
 1393       const geos::geom::GeometryCollection *gc =
 
 1394           dynamic_cast<const geos::geom::GeometryCollection *
>(geom);
 
 1395       for(
unsigned int i = 0; i < gc->getNumGeometries(); ++i) {
 
 1396         geos::geom::MultiPolygon *subMultiPoly =
 
 1397             MakeMultiPolygon(gc->getGeometryN(i));
 
 1399         for(
unsigned int subPoly = 0;
 
 1400             subPoly < subMultiPoly->getNumGeometries();
 
 1402           const geos::geom::Polygon *poly =
 
 1403               dynamic_cast<const geos::geom::Polygon *
>(
 
 1404                 subMultiPoly->getGeometryN(subPoly));
 
 1405           polys->push_back(
dynamic_cast<geos::geom::Polygon *
>(poly->clone()));
 
 1409       geos::geom::MultiPolygon *mp =
 
 1410           Isis::globalFactory->createMultiPolygon(polys);
 
 1411       if(mp->getArea() - DBL_EPSILON <= DBL_EPSILON) {
 
 1413         mp = Isis::globalFactory->createMultiPolygon();
 
 1421       return Isis::globalFactory->createMultiPolygon();
 
 1426   geos::geom::MultiPolygon *PolygonTools::FixSeam(
 
 1427       const geos::geom::Polygon *polyA, 
const geos::geom::Polygon *polyB) {
 
 1428     geos::geom::CoordinateSequence *polyAPoints = polyA->getCoordinates();
 
 1429     geos::geom::CoordinateSequence *polyBPoints = polyB->getCoordinates();
 
 1431     unsigned int aIntersectionBegin = 0;
 
 1432     unsigned int aIntersectionEnd = 0;
 
 1433     unsigned int bIntersectionBegin = 0;
 
 1434     unsigned int bIntersectionEnd = 0;
 
 1436     bool intersectionStarted = 
false;
 
 1437     bool intersectionEnded = 
false;
 
 1439     unsigned int lastBMatch  = 0;
 
 1440     for (
unsigned int i = 0;
 
 1441         !intersectionEnded && i < polyAPoints->getSize();
 
 1444       bool foundEquivalent = 
false;
 
 1446       geos::geom::Coordinate coordA = polyAPoints->getAt(i);
 
 1447       coordA = *ReducePrecision(&coordA, 13);
 
 1449       for (
unsigned int j = 0;
 
 1450            !foundEquivalent && j < polyBPoints->getSize();
 
 1452         geos::geom::Coordinate coordB = polyBPoints->getAt(j);
 
 1453         coordB = *ReducePrecision(&coordB, 13);
 
 1455         foundEquivalent = coordA.equals(coordB);
 
 1457         if (foundEquivalent) lastBMatch = j;
 
 1459         if (foundEquivalent && !intersectionStarted) {
 
 1460           intersectionStarted = 
true;
 
 1461           aIntersectionBegin = i;
 
 1462           bIntersectionBegin = j;
 
 1466       if (!foundEquivalent && intersectionStarted && !intersectionEnded) {
 
 1467         intersectionEnded = 
true;
 
 1468         aIntersectionEnd = i;
 
 1469         bIntersectionEnd = lastBMatch;
 
 1473     geos::geom::MultiPolygon * result = NULL;
 
 1474     if (intersectionStarted && intersectionEnded) {
 
 1475       geos::geom::CoordinateSequence *merged =
 
 1476           new geos::geom::CoordinateArraySequence;
 
 1479       for (i = 0; i < aIntersectionBegin; i ++) {
 
 1480         merged->add(polyAPoints->getAt(i));
 
 1483       i = bIntersectionBegin;
 
 1484       while (i != bIntersectionEnd) {
 
 1485         merged->add(polyBPoints->getAt(i));
 
 1487         if (i >= polyBPoints->getSize()) i = 0;
 
 1490       for (i = aIntersectionEnd; i < polyAPoints->getSize() - 1; i++) {
 
 1491         merged->add(polyAPoints->getAt(i));
 
 1494       merged->add(merged->getAt(0));
 
 1495       result = MakeMultiPolygon(globalFactory->createPolygon(
 
 1496           globalFactory->createLinearRing(merged), NULL));
 
 1503   geos::geom::MultiPolygon *PolygonTools::FixSeam(
 
 1504       const geos::geom::MultiPolygon *poly) {
 
 1506     std::vector<geos::geom::Geometry *> *polys =
 
 1507         new std::vector<geos::geom::Geometry *>;
 
 1510     for(
unsigned int copyIndex = 0;
 
 1511         copyIndex < poly->getNumGeometries();
 
 1513       polys->push_back(poly->getGeometryN(copyIndex)->clone());
 
 1516     unsigned int outerPolyIndex = 0;
 
 1518     while(outerPolyIndex + 1 < polys->size()) {
 
 1519       unsigned int innerPolyIndex = outerPolyIndex + 1;
 
 1521       while(innerPolyIndex < polys->size()) {
 
 1522         geos::geom::MultiPolygon *fixedPair = FixSeam(
 
 1523             dynamic_cast<geos::geom::Polygon *
>(polys->at(outerPolyIndex)),
 
 1524             dynamic_cast<geos::geom::Polygon *
>(polys->at(innerPolyIndex)));
 
 1526         if(fixedPair != NULL) {
 
 1527           geos::geom::Geometry *oldInnerPoly = polys->at(innerPolyIndex);
 
 1528           geos::geom::Geometry *oldOuterPoly = polys->at(outerPolyIndex);
 
 1530           polys->erase(polys->begin() + innerPolyIndex);
 
 1531           (*polys)[outerPolyIndex] = fixedPair->getGeometryN(0)->clone();
 
 1532           innerPolyIndex = outerPolyIndex + 1;
 
 1537           delete oldInnerPoly;
 
 1538           oldInnerPoly = NULL;
 
 1540           delete oldOuterPoly;
 
 1541           oldOuterPoly = NULL;
 
 1551     return globalFactory->createMultiPolygon(polys);
 
 1564   geos::geom::Geometry *PolygonTools::ReducePrecision(
const geos::geom::Geometry *geom, 
 
 1565                                                       unsigned int precision) {
 
 1566     if(geom->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
 
 1567       return ReducePrecision(
 
 1568           dynamic_cast<const geos::geom::MultiPolygon *
>(geom), precision);
 
 1570     if(geom->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
 1571       return ReducePrecision(
 
 1572           dynamic_cast<const geos::geom::LinearRing *
>(geom), precision);
 
 1574     if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
 
 1575       return ReducePrecision(
 
 1576           dynamic_cast<const geos::geom::Polygon *
>(geom), precision);
 
 1578     if(geom->getGeometryTypeId() == geos::geom::GEOS_GEOMETRYCOLLECTION) {
 
 1579       return ReducePrecision(MakeMultiPolygon(geom), precision);
 
 1582       IString msg = 
"PolygonTools::ReducePrecision does not support [" + 
 
 1583                     GetGeometryName(geom) + 
"]";
 
 1584       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1598   geos::geom::MultiPolygon *PolygonTools::ReducePrecision(
const geos::geom::MultiPolygon *poly, 
 
 1599                                                           unsigned int precision) {
 
 1601     vector<geos::geom::Geometry *> *newPolys = 
new vector<geos::geom::Geometry *>;
 
 1604     for(
unsigned int geomIndex = 0; geomIndex < poly->getNumGeometries(); geomIndex ++) {
 
 1605       geos::geom::Geometry *lowerPrecision = ReducePrecision(
 
 1606           dynamic_cast<const geos::geom::Polygon *
>(
 
 1607             poly->getGeometryN(geomIndex)),
 
 1610       if(!lowerPrecision->isEmpty()) {
 
 1611         newPolys->push_back(lowerPrecision);
 
 1614         delete lowerPrecision;
 
 1618     geos::geom::MultiPolygon *mp = Isis::globalFactory->createMultiPolygon(newPolys);
 
 1632   geos::geom::Polygon *PolygonTools::ReducePrecision(
const geos::geom::Polygon *poly, 
 
 1633                                                      unsigned int precision) {
 
 1635     vector<geos::geom::Geometry *> *holes = 
new vector<geos::geom::Geometry *>;
 
 1636     for(
unsigned int holeIndex = 0; holeIndex < poly->getNumInteriorRing(); holeIndex ++) {
 
 1637       const geos::geom::LineString *thisHole = poly->getInteriorRingN(holeIndex);
 
 1640       if(thisHole->getGeometryTypeId() != geos::geom::GEOS_LINEARRING) {
 
 1641         holes->push_back(thisHole->clone());
 
 1646         geos::geom::LinearRing *newHole = ReducePrecision((geos::geom::LinearRing *)thisHole, 
 
 1649         if(!newHole->isEmpty()) {
 
 1650           holes->push_back(newHole);
 
 1658         IString msg = 
"Failed when attempting to fix interior ring of multipolygon";
 
 1659         throw IException(e, IException::Programmer, msg, _FILEINFO_);
 
 1664     const geos::geom::LineString *exterior = poly->getExteriorRing();
 
 1667       geos::geom::LinearRing *newExterior = NULL;
 
 1669       if(exterior->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
 1670         newExterior = ReducePrecision((geos::geom::LinearRing *)exterior, precision);
 
 1673         IString msg = 
"Failed when attempting to fix exterior ring of polygon. The exterior " 
 1674                       "ring is not simple and closed";
 
 1675         throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1678       return globalFactory->createPolygon(newExterior, holes);
 
 1681       IString msg = 
"Failed when attempting to fix exterior ring of polygon";
 
 1682       throw IException(e, IException::Programmer, msg, _FILEINFO_);
 
 1696   geos::geom::LinearRing *PolygonTools::ReducePrecision(
const geos::geom::LinearRing *ring, 
 
 1697                                                         unsigned int precision) {
 
 1698     geos::geom::CoordinateSequence *coords = ring->getCoordinates();
 
 1701     if(coords->getSize() <= 0) 
return dynamic_cast<geos::geom::LinearRing *
>(
 
 1704     geos::geom::CoordinateSequence *newCoords = 
new geos::geom::DefaultCoordinateSequence();
 
 1705     geos::geom::Coordinate *coord = ReducePrecision(&coords->getAt(0), precision);
 
 1706     newCoords->add(*coord);
 
 1711     for(
unsigned int coordIndex = 1; coordIndex < coords->getSize() - 1; coordIndex ++) {
 
 1712       const geos::geom::Coordinate *thisCoordinate = &coords->getAt(coordIndex);
 
 1713       coord = ReducePrecision(thisCoordinate, precision);
 
 1714       newCoords->add(*coord);
 
 1719     newCoords->add(geos::geom::Coordinate(newCoords->getAt(0).x, newCoords->getAt(0).y));
 
 1720     geos::geom::LinearRing *newRing = NULL;
 
 1724       newRing = globalFactory->createLinearRing(newCoords);
 
 1726     catch(geos::util::GEOSException *exc) {
 
 1730       IString msg = 
"Error when attempting to reduce precision of linear ring";
 
 1731       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1736       geos::geom::LinearRing *tmp = Despike(newRing);
 
 1743     if(!newRing->isValid()) {
 
 1744       IString msg = 
"Failed when attempting to reduce precision of linear ring";
 
 1745       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1761   geos::geom::Coordinate *PolygonTools::ReducePrecision(
const geos::geom::Coordinate *coord, 
 
 1762                                                         unsigned int precision) {
 
 1763     return new geos::geom::Coordinate(
 
 1764              ReducePrecision(coord->x, precision),
 
 1765              ReducePrecision(coord->y, precision),
 
 1766              ReducePrecision(coord->z, precision)
 
 1780   double PolygonTools::ReducePrecision(
double num, 
unsigned int precision) {
 
 1781     double result = num;
 
 1785       int decimalPlace = DecimalPlace(num);
 
 1786       double factor = pow(10.0, (
int)decimalPlace);
 
 1789       double reducedNum = num / factor;
 
 1791       double cutoff = pow(10.0, (
int)precision);
 
 1792       double round_offset = (num < 0) ? -0.5 : 0.5;
 
 1795       reducedNum = ((
long long)(reducedNum * cutoff + round_offset)) / cutoff;
 
 1796       result = reducedNum * factor;
 
 1811   QString PolygonTools::GetGeometryName(
const geos::geom::Geometry *geom) {
 
 1812     switch(geom->getGeometryTypeId()) {
 
 1813       case geos::geom::GEOS_POINT:
 
 1815       case geos::geom::GEOS_LINESTRING:
 
 1816         return "Line String";
 
 1817       case geos::geom::GEOS_LINEARRING:
 
 1818         return "Linear Ring";
 
 1819       case geos::geom::GEOS_POLYGON:
 
 1821       case geos::geom::GEOS_MULTIPOINT:
 
 1822         return "Multi Point";
 
 1823       case geos::geom::GEOS_MULTILINESTRING:
 
 1824         return "Multi Line String";
 
 1825       case geos::geom::GEOS_MULTIPOLYGON:
 
 1826         return "Multi Polygon";
 
 1827       case geos::geom::GEOS_GEOMETRYCOLLECTION:
 
 1828         return "Geometry Collection";
 
 1835   bool PolygonTools::Equal(
const geos::geom::MultiPolygon *poly1, 
 
 1836                            const geos::geom::MultiPolygon *poly2) {
 
 1838     vector<const geos::geom::Polygon *> polys1;
 
 1839     vector<const geos::geom::Polygon *> polys2;
 
 1841     if(poly1->getNumGeometries() != poly2->getNumGeometries())  
return false;
 
 1844     for(
unsigned int geomIndex = 0; geomIndex < poly1->getNumGeometries(); geomIndex ++) {
 
 1845       polys1.push_back(
dynamic_cast<const geos::geom::Polygon *
>(
 
 1846           poly1->getGeometryN(geomIndex)));
 
 1847       polys2.push_back(
dynamic_cast<const geos::geom::Polygon *
>(
 
 1848           poly2->getGeometryN(geomIndex)));
 
 1851     for(
int p1 = polys1.size() - 1; (p1 >= 0) && polys1.size(); p1 --) {
 
 1852       for(
int p2 = polys2.size() - 1; (p2 >= 0) && polys2.size(); p2 --) {
 
 1853         if(Equal(polys1[p1], polys2[p2])) {
 
 1855           polys1[p1] = polys1[polys1.size()-1];
 
 1856           polys1.resize(polys1.size() - 1);
 
 1858           polys2[p2] = polys2[polys2.size()-1];
 
 1859           polys2.resize(polys2.size() - 1);
 
 1864     return (polys1.size() == 0) && (polys2.size() == 0);
 
 1868   bool PolygonTools::Equal(
const geos::geom::Polygon *poly1, 
const geos::geom::Polygon *poly2) {
 
 1869     vector<const geos::geom::LineString *> holes1;
 
 1870     vector<const geos::geom::LineString *> holes2;
 
 1872     if(poly1->getNumInteriorRing() != poly2->getNumInteriorRing())  
return false;
 
 1874     if(!Equal(poly1->getExteriorRing(), poly2->getExteriorRing()))  
return false;
 
 1877     for(
unsigned int holeIndex = 0; holeIndex < poly1->getNumInteriorRing(); holeIndex ++) {
 
 1880       if(poly1->getInteriorRingN(holeIndex)->getGeometryTypeId() == geos::geom::GEOS_LINESTRING) {
 
 1881         holes1.push_back(poly1->getInteriorRingN(holeIndex));
 
 1884       if(poly2->getInteriorRingN(holeIndex)->getGeometryTypeId() == geos::geom::GEOS_LINESTRING) {
 
 1885         holes2.push_back(poly2->getInteriorRingN(holeIndex));
 
 1890     if(holes1.size() != holes2.size())  
return false;
 
 1892     for(
int h1 = holes1.size() - 1; (h1 >= 0) && holes1.size(); h1 --) {
 
 1893       for(
int h2 = holes2.size() - 1; (h2 >= 0) && holes2.size(); h2 --) {
 
 1894         if(Equal(holes1[h1], holes2[h2])) {
 
 1896           holes1[h1] = holes1[holes1.size()-1];
 
 1897           holes1.resize(holes1.size() - 1);
 
 1899           holes2[h2] = holes2[holes2.size()-1];
 
 1900           holes2.resize(holes2.size() - 1);
 
 1905     return (holes1.size() == 0) && (holes2.size() == 0);
 
 1909   bool PolygonTools::Equal(
const geos::geom::LineString *lineString1, 
 
 1910                            const geos::geom::LineString *lineString2) {
 
 1912     geos::geom::CoordinateSequence *coords1 = lineString1->getCoordinates();
 
 1913     geos::geom::CoordinateSequence *coords2 = lineString2->getCoordinates();
 
 1914     bool isEqual = 
true;
 
 1916     if(coords1->getSize() != coords2->getSize()) isEqual = 
false;
 
 1918     unsigned int index1 = 0;
 
 1919     unsigned int index2 = 0;
 
 1922     for(; index2 < coords2->getSize() - 1 && isEqual; index2 ++) {
 
 1923       if(Equal(coords1->getAt(index1), coords2->getAt(index2)))  
break;
 
 1926     if(index2 == coords2->getSize() - 1) isEqual = 
false;
 
 1928     for(; index1 < coords1->getSize() - 1 && isEqual; index1 ++, index2 ++) {
 
 1929       if(!Equal(coords1->getAt(index1), coords2->getAt(index2 % (coords2->getSize() - 1)))) {
 
 1940   bool PolygonTools::Equal(
const geos::geom::Coordinate &coord1, 
 
 1941                            const geos::geom::Coordinate &coord2) {
 
 1943     if(!Equal(coord1.x, coord2.x))  
return false;
 
 1944     if(!Equal(coord1.y, coord2.y))  
return false;
 
 1945     if(!Equal(coord1.y, coord2.y))  
return false;
 
 1951   bool PolygonTools::Equal(
const double d1, 
const double d2) {
 
 1952     const double cutoff = 1e15;
 
 1954     if(DecimalPlace(d1) != DecimalPlace(d2)) 
return false;
 
 1956     int decimalPlace = DecimalPlace(d1);
 
 1957     double factor = pow(10.0, (
int)decimalPlace);
 
 1960     double reducedNum = d1 / factor;
 
 1962     double round_offset = (d1 < 0) ? -0.5 : 0.5;
 
 1965     long long num1 = ((
long long)(reducedNum * cutoff + round_offset));
 
 1967     factor = pow(10.0, (
int)decimalPlace);
 
 1970     reducedNum = d2 / factor;
 
 1972     round_offset = (d2 < 0) ? -0.5 : 0.5;
 
 1975     long long num2 = ((
long long)(reducedNum * cutoff + round_offset));
 
 1978     return (num1 == num2);
 
 1996   geos::geom::MultiPolygon *PolygonTools::SplitPolygonOn360(
const geos::geom::Polygon *inPoly) {
 
 1997     bool convertLon = 
false;
 
 1998     bool negAdjust = 
false;
 
 1999     bool newCoords = 
false;  
 
 2000     geos::geom::CoordinateSequence *newLonLatPts = 
new geos::geom::CoordinateArraySequence();
 
 2002     double lonOffset = 0;
 
 2003     geos::geom::CoordinateSequence *inPolyCoords = inPoly->getCoordinates();
 
 2004     double prevLon = inPolyCoords->getAt(0).x;
 
 2005     double prevLat = inPolyCoords->getAt(0).y;
 
 2007     newLonLatPts->add(geos::geom::Coordinate(prevLon, prevLat));
 
 2009     for (
unsigned int i = 1; i < inPolyCoords->getSize(); i++) {
 
 2010       lon = inPolyCoords->getAt(i).x;
 
 2011       lat = inPolyCoords->getAt(i).y;
 
 2013       if (abs(lon - prevLon) > 180 && (prevLat != 90 && prevLat != -90)) {
 
 2021           if ((lon - prevLon) > 0) {
 
 2025           else if ((lon - prevLon) < 0) {
 
 2034       if (newCoords  &&  dist == 0.0) {
 
 2035         double longitude = (lon + lonOffset) - prevLon;
 
 2036         double latitude = lat - prevLat;
 
 2037         dist = std::sqrt((longitude * longitude) + (latitude * latitude));
 
 2041       newLonLatPts->add(geos::geom::Coordinate(lon + lonOffset, lat));
 
 2048     delete inPolyCoords;
 
 2052       geos::geom::Polygon *newPoly = globalFactory->createPolygon
 
 2053                                      (globalFactory->createLinearRing(newLonLatPts), NULL);
 
 2054       geos::geom::MultiPolygon *multi_polygon = PolygonTools::MakeMultiPolygon(newPoly);
 
 2055       delete newLonLatPts;
 
 2056       return multi_polygon;
 
 2061       geos::geom::Polygon *newPoly = globalFactory->createPolygon
 
 2062                                      (globalFactory->createLinearRing(newLonLatPts), NULL);
 
 2064       geos::geom::CoordinateSequence *pts = 
new geos::geom::CoordinateArraySequence();
 
 2065       geos::geom::CoordinateSequence *pts2 = 
new geos::geom::CoordinateArraySequence();
 
 2073         pts->add(geos::geom::Coordinate(0., 90.));
 
 2074         pts->add(geos::geom::Coordinate(-360., 90.));
 
 2075         pts->add(geos::geom::Coordinate(-360., -90.));
 
 2076         pts->add(geos::geom::Coordinate(0., -90.));
 
 2077         for (
double lat = -90.0 + dist; lat < 90.0; lat += dist) {
 
 2078           pts->add(geos::geom::Coordinate(0.0, lat));
 
 2080         pts->add(geos::geom::Coordinate(0., 90.));
 
 2081         pts2->add(geos::geom::Coordinate(0., 90.));
 
 2082         pts2->add(geos::geom::Coordinate(360., 90.));
 
 2083         pts2->add(geos::geom::Coordinate(360., -90.));
 
 2084         pts2->add(geos::geom::Coordinate(0., -90.));
 
 2085         for (
double lat = -90.0 + dist; lat < 90.0; lat += dist) {
 
 2086           pts2->add(geos::geom::Coordinate(0.0, lat));
 
 2088         pts2->add(geos::geom::Coordinate(0., 90.));
 
 2091         pts->add(geos::geom::Coordinate(360., 90.));
 
 2092         pts->add(geos::geom::Coordinate(720., 90.));
 
 2093         pts->add(geos::geom::Coordinate(720., -90.));
 
 2094         pts->add(geos::geom::Coordinate(360., -90.));
 
 2095         for (
double lat = -90.0 + dist; lat < 90.0; lat += dist) {
 
 2096           pts->add(geos::geom::Coordinate(360.0, lat));
 
 2098         pts->add(geos::geom::Coordinate(360., 90.));
 
 2099         pts2->add(geos::geom::Coordinate(360., 90.));
 
 2100         pts2->add(geos::geom::Coordinate(0., 90.));
 
 2101         pts2->add(geos::geom::Coordinate(0., -90.));
 
 2102         pts2->add(geos::geom::Coordinate(360., -90.));
 
 2103         for (
double lat = -90.0 + dist; lat < 90.0; lat += dist) {
 
 2104           pts2->add(geos::geom::Coordinate(360.0, lat));
 
 2106         pts2->add(geos::geom::Coordinate(360., 90.));
 
 2109       geos::geom::Polygon *boundaryPoly = globalFactory->createPolygon
 
 2110                                           (globalFactory->createLinearRing(pts), NULL);
 
 2111       geos::geom::Polygon *boundaryPoly2 = globalFactory->createPolygon
 
 2112                                            (globalFactory->createLinearRing(pts2), NULL);
 
 2118       geos::geom::Geometry *intersection = PolygonTools::Intersect(newPoly, boundaryPoly);
 
 2119       geos::geom::MultiPolygon *convertPoly = PolygonTools::MakeMultiPolygon(intersection);
 
 2120       delete intersection;
 
 2122       intersection = PolygonTools::Intersect(newPoly, boundaryPoly2);
 
 2123       geos::geom::MultiPolygon *convertPoly2 = PolygonTools::MakeMultiPolygon(intersection);
 
 2124       delete intersection;
 
 2131       vector<geos::geom::Geometry *> *finalpolys = 
new vector<geos::geom::Geometry *>;
 
 2132       geos::geom::Geometry *newGeom = NULL;
 
 2134       for (
unsigned int i = 0; i < convertPoly->getNumGeometries(); i++) {
 
 2135         newGeom = (convertPoly->getGeometryN(i))->clone();
 
 2136         pts = convertPoly->getGeometryN(i)->getCoordinates();
 
 2137         geos::geom::CoordinateSequence *newLonLatPts = 
new geos::geom::CoordinateArraySequence();
 
 2140         for (
unsigned int k = 0; k < pts->getSize() ; k++) {
 
 2141           double lon = pts->getAt(k).x;
 
 2142           double lat = pts->getAt(k).y;
 
 2149           newLonLatPts->add(geos::geom::Coordinate(lon, lat), k);
 
 2153         finalpolys->push_back(globalFactory->createPolygon
 
 2154                               (globalFactory->createLinearRing(newLonLatPts), NULL));
 
 2158       for (
unsigned int i = 0; i < convertPoly2->getNumGeometries(); i++) {
 
 2159         newGeom = (convertPoly2->getGeometryN(i))->clone();
 
 2160         finalpolys->push_back(newGeom);
 
 2163       geos::geom::MultiPolygon *multi_polygon = globalFactory->createMultiPolygon(*finalpolys);
 
 2167       delete newLonLatPts;
 
 2170       return multi_polygon;
 
 2172     catch(geos::util::IllegalArgumentException *geosIll) {
 
 2173       std::string msg = 
"Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
 
 2174       msg += 
"geos illegal argument [" + 
IString(geosIll->what()) + 
"]";
 
 2176       throw IException(IException::Unknown, msg, _FILEINFO_);
 
 2178     catch(geos::util::GEOSException *geosExc) {
 
 2179       std::string msg = 
"Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
 
 2180       msg += 
"geos exception [" + 
IString(geosExc->what()) + 
"]";
 
 2182       throw IException(IException::Unknown, msg, _FILEINFO_);
 
 2185       std::string msg = 
"Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
 
 2186       msg += 
"isis operation exception [" + 
IString(e.
what()) + 
"]";
 
 2187       throw IException(e, IException::Unknown, msg, _FILEINFO_);
 
 2190       std::string msg = 
"Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
 
 2191       msg += 
"unknown exception";
 
 2192       throw IException(IException::Unknown, msg, _FILEINFO_);