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" 
   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";
 
   72     if(lonLatPolygon.isEmpty()) {
 
   73       return globalFactory.createMultiPolygon();
 
   76       vector<geos::geom::Geometry *> *xyPolys = 
new vector<geos::geom::Geometry *>;
 
   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));
 
   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();
 
   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(),
 
   98           geos::geom::LinearRing *hole = globalFactory.createLinearRing(xycoords);
 
  100           if(hole->isValid() && !hole->isEmpty()) {
 
  101             holes->push_back(hole);
 
  109         geos::geom::CoordinateSequence *xycoords = 
new geos::geom::CoordinateArraySequence();
 
  110         geos::geom::CoordinateSequence *llcoords =
 
  111           poly->getExteriorRing()->getCoordinates();
 
  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(),
 
  121         geos::geom::Polygon *newPoly = globalFactory.createPolygon(
 
  122                                          globalFactory.createLinearRing(xycoords), holes);
 
  124         if(newPoly->isValid() && !newPoly->isEmpty() && newPoly->getArea() > 1.0e-14) {
 
  125           xyPolys->push_back(newPoly);
 
  133       geos::geom::MultiPolygon *spikedPoly = globalFactory.createMultiPolygon(xyPolys);
 
  135       if(spikedPoly->isValid() && !spikedPoly->isEmpty()) {
 
  140           geos::geom::MultiPolygon *despikedPoly = Despike(spikedPoly);
 
  148           IString msg = 
"Unable to convert polygon from Lat/Lon to X/Y";
 
  169   geos::geom::MultiPolygon *PolygonTools::XYToLatLon(
 
  170     const geos::geom::MultiPolygon &xYPolygon, 
TProjection *projection) {
 
  172     if(projection == NULL) {
 
  173       string msg = 
"Unable to convert X/Y polygon to Lon/Lat. ";
 
  174       msg += 
"No projection was supplied";
 
  179     if(xYPolygon.isEmpty()) {
 
  180       return globalFactory.createMultiPolygon();
 
  183       vector<geos::geom::Geometry *> *llPolys = 
new vector<geos::geom::Geometry *>;
 
  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));
 
  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();
 
  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(),
 
  204           holes->push_back(globalFactory.createLinearRing(llcoords));
 
  208         geos::geom::CoordinateSequence *llcoords = 
new geos::geom::DefaultCoordinateSequence();
 
  209         geos::geom::CoordinateSequence *xycoords =
 
  210           poly->getExteriorRing()->getCoordinates();
 
  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(),
 
  220         llPolys->push_back(globalFactory.createPolygon(
 
  221                              globalFactory.createLinearRing(llcoords), holes));
 
  226       geos::geom::MultiPolygon *spikedPoly = globalFactory.createMultiPolygon(llPolys);
 
  228       if(spikedPoly->isValid() && !spikedPoly->isEmpty()) {
 
  233           geos::geom::MultiPolygon *despikedPoly = Despike(spikedPoly);
 
  241           IString msg = 
"Unable to convert polygon from X/Y to Lat/Lon";
 
  261   geos::geom::MultiPolygon *PolygonTools::LatLonToSampleLine(
 
  265       string msg = 
"Unable to convert Lon/Lat polygon to Sample/Line. ";
 
  266       msg += 
"No UniversalGroundMap was supplied";
 
  271     if(lonLatPolygon.isEmpty()) {
 
  272       return globalFactory.createMultiPolygon();
 
  275       vector<geos::geom::Geometry *> *slPolys = 
new vector<geos::geom::Geometry *>;
 
  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));
 
  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();
 
  290           for(
unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
 
  292                                     llcoords->getAt(cord).x);
 
  293             slcoords->add(geos::geom::Coordinate(ugm->
Sample(),
 
  296           holes->push_back(globalFactory.createLinearRing(slcoords));
 
  302         geos::geom::CoordinateSequence *slcoords = 
new geos::geom::CoordinateArraySequence();
 
  303         geos::geom::CoordinateSequence *llcoords =
 
  304           poly->getExteriorRing()->getCoordinates();
 
  307         for(
unsigned int cord = 0; cord < llcoords->getSize(); ++cord) {
 
  309                                       llcoords->getAt(cord).x)) {
 
  310             slcoords->add(geos::geom::Coordinate(ugm->
Sample(),
 
  316         if (slcoords->getSize() > 0 && !slcoords->front().equals(slcoords->back())) {
 
  317           slcoords->add(slcoords->front());
 
  321           slPolys->push_back(globalFactory.createPolygon(
 
  322                               globalFactory.createLinearRing(slcoords), holes));
 
  324         catch (std::exception &e) {
 
  326                            QObject::tr(
"Unable to convert polygon from Lat/Lon to Sample/Line. The " 
  327                                        "error given was [%1].").arg(e.what()),
 
  335       geos::geom::MultiPolygon *spikedPoly = globalFactory.createMultiPolygon(slPolys);
 
  337       if(spikedPoly->isValid() && !spikedPoly->isEmpty()) {
 
  342           geos::geom::MultiPolygon *despikedPoly = Despike(spikedPoly);
 
  350           IString msg = 
"Unable to convert polygon from Lat/Lon to Sample/Line";
 
  370   geos::geom::MultiPolygon *PolygonTools::CopyMultiPolygon(
const geos::geom::MultiPolygon *mpolygon) {
 
  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());
 
  376     return globalFactory.createMultiPolygon(polys);
 
  392   geos::geom::MultiPolygon *PolygonTools::CopyMultiPolygon(
const geos::geom::MultiPolygon &mpolygon) {
 
  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());
 
  398     return globalFactory.createMultiPolygon(polys);
 
  410   QString PolygonTools::ToGML(
const geos::geom::MultiPolygon *mpolygon, QString idString,
 
  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>";
 
  426                        setprecision(15) << mpolygon->getEnvelopeInternal()->getMinX() << 
 
  429                        setprecision(15) << mpolygon->getEnvelopeInternal()->getMinY() << 
 
  431     os <<      
"</gml:coord>" << endl;
 
  432     os << 
"      <gml:coord>";
 
  434                        setprecision(15) << mpolygon->getEnvelopeInternal()->getMaxX() <<
 
  437                        setprecision(15) << mpolygon->getEnvelopeInternal()->getMaxY() << 
 
  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>";
 
  449     for(
unsigned int polyN = 0; polyN < mpolygon->getNumGeometries(); polyN++) {
 
  450       geos::geom::CoordinateSequence *pts = mpolygon->getGeometryN(polyN)->getCoordinates();
 
  452       for(
unsigned int i = 0; i < pts->getSize(); i++) {
 
  453         double lon = pts->getAt(i).x;
 
  454         double lat = pts->getAt(i).y;
 
  456         os << setprecision(15) << lon << 
"," << setprecision(15) << lat << 
" ";
 
  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>";
 
  467     return os.str().c_str();
 
  477   QString PolygonTools::GMLSchema() {
 
  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>";
 
  532     return os.str().c_str();
 
  543   geos::geom::MultiPolygon *PolygonTools::To180(geos::geom::MultiPolygon *poly360) {
 
  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));
 
  559       geos::geom::LinearRing *leftOf180Geom =
 
  560           globalFactory.createLinearRing(leftOf180Pts);
 
  562       geos::geom::Polygon *leftOf180Poly =
 
  563           globalFactory.createPolygon(leftOf180Geom, NULL);
 
  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));
 
  573       geos::geom::LinearRing *rightOf180Geom =
 
  574           globalFactory.createLinearRing(rightOf180Pts);
 
  576       geos::geom::Polygon *rightOf180Poly =
 
  577           globalFactory.createPolygon(rightOf180Geom, NULL);
 
  579       geos::geom::Geometry *preserved = Intersect(leftOf180Poly, poly360);
 
  580       geos::geom::Geometry *moving = Intersect(rightOf180Poly, poly360);
 
  582       geos::geom::CoordinateSequence *movingPts = moving->getCoordinates();
 
  583       geos::geom::CoordinateSequence *movedPts =
 
  584           new geos::geom::CoordinateArraySequence();
 
  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));
 
  591       if(movedPts->getSize()) {
 
  592         movedPts->add(geos::geom::Coordinate(movedPts->getAt(0).x,
 
  593                                             movedPts->getAt(0).y));
 
  596       geos::geom::Geometry *moved = globalFactory.createPolygon(
 
  597           globalFactory.createLinearRing(movedPts), NULL);
 
  599       std::vector<geos::geom::Geometry *> *geomsForCollection = 
new 
  600           std::vector<geos::geom::Geometry *>;
 
  601       geomsForCollection->push_back(preserved);
 
  602       geomsForCollection->push_back(moved);
 
  604       geos::geom::GeometryCollection *the180Polys =
 
  605           Isis::globalFactory.createGeometryCollection(geomsForCollection);
 
  607       geos::geom::MultiPolygon *result = MakeMultiPolygon(the180Polys);
 
  611       geos::geom::MultiPolygon *fixedResult = FixSeam(result);
 
  617     catch(geos::util::GEOSException *exc) {
 
  618       IString msg = 
"Conversion to 180 failed. The reason given was [" +
 
  624       IString msg = 
"Conversion to 180 failed. Could not determine the reason";
 
  640   double PolygonTools::Thickness(
const geos::geom::MultiPolygon *mpolygon) {
 
  641     const geos::geom::Envelope *envelope = mpolygon->getEnvelopeInternal();
 
  643     double x = fabs(envelope->getMaxX() - envelope->getMinX());
 
  644     double y = fabs(envelope->getMaxY() - envelope->getMinY());
 
  645     double extent = max(x, y);
 
  647     return mpolygon->getArea() / (extent * extent);
 
  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);
 
  683   geos::geom::MultiPolygon *PolygonTools::Despike(
const geos::geom::MultiPolygon *multiPoly) {
 
  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));
 
  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;
 
  701           geos::geom::LinearRing *fixed = FixGeometry(lr);
 
  708           holes->push_back(lr);
 
  716       const geos::geom::LineString *ls = poly->getExteriorRing();
 
  717       geos::geom::LinearRing *lr;
 
  723           geos::geom::LinearRing *fixed = FixGeometry(lr);
 
  731         if(ls->isValid() && ls->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
  732           lr = 
dynamic_cast<geos::geom::LinearRing *
>(ls->clone());
 
  741         geos::geom::Polygon *tp = Isis::globalFactory.createPolygon(lr, holes);
 
  743         if(tp->isEmpty() || !tp->isValid()) {
 
  745           newPolys->push_back(poly->clone());
 
  748           newPolys->push_back(tp);
 
  754     geos::geom::MultiPolygon *mp = Isis::globalFactory.createMultiPolygon(newPolys);
 
  756     if(!mp->isValid() || mp->isEmpty()) {
 
  759       IString msg = 
"Despike failed to correct the polygon";
 
  764     if(fabs((mp->getArea() / multiPoly->getArea()) - 1.0) > 0.50) {
 
  765       IString msg = 
"Despike failed to correct the polygon " + mp->toString();
 
  788   geos::geom::LinearRing *PolygonTools::Despike(
const geos::geom::LineString *lineString) {
 
  789     geos::geom::CoordinateSequence *vertices = lineString->getCoordinates();
 
  792     if(vertices->getSize() < 4) {
 
  794       return Isis::globalFactory.createLinearRing(geos::geom::CoordinateArraySequence());
 
  799     vertices->deleteAt(vertices->getSize() - 1);
 
  804     for(
long index = 0; index < (long)vertices->getSize(); index++) {
 
  806       if(vertices->getSize() < 3) {
 
  812       long testCoords[3] = {
 
  820       for(
int j = 0; j < 3; j++) {
 
  821         while(testCoords[j] < 0) {
 
  822           testCoords[j] += vertices->getSize();
 
  825         while(testCoords[j] >= (
long)vertices->getSize()) {
 
  826           testCoords[j] -= vertices->getSize();
 
  831       if(IsSpiked(vertices->getAt(testCoords[0]),
 
  832                   vertices->getAt(testCoords[1]),
 
  833                   vertices->getAt(testCoords[2]))) {
 
  835         vertices->deleteAt(testCoords[1]);
 
  842     if(vertices->getSize() < 3) {
 
  846       return Isis::globalFactory.createLinearRing(geos::geom::CoordinateArraySequence());
 
  850       vertices->add(vertices->getAt(0));
 
  851       return Isis::globalFactory.createLinearRing(vertices);
 
  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);
 
  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);
 
  904     geos::geom::CoordinateSequence *coords = 
new geos::geom::CoordinateArraySequence();
 
  907     geos::geom::LineString *line = Isis::globalFactory.createLineString(coords); 
 
  913     double tolerance = line->getLength() / 100.0;
 
  917     double distanceLastMiddle = geos::operation::distance::DistanceOp::distance(lastPt, middlePt);
 
  918     double distanceLastLine = geos::operation::distance::DistanceOp::distance(lastPt, line);
 
  920     if(distanceLastMiddle == 0.0) 
return true; 
 
  924     if(distanceLastLine / distanceLastMiddle >= .05) {
 
  929     if(spiked && distanceLastLine > tolerance) {
 
  934       geos::geom::CoordinateSequence *coords = 
new geos::geom::CoordinateArraySequence();
 
  941       geos::geom::LinearRing *shell = Isis::globalFactory.createLinearRing(coords);
 
  942       std::vector<geos::geom::Geometry *> *empty = 
new std::vector<geos::geom::Geometry *>;
 
  945       geos::geom::Polygon *poly = Isis::globalFactory.createPolygon(shell, empty);
 
  950       if(poly->getArea() < 1.0e-10) {
 
  980   geos::geom::Geometry *PolygonTools::Intersect(
const geos::geom::Geometry *geom1, 
 
  981                                                 const geos::geom::Geometry *geom2) {
 
  983       return Operate(geom1, geom2, (
unsigned int)geos::operation::overlay::OverlayOp::opINTERSECTION);
 
  985     catch(geos::util::GEOSException *exc) {
 
  986       IString msg = 
"Intersect operation failed. The reason given was [" + 
IString(exc->what()) + 
"]";
 
  991       IString msg = 
"Intersect operation failed";
 
  995       IString msg = 
"Intersect operation failed for an unknown reason";
 
 1001   geos::geom::Geometry *PolygonTools::Operate(
const geos::geom::Geometry *geom1,
 
 1002                                               const geos::geom::Geometry *geom2, 
 
 1003                                               unsigned int opcode) {
 
 1005     geos::operation::overlay::OverlayOp::OpCode code =
 
 1006       (geos::operation::overlay::OverlayOp::OpCode)opcode;
 
 1008     geos::geom::Geometry *result = NULL;
 
 1010     geos::geom::Geometry *geomFirst  = MakeMultiPolygon(geom1);
 
 1011     geos::geom::Geometry *geomSecond = MakeMultiPolygon(geom2);
 
 1013     geos::operation::overlay::snap::GeometrySnapper snap(*geomFirst);
 
 1014     geos::geom::Geometry *geomSnapped = snap.snapTo(*geomSecond, 1.0e-10)->clone();
 
 1015     if(!geomSnapped->isValid()) {
 
 1020       geomFirst = geomSnapped;
 
 1023     unsigned int precision = 15;
 
 1024     unsigned int minPrecision = 13;
 
 1027         std::auto_ptr< geos::geom::Geometry > resultAuto =
 
 1028           BinaryOp(geomFirst, geomSecond, geos::operation::overlay::overlayOp(code));
 
 1030         result = resultAuto->clone();
 
 1032       catch(geos::util::GEOSException *exc) {
 
 1034         if(!failed || precision == minPrecision) 
throw;
 
 1039         if(precision == minPrecision) {
 
 1040           IString msg = 
"An unknown geos error occurred";
 
 1041           throw IException(IException::Programmer, msg, 
_FILEINFO_);
 
 1045           IString msg = 
"An unknown geos error occurred when attempting to clone a geometry";
 
 1046           throw IException(IException::Programmer, msg, 
_FILEINFO_);
 
 1053         geos::geom::Geometry *tmp = ReducePrecision(geomFirst, precision);
 
 1057         tmp = ReducePrecision(geomSecond, precision);
 
 1069     if(result && !result->isValid()) {
 
 1071         geos::geom::Geometry *newResult = FixGeometry(result);
 
 1073         if(fabs(newResult->getArea() / result->getArea() - 1.0) > 0.50) {
 
 1077           IString msg = 
"Operation [" + IString((
int)opcode) + 
"] failed";
 
 1078           throw IException(IException::Programmer, msg, 
_FILEINFO_);
 
 1084       catch(IException &e) {
 
 1085         IString msg = 
"Operation [" + IString((
int)opcode) + 
"] failed";
 
 1086         throw IException(IException::Programmer, msg, 
_FILEINFO_);
 
 1090     if(result == NULL) {
 
 1091       IString msg = 
"Operation [" + IString((
int)opcode) + 
" failed";
 
 1092       throw IException(IException::Programmer, msg, 
_FILEINFO_);
 
 1108   geos::geom::Geometry *PolygonTools::FixGeometry(
const geos::geom::Geometry *geom) {
 
 1109     if(geom->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
 
 1110       return FixGeometry(dynamic_cast<const geos::geom::MultiPolygon *>(geom));
 
 1112     if(geom->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
 1113       return FixGeometry(dynamic_cast<const geos::geom::LinearRing *>(geom));
 
 1115     if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
 
 1116       return FixGeometry(dynamic_cast<const geos::geom::Polygon *>(geom));
 
 1118     if(geom->getGeometryTypeId() == geos::geom::GEOS_GEOMETRYCOLLECTION) {
 
 1119       return FixGeometry(MakeMultiPolygon(geom));
 
 1122       IString msg = 
"PolygonTools::FixGeometry does not support [" + GetGeometryName(geom) + 
"]";
 
 1136   geos::geom::MultiPolygon *PolygonTools::FixGeometry(
const geos::geom::MultiPolygon *poly) {
 
 1138     vector<geos::geom::Geometry *> *newPolys = 
new vector<geos::geom::Geometry *>;
 
 1141     for(
unsigned int geomIndex = 0; geomIndex < poly->getNumGeometries(); geomIndex ++) {
 
 1142       geos::geom::Polygon *fixedpoly = FixGeometry(
 
 1143           dynamic_cast<const geos::geom::Polygon *>(
 
 1144             poly->getGeometryN(geomIndex)));
 
 1145       if(fixedpoly->isValid()) {
 
 1146         newPolys->push_back(fixedpoly);
 
 1154     geos::geom::MultiPolygon *mp = Isis::globalFactory.createMultiPolygon(newPolys);
 
 1159   geos::geom::Polygon *PolygonTools::FixGeometry(
const geos::geom::Polygon *poly) {
 
 1162     vector<geos::geom::Geometry *> *holes = 
new vector<geos::geom::Geometry *>;
 
 1163     for(
unsigned int holeIndex = 0; holeIndex < poly->getNumInteriorRing(); holeIndex ++) {
 
 1164       const geos::geom::LineString *thisHole = poly->getInteriorRingN(holeIndex);
 
 1167       if(thisHole->getGeometryTypeId() != geos::geom::GEOS_LINEARRING) {
 
 1168         holes->push_back(thisHole->clone());
 
 1173         geos::geom::LinearRing *newHole = FixGeometry((geos::geom::LinearRing *)thisHole);
 
 1174         holes->push_back(newHole);
 
 1176       catch (IException &e) {
 
 1177         IString msg = 
"Failed when attempting to fix interior ring of multipolygon";
 
 1178         throw IException(IException::Programmer, msg, 
_FILEINFO_);
 
 1183     const geos::geom::LineString *exterior = poly->getExteriorRing();
 
 1186       geos::geom::LinearRing *newExterior = NULL;
 
 1188       if(exterior->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
 1189         newExterior = FixGeometry((geos::geom::LinearRing *)exterior);
 
 1192         IString msg = 
"Failed when attempting to fix exterior ring of polygon. The exterior " 
 1193                       "ring is not simple and closed";
 
 1194         throw IException(IException::Programmer, msg, 
_FILEINFO_);
 
 1197       return globalFactory.createPolygon(newExterior, holes);
 
 1199     catch (IException &e) {
 
 1200       IString msg = 
"Failed when attempting to fix exterior ring of polygon";
 
 1201       throw IException(e, IException::Programmer, msg, 
_FILEINFO_);
 
 1221   geos::geom::LinearRing *PolygonTools::FixGeometry(
const geos::geom::LinearRing *ring) {
 
 1223     geos::geom::CoordinateSequence *coords = ring->getCoordinates();
 
 1226     if(coords->getSize() < 4) {
 
 1227       return globalFactory.createLinearRing(
new geos::geom::DefaultCoordinateSequence());
 
 1230     geos::geom::CoordinateSequence *newCoords = 
new geos::geom::DefaultCoordinateSequence();
 
 1231     const geos::geom::Coordinate *lastCoordinate = &coords->getAt(0);
 
 1232     newCoords->add(*lastCoordinate);
 
 1235     for(
unsigned int coordIndex = 1; coordIndex < coords->getSize() - 1; coordIndex ++) {
 
 1236       const geos::geom::Coordinate *thisCoordinate = &coords->getAt(coordIndex);
 
 1241       double difference[2] = {
 
 1242         lastCoordinate->x - thisCoordinate->x,
 
 1243         lastCoordinate->y - thisCoordinate->y,
 
 1247       double minDiff = fabs(DecimalPlace(thisCoordinate->x) - DecimalPlace(difference[0]));
 
 1249       minDiff = min(minDiff, fabs(DecimalPlace(thisCoordinate->y) - DecimalPlace(difference[1])));
 
 1253       if(difference[0] == 0.0 && difference[1] != 0.0) {
 
 1255         minDiff = fabs(DecimalPlace(thisCoordinate->y) - DecimalPlace(difference[1]));
 
 1257       else if(difference[1] == 0.0 && difference[0] != 0.0) {
 
 1259         minDiff = fabs(DecimalPlace(thisCoordinate->x) - DecimalPlace(difference[0]));
 
 1261       else if(difference[0] == 0.0 && difference[1] == 0.0) {
 
 1268         newCoords->add(*thisCoordinate);
 
 1269         lastCoordinate = thisCoordinate;
 
 1273     newCoords->add(geos::geom::Coordinate(newCoords->getAt(0).x, newCoords->getAt(0).y));
 
 1274     geos::geom::LinearRing *newRing = NULL;
 
 1278       if(newCoords->getSize() > 3) {
 
 1279         newRing = globalFactory.createLinearRing(newCoords);
 
 1286     catch(geos::util::GEOSException *exc) {
 
 1290       IString msg = 
"Error when attempting to fix linear ring";
 
 1294     if(newRing && !newRing->isValid() && ring->isValid()) {
 
 1295       IString msg = 
"Failed when attempting to fix linear ring";
 
 1298     else if(!newRing || !newRing->isValid()) {
 
 1303       newRing = 
dynamic_cast<geos::geom::LinearRing *
>(ring->clone());
 
 1321   int PolygonTools::DecimalPlace(
double num) {
 
 1323     if(num == 0.0) 
return 0;
 
 1327     int decimalPlace = 1;
 
 1338     return decimalPlace;
 
 1349   geos::geom::Geometry *PolygonTools::Difference(
const geos::geom::Geometry *geom1, 
 
 1350                                                  const geos::geom::Geometry *geom2) {
 
 1352       return Operate(geom1, geom2, (
unsigned int)geos::operation::overlay::OverlayOp::opDIFFERENCE);
 
 1354     catch(geos::util::GEOSException *exc) {
 
 1355       IString msg = 
"Difference operation failed. The reason given was [" + 
 
 1361       IString msg = 
"Difference operation failed";
 
 1365       IString msg = 
"Difference operation failed for an unknown reason";
 
 1384   geos::geom::MultiPolygon *PolygonTools::MakeMultiPolygon(
const geos::geom::Geometry *geom) {
 
 1386     if(geom->isEmpty()) {
 
 1387       return Isis::globalFactory.createMultiPolygon();
 
 1390     else if(geom->getArea() - DBL_EPSILON <= DBL_EPSILON) {
 
 1391       return Isis::globalFactory.createMultiPolygon();
 
 1394     else if(geom->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
 
 1395       return dynamic_cast<geos::geom::MultiPolygon *
>(geom->clone());
 
 1398     else if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
 
 1399       vector<geos::geom::Geometry *> *polys = 
new vector<geos::geom::Geometry *>;
 
 1400       polys->push_back(geom->clone());
 
 1401       geos::geom::MultiPolygon *mp = Isis::globalFactory.createMultiPolygon(polys);
 
 1405     else if(geom->getGeometryTypeId() == geos::geom::GEOS_GEOMETRYCOLLECTION) {
 
 1406       vector<geos::geom::Geometry *> * polys =
 
 1407           new vector<geos::geom::Geometry *>;
 
 1408       const geos::geom::GeometryCollection *gc =
 
 1409           dynamic_cast<const geos::geom::GeometryCollection *
>(geom);
 
 1410       for(
unsigned int i = 0; i < gc->getNumGeometries(); ++i) {
 
 1411         geos::geom::MultiPolygon *subMultiPoly =
 
 1412             MakeMultiPolygon(gc->getGeometryN(i));
 
 1414         for(
unsigned int subPoly = 0;
 
 1415             subPoly < subMultiPoly->getNumGeometries();
 
 1417           const geos::geom::Polygon *poly =
 
 1418               dynamic_cast<const geos::geom::Polygon *
>(
 
 1419                 subMultiPoly->getGeometryN(subPoly));
 
 1420           polys->push_back(dynamic_cast<geos::geom::Polygon *>(poly->clone()));
 
 1424       geos::geom::MultiPolygon *mp =
 
 1425           Isis::globalFactory.createMultiPolygon(polys);
 
 1426       if(mp->getArea() - DBL_EPSILON <= DBL_EPSILON) {
 
 1428         mp = Isis::globalFactory.createMultiPolygon();
 
 1436       return Isis::globalFactory.createMultiPolygon();
 
 1441   geos::geom::MultiPolygon *PolygonTools::FixSeam(
 
 1442       const geos::geom::Polygon *polyA, 
const geos::geom::Polygon *polyB) {
 
 1443     geos::geom::CoordinateSequence *polyAPoints = polyA->getCoordinates();
 
 1444     geos::geom::CoordinateSequence *polyBPoints = polyB->getCoordinates();
 
 1446     unsigned int aIntersectionBegin = 0;
 
 1447     unsigned int aIntersectionEnd = 0;
 
 1448     unsigned int bIntersectionBegin = 0;
 
 1449     unsigned int bIntersectionEnd = 0;
 
 1451     bool intersectionStarted = 
false;
 
 1452     bool intersectionEnded = 
false;
 
 1454     unsigned int lastBMatch  = 0;
 
 1455     for (
unsigned int i = 0;
 
 1456         !intersectionEnded && i < polyAPoints->getSize();
 
 1459       bool foundEquivalent = 
false;
 
 1461       geos::geom::Coordinate coordA = polyAPoints->getAt(i);
 
 1462       coordA = *ReducePrecision(&coordA, 13);
 
 1464       for (
unsigned int j = 0;
 
 1465            !foundEquivalent && j < polyBPoints->getSize();
 
 1467         geos::geom::Coordinate coordB = polyBPoints->getAt(j);
 
 1468         coordB = *ReducePrecision(&coordB, 13);
 
 1470         foundEquivalent = coordA.equals(coordB);
 
 1472         if (foundEquivalent) lastBMatch = j;
 
 1474         if (foundEquivalent && !intersectionStarted) {
 
 1475           intersectionStarted = 
true;
 
 1476           aIntersectionBegin = i;
 
 1477           bIntersectionBegin = j;
 
 1481       if (!foundEquivalent && intersectionStarted && !intersectionEnded) {
 
 1482         intersectionEnded = 
true;
 
 1483         aIntersectionEnd = i;
 
 1484         bIntersectionEnd = lastBMatch;
 
 1488     geos::geom::MultiPolygon * result = NULL;
 
 1489     if (intersectionStarted && intersectionEnded) {
 
 1490       geos::geom::CoordinateSequence *merged =
 
 1491           new geos::geom::CoordinateArraySequence;
 
 1494       for (i = 0; i < aIntersectionBegin; i ++) {
 
 1495         merged->add(polyAPoints->getAt(i));
 
 1498       i = bIntersectionBegin;
 
 1499       while (i != bIntersectionEnd) {
 
 1500         merged->add(polyBPoints->getAt(i));
 
 1502         if (i >= polyBPoints->getSize()) i = 0;
 
 1505       for (i = aIntersectionEnd; i < polyAPoints->getSize() - 1; i++) {
 
 1506         merged->add(polyAPoints->getAt(i));
 
 1509       merged->add(merged->getAt(0));
 
 1510       result = MakeMultiPolygon(globalFactory.createPolygon(
 
 1511           globalFactory.createLinearRing(merged), NULL));
 
 1518   geos::geom::MultiPolygon *PolygonTools::FixSeam(
 
 1519       const geos::geom::MultiPolygon *poly) {
 
 1521     std::vector<geos::geom::Geometry *> *polys =
 
 1522         new std::vector<geos::geom::Geometry *>;
 
 1525     for(
unsigned int copyIndex = 0;
 
 1526         copyIndex < poly->getNumGeometries();
 
 1528       polys->push_back(poly->getGeometryN(copyIndex)->clone());
 
 1531     unsigned int outerPolyIndex = 0;
 
 1533     while(outerPolyIndex + 1 < polys->size()) {
 
 1534       unsigned int innerPolyIndex = outerPolyIndex + 1;
 
 1536       while(innerPolyIndex < polys->size()) {
 
 1537         geos::geom::MultiPolygon *fixedPair = FixSeam(
 
 1538             dynamic_cast<geos::geom::Polygon *>(polys->at(outerPolyIndex)),
 
 1539             dynamic_cast<geos::geom::Polygon *>(polys->at(innerPolyIndex)));
 
 1541         if(fixedPair != NULL) {
 
 1542           geos::geom::Geometry *oldInnerPoly = polys->at(innerPolyIndex);
 
 1543           geos::geom::Geometry *oldOuterPoly = polys->at(outerPolyIndex);
 
 1545           polys->erase(polys->begin() + innerPolyIndex);
 
 1546           (*polys)[outerPolyIndex] = fixedPair->getGeometryN(0)->clone();
 
 1547           innerPolyIndex = outerPolyIndex + 1;
 
 1552           delete oldInnerPoly;
 
 1553           oldInnerPoly = NULL;
 
 1555           delete oldOuterPoly;
 
 1556           oldOuterPoly = NULL;
 
 1566     return globalFactory.createMultiPolygon(polys);
 
 1579   geos::geom::Geometry *PolygonTools::ReducePrecision(
const geos::geom::Geometry *geom, 
 
 1580                                                       unsigned int precision) {
 
 1581     if(geom->getGeometryTypeId() == geos::geom::GEOS_MULTIPOLYGON) {
 
 1582       return ReducePrecision(
 
 1583           dynamic_cast<const geos::geom::MultiPolygon *>(geom), precision);
 
 1585     if(geom->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
 1586       return ReducePrecision(
 
 1587           dynamic_cast<const geos::geom::LinearRing *>(geom), precision);
 
 1589     if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON) {
 
 1590       return ReducePrecision(
 
 1591           dynamic_cast<const geos::geom::Polygon *>(geom), precision);
 
 1593     if(geom->getGeometryTypeId() == geos::geom::GEOS_GEOMETRYCOLLECTION) {
 
 1594       return ReducePrecision(MakeMultiPolygon(geom), precision);
 
 1597       IString msg = 
"PolygonTools::ReducePrecision does not support [" + 
 
 1598                     GetGeometryName(geom) + 
"]";
 
 1613   geos::geom::MultiPolygon *PolygonTools::ReducePrecision(
const geos::geom::MultiPolygon *poly, 
 
 1614                                                           unsigned int precision) {
 
 1616     vector<geos::geom::Geometry *> *newPolys = 
new vector<geos::geom::Geometry *>;
 
 1619     for(
unsigned int geomIndex = 0; geomIndex < poly->getNumGeometries(); geomIndex ++) {
 
 1620       geos::geom::Geometry *lowerPrecision = ReducePrecision(
 
 1621           dynamic_cast<const geos::geom::Polygon *>(
 
 1622             poly->getGeometryN(geomIndex)),
 
 1625       if(!lowerPrecision->isEmpty()) {
 
 1626         newPolys->push_back(lowerPrecision);
 
 1629         delete lowerPrecision;
 
 1633     geos::geom::MultiPolygon *mp = Isis::globalFactory.createMultiPolygon(newPolys);
 
 1647   geos::geom::Polygon *PolygonTools::ReducePrecision(
const geos::geom::Polygon *poly, 
 
 1648                                                      unsigned int precision) {
 
 1650     vector<geos::geom::Geometry *> *holes = 
new vector<geos::geom::Geometry *>;
 
 1651     for(
unsigned int holeIndex = 0; holeIndex < poly->getNumInteriorRing(); holeIndex ++) {
 
 1652       const geos::geom::LineString *thisHole = poly->getInteriorRingN(holeIndex);
 
 1655       if(thisHole->getGeometryTypeId() != geos::geom::GEOS_LINEARRING) {
 
 1656         holes->push_back(thisHole->clone());
 
 1661         geos::geom::LinearRing *newHole = ReducePrecision((geos::geom::LinearRing *)thisHole, 
 
 1664         if(!newHole->isEmpty()) {
 
 1665           holes->push_back(newHole);
 
 1673         IString msg = 
"Failed when attempting to fix interior ring of multipolygon";
 
 1679     const geos::geom::LineString *exterior = poly->getExteriorRing();
 
 1682       geos::geom::LinearRing *newExterior = NULL;
 
 1684       if(exterior->getGeometryTypeId() == geos::geom::GEOS_LINEARRING) {
 
 1685         newExterior = ReducePrecision((geos::geom::LinearRing *)exterior, precision);
 
 1688         IString msg = 
"Failed when attempting to fix exterior ring of polygon. The exterior " 
 1689                       "ring is not simple and closed";
 
 1693       return globalFactory.createPolygon(newExterior, holes);
 
 1696       IString msg = 
"Failed when attempting to fix exterior ring of polygon";
 
 1711   geos::geom::LinearRing *PolygonTools::ReducePrecision(
const geos::geom::LinearRing *ring, 
 
 1712                                                         unsigned int precision) {
 
 1713     geos::geom::CoordinateSequence *coords = ring->getCoordinates();
 
 1716     if(coords->getSize() <= 0) 
return dynamic_cast<geos::geom::LinearRing *>(
 
 1719     geos::geom::CoordinateSequence *newCoords = 
new geos::geom::DefaultCoordinateSequence();
 
 1720     geos::geom::Coordinate *coord = ReducePrecision(&coords->getAt(0), precision);
 
 1721     newCoords->add(*coord);
 
 1726     for(
unsigned int coordIndex = 1; coordIndex < coords->getSize() - 1; coordIndex ++) {
 
 1727       const geos::geom::Coordinate *thisCoordinate = &coords->getAt(coordIndex);
 
 1728       coord = ReducePrecision(thisCoordinate, precision);
 
 1729       newCoords->add(*coord);
 
 1734     newCoords->add(geos::geom::Coordinate(newCoords->getAt(0).x, newCoords->getAt(0).y));
 
 1735     geos::geom::LinearRing *newRing = NULL;
 
 1739       newRing = globalFactory.createLinearRing(newCoords);
 
 1741     catch(geos::util::GEOSException *exc) {
 
 1745       IString msg = 
"Error when attempting to reduce precision of linear ring";
 
 1751       geos::geom::LinearRing *tmp = Despike(newRing);
 
 1758     if(!newRing->isValid()) {
 
 1759       IString msg = 
"Failed when attempting to reduce precision of linear ring";
 
 1776   geos::geom::Coordinate *PolygonTools::ReducePrecision(
const geos::geom::Coordinate *coord, 
 
 1777                                                         unsigned int precision) {
 
 1778     return new geos::geom::Coordinate(
 
 1779              ReducePrecision(coord->x, precision),
 
 1780              ReducePrecision(coord->y, precision),
 
 1781              ReducePrecision(coord->z, precision)
 
 1795   double PolygonTools::ReducePrecision(
double num, 
unsigned int precision) {
 
 1796     double result = num;
 
 1800       int decimalPlace = DecimalPlace(num);
 
 1801       double factor = pow(10.0, (
int)decimalPlace);
 
 1804       double reducedNum = num / factor;
 
 1806       double cutoff = pow(10.0, (
int)precision);
 
 1807       double round_offset = (num < 0) ? -0.5 : 0.5;
 
 1810       reducedNum = ((
long long)(reducedNum * cutoff + round_offset)) / cutoff;
 
 1811       result = reducedNum * factor;
 
 1826   QString PolygonTools::GetGeometryName(
const geos::geom::Geometry *geom) {
 
 1827     switch(geom->getGeometryTypeId()) {
 
 1828       case geos::geom::GEOS_POINT:
 
 1830       case geos::geom::GEOS_LINESTRING:
 
 1831         return "Line String";
 
 1832       case geos::geom::GEOS_LINEARRING:
 
 1833         return "Linear Ring";
 
 1834       case geos::geom::GEOS_POLYGON:
 
 1836       case geos::geom::GEOS_MULTIPOINT:
 
 1837         return "Multi Point";
 
 1838       case geos::geom::GEOS_MULTILINESTRING:
 
 1839         return "Multi Line String";
 
 1840       case geos::geom::GEOS_MULTIPOLYGON:
 
 1841         return "Multi Polygon";
 
 1842       case geos::geom::GEOS_GEOMETRYCOLLECTION:
 
 1843         return "Geometry Collection";
 
 1850   bool PolygonTools::Equal(
const geos::geom::MultiPolygon *poly1, 
 
 1851                            const geos::geom::MultiPolygon *poly2) {
 
 1853     vector<const geos::geom::Polygon *> polys1;
 
 1854     vector<const geos::geom::Polygon *> polys2;
 
 1856     if(poly1->getNumGeometries() != poly2->getNumGeometries())  
return false;
 
 1859     for(
unsigned int geomIndex = 0; geomIndex < poly1->getNumGeometries(); geomIndex ++) {
 
 1860       polys1.push_back(dynamic_cast<const geos::geom::Polygon *>(
 
 1861           poly1->getGeometryN(geomIndex)));
 
 1862       polys2.push_back(dynamic_cast<const geos::geom::Polygon *>(
 
 1863           poly2->getGeometryN(geomIndex)));
 
 1866     for(
int p1 = polys1.size() - 1; (p1 >= 0) && polys1.size(); p1 --) {
 
 1867       for(
int p2 = polys2.size() - 1; (p2 >= 0) && polys2.size(); p2 --) {
 
 1868         if(Equal(polys1[p1], polys2[p2])) {
 
 1870           polys1[p1] = polys1[polys1.size()-1];
 
 1871           polys1.resize(polys1.size() - 1);
 
 1873           polys2[p2] = polys2[polys2.size()-1];
 
 1874           polys2.resize(polys2.size() - 1);
 
 1879     return (polys1.size() == 0) && (polys2.size() == 0);
 
 1883   bool PolygonTools::Equal(
const geos::geom::Polygon *poly1, 
const geos::geom::Polygon *poly2) {
 
 1884     vector<const geos::geom::LineString *> holes1;
 
 1885     vector<const geos::geom::LineString *> holes2;
 
 1887     if(poly1->getNumInteriorRing() != poly2->getNumInteriorRing())  
return false;
 
 1889     if(!Equal(poly1->getExteriorRing(), poly2->getExteriorRing()))  
return false;
 
 1892     for(
unsigned int holeIndex = 0; holeIndex < poly1->getNumInteriorRing(); holeIndex ++) {
 
 1895       if(poly1->getInteriorRingN(holeIndex)->getGeometryTypeId() == geos::geom::GEOS_LINESTRING) {
 
 1896         holes1.push_back(poly1->getInteriorRingN(holeIndex));
 
 1899       if(poly2->getInteriorRingN(holeIndex)->getGeometryTypeId() == geos::geom::GEOS_LINESTRING) {
 
 1900         holes2.push_back(poly2->getInteriorRingN(holeIndex));
 
 1905     if(holes1.size() != holes2.size())  
return false;
 
 1907     for(
int h1 = holes1.size() - 1; (h1 >= 0) && holes1.size(); h1 --) {
 
 1908       for(
int h2 = holes2.size() - 1; (h2 >= 0) && holes2.size(); h2 --) {
 
 1909         if(Equal(holes1[h1], holes2[h2])) {
 
 1911           holes1[h1] = holes1[holes1.size()-1];
 
 1912           holes1.resize(holes1.size() - 1);
 
 1914           holes2[h2] = holes2[holes2.size()-1];
 
 1915           holes2.resize(holes2.size() - 1);
 
 1920     return (holes1.size() == 0) && (holes2.size() == 0);
 
 1924   bool PolygonTools::Equal(
const geos::geom::LineString *lineString1, 
 
 1925                            const geos::geom::LineString *lineString2) {
 
 1927     geos::geom::CoordinateSequence *coords1 = lineString1->getCoordinates();
 
 1928     geos::geom::CoordinateSequence *coords2 = lineString2->getCoordinates();
 
 1929     bool isEqual = 
true;
 
 1931     if(coords1->getSize() != coords2->getSize()) isEqual = 
false;
 
 1933     unsigned int index1 = 0;
 
 1934     unsigned int index2 = 0;
 
 1937     for(; index2 < coords2->getSize() - 1 && isEqual; index2 ++) {
 
 1938       if(Equal(coords1->getAt(index1), coords2->getAt(index2)))  
break;
 
 1941     if(index2 == coords2->getSize() - 1) isEqual = 
false;
 
 1943     for(; index1 < coords1->getSize() - 1 && isEqual; index1 ++, index2 ++) {
 
 1944       if(!Equal(coords1->getAt(index1), coords2->getAt(index2 % (coords2->getSize() - 1)))) {
 
 1955   bool PolygonTools::Equal(
const geos::geom::Coordinate &coord1, 
 
 1956                            const geos::geom::Coordinate &coord2) {
 
 1958     if(!Equal(coord1.x, coord2.x))  
return false;
 
 1959     if(!Equal(coord1.y, coord2.y))  
return false;
 
 1960     if(!Equal(coord1.y, coord2.y))  
return false;
 
 1966   bool PolygonTools::Equal(
const double d1, 
const double d2) {
 
 1967     const double cutoff = 1e15;
 
 1969     if(DecimalPlace(d1) != DecimalPlace(d2)) 
return false;
 
 1971     int decimalPlace = DecimalPlace(d1);
 
 1972     double factor = pow(10.0, (
int)decimalPlace);
 
 1975     double reducedNum = d1 / factor;
 
 1977     double round_offset = (d1 < 0) ? -0.5 : 0.5;
 
 1980     long long num1 = ((
long long)(reducedNum * cutoff + round_offset));
 
 1982     factor = pow(10.0, (
int)decimalPlace);
 
 1985     reducedNum = d2 / factor;
 
 1987     round_offset = (d2 < 0) ? -0.5 : 0.5;
 
 1990     long long num2 = ((
long long)(reducedNum * cutoff + round_offset));
 
 1993     return (num1 == num2);
 
 2011   geos::geom::MultiPolygon *PolygonTools::SplitPolygonOn360(
const geos::geom::Polygon *inPoly) {
 
 2012     bool convertLon = 
false;
 
 2013     bool negAdjust = 
false;
 
 2014     bool newCoords = 
false;  
 
 2015     geos::geom::CoordinateSequence *newLonLatPts = 
new geos::geom::CoordinateArraySequence();
 
 2017     double lonOffset = 0;
 
 2018     geos::geom::CoordinateSequence *inPolyCoords = inPoly->getCoordinates();
 
 2019     double prevLon = inPolyCoords->getAt(0).x;
 
 2020     double prevLat = inPolyCoords->getAt(0).y;
 
 2022     newLonLatPts->add(geos::geom::Coordinate(prevLon, prevLat));
 
 2024     for (
unsigned int i = 1; i < inPolyCoords->getSize(); i++) {
 
 2025       lon = inPolyCoords->getAt(i).x;
 
 2026       lat = inPolyCoords->getAt(i).y;
 
 2028       if (abs(lon - prevLon) > 180 && (prevLat != 90 && prevLat != -90)) {
 
 2036           if ((lon - prevLon) > 0) {
 
 2040           else if ((lon - prevLon) < 0) {
 
 2049       if (newCoords  &&  dist == 0.0) {
 
 2050         double longitude = (lon + lonOffset) - prevLon;
 
 2051         double latitude = lat - prevLat;
 
 2052         dist = std::sqrt((longitude * longitude) + (latitude * latitude));
 
 2056       newLonLatPts->add(geos::geom::Coordinate(lon + lonOffset, lat));
 
 2063     delete inPolyCoords;
 
 2067       geos::geom::Polygon *newPoly = globalFactory.createPolygon
 
 2068                                      (globalFactory.createLinearRing(newLonLatPts), NULL);
 
 2069       geos::geom::MultiPolygon *multi_polygon = PolygonTools::MakeMultiPolygon(newPoly);
 
 2070       delete newLonLatPts;
 
 2071       return multi_polygon;
 
 2076       geos::geom::Polygon *newPoly = globalFactory.createPolygon
 
 2077                                      (globalFactory.createLinearRing(newLonLatPts), NULL);
 
 2079       geos::geom::CoordinateSequence *pts = 
new geos::geom::CoordinateArraySequence();
 
 2080       geos::geom::CoordinateSequence *pts2 = 
new geos::geom::CoordinateArraySequence();
 
 2088         pts->add(geos::geom::Coordinate(0., 90.));
 
 2089         pts->add(geos::geom::Coordinate(-360., 90.));
 
 2090         pts->add(geos::geom::Coordinate(-360., -90.));
 
 2091         pts->add(geos::geom::Coordinate(0., -90.));
 
 2092         for (
double lat = -90.0 + dist; lat < 90.0; lat += dist) {
 
 2093           pts->add(geos::geom::Coordinate(0.0, lat));
 
 2095         pts->add(geos::geom::Coordinate(0., 90.));
 
 2096         pts2->add(geos::geom::Coordinate(0., 90.));
 
 2097         pts2->add(geos::geom::Coordinate(360., 90.));
 
 2098         pts2->add(geos::geom::Coordinate(360., -90.));
 
 2099         pts2->add(geos::geom::Coordinate(0., -90.));
 
 2100         for (
double lat = -90.0 + dist; lat < 90.0; lat += dist) {
 
 2101           pts2->add(geos::geom::Coordinate(0.0, lat));
 
 2103         pts2->add(geos::geom::Coordinate(0., 90.));
 
 2106         pts->add(geos::geom::Coordinate(360., 90.));
 
 2107         pts->add(geos::geom::Coordinate(720., 90.));
 
 2108         pts->add(geos::geom::Coordinate(720., -90.));
 
 2109         pts->add(geos::geom::Coordinate(360., -90.));
 
 2110         for (
double lat = -90.0 + dist; lat < 90.0; lat += dist) {
 
 2111           pts->add(geos::geom::Coordinate(360.0, lat));
 
 2113         pts->add(geos::geom::Coordinate(360., 90.));
 
 2114         pts2->add(geos::geom::Coordinate(360., 90.));
 
 2115         pts2->add(geos::geom::Coordinate(0., 90.));
 
 2116         pts2->add(geos::geom::Coordinate(0., -90.));
 
 2117         pts2->add(geos::geom::Coordinate(360., -90.));
 
 2118         for (
double lat = -90.0 + dist; lat < 90.0; lat += dist) {
 
 2119           pts2->add(geos::geom::Coordinate(360.0, lat));
 
 2121         pts2->add(geos::geom::Coordinate(360., 90.));
 
 2124       geos::geom::Polygon *boundaryPoly = globalFactory.createPolygon
 
 2125                                           (globalFactory.createLinearRing(pts), NULL);
 
 2126       geos::geom::Polygon *boundaryPoly2 = globalFactory.createPolygon
 
 2127                                            (globalFactory.createLinearRing(pts2), NULL);
 
 2133       geos::geom::Geometry *intersection = PolygonTools::Intersect(newPoly, boundaryPoly);
 
 2134       geos::geom::MultiPolygon *convertPoly = PolygonTools::MakeMultiPolygon(intersection);
 
 2135       delete intersection;
 
 2137       intersection = PolygonTools::Intersect(newPoly, boundaryPoly2);
 
 2138       geos::geom::MultiPolygon *convertPoly2 = PolygonTools::MakeMultiPolygon(intersection);
 
 2139       delete intersection;
 
 2146       vector<geos::geom::Geometry *> *finalpolys = 
new vector<geos::geom::Geometry *>;
 
 2147       geos::geom::Geometry *newGeom = NULL;
 
 2149       for (
unsigned int i = 0; i < convertPoly->getNumGeometries(); i++) {
 
 2150         newGeom = (convertPoly->getGeometryN(i))->clone();
 
 2151         pts = convertPoly->getGeometryN(i)->getCoordinates();
 
 2152         geos::geom::CoordinateSequence *newLonLatPts = 
new geos::geom::CoordinateArraySequence();
 
 2155         for (
unsigned int k = 0; k < pts->getSize() ; k++) {
 
 2156           double lon = pts->getAt(k).x;
 
 2157           double lat = pts->getAt(k).y;
 
 2164           newLonLatPts->add(geos::geom::Coordinate(lon, lat), k);
 
 2168         finalpolys->push_back(globalFactory.createPolygon
 
 2169                               (globalFactory.createLinearRing(newLonLatPts), NULL));
 
 2173       for (
unsigned int i = 0; i < convertPoly2->getNumGeometries(); i++) {
 
 2174         newGeom = (convertPoly2->getGeometryN(i))->clone();
 
 2175         finalpolys->push_back(newGeom);
 
 2178       geos::geom::MultiPolygon *multi_polygon = globalFactory.createMultiPolygon(*finalpolys);
 
 2182       delete newLonLatPts;
 
 2185       return multi_polygon;
 
 2187     catch(geos::util::IllegalArgumentException *geosIll) {
 
 2188       std::string msg = 
"Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
 
 2189       msg += 
"geos illegal argument [" + 
IString(geosIll->what()) + 
"]";
 
 2193     catch(geos::util::GEOSException *geosExc) {
 
 2194       std::string msg = 
"Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
 
 2195       msg += 
"geos exception [" + 
IString(geosExc->what()) + 
"]";
 
 2200       std::string msg = 
"Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
 
 2201       msg += 
"isis operation exception [" + 
IString(e.
what()) + 
"]";
 
 2205       std::string msg = 
"Unable to split polygon on longitude boundry (SplitPolygonOn360) due to ";
 
 2206       msg += 
"unknown exception";
 
double Sample() const 
Returns the current line value of the camera model or projection. 
 
Base class for Map TProjections. 
 
double XCoord() const 
This returns the projection X provided SetGround, SetCoordinate, SetUniversalGround, or SetWorld returned with success. 
 
double Line() const 
Returns the current line value of the camera model or projection. 
 
const char * what() const 
Returns a string representation of this exception in its current state. 
 
#define _FILEINFO_
Macro for the filename and line number. 
 
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. 
 
bool SetWorld(const double x, const double y)
This method is used to set a world coordinate. 
 
double Longitude() const 
This returns a longitude with correct longitude direction and domain as specified in the label object...
 
double Latitude() const 
This returns a latitude with correct latitude type as specified in the label object. 
 
Adds specific functionality to C++ strings. 
 
bool SetUniversalGround(double lat, double lon)
Returns whether the lat/lon position was set successfully in the camera model or projection.