public COVERAGE_TYPE polygonGeometry() { /////////////////////////////////////////////////////////////////////// // get the mean polygon location and the polygon area // convert all vertices to UTM // get the UTM zone that will be used // get recangular bounding box // determine the orientation of the point-scatter covariance ellipse /////////////////////////////////////////////////////////////////////// utm = new UTM2Geodetic(); //class for LL to UTM conversion for (int i = 0; i < polygons.Count; i++) { //local copy of the polygon -- re-inserted into the polygon list at the end of this procedure Polygon p = polygons[i]; int numLatLonPoints = p.latitudeDeg.Count; //mean Northing and Easting from mean lat/lon -- also use this to get the UTMZone that will be held fixed for this polygon p.meanNorthing = 0.0; p.meanEasting = 0.0; //NOTE: get the UTMZone here (for the polygon mean location) that will be used for the whole project utm.LLtoUTM(p.meanLatDeg * Deg2Rad, p.meanLonDeg * Deg2Rad, ref p.meanNorthing, ref p.meanEasting, ref p.UTMZone, false); //the UTM zone will be held constant across the polygon points -- deals with polys that cross UTM zones //get the Northing and Easting from the lat/lon poly points double northing = 0.0, easting = 0.0; p.maxNorthing = -9999999.0; p.maxEasting = -999999999.0; p.minNorthing = 999999999.0; p.minEasting = 99999999999.0; p.maxLatDeg = -99999.0; p.maxLonDeg = -9999999.0; p.minLatDeg = 99999.0; p.minLonDeg = 999999.0; double CovXX = 0.0, CovYY = 0.0, CovXY = 0.0; //covariance of the UTM poly points for (int ipnts = 0; ipnts < numLatLonPoints; ipnts++) { utm.LLtoUTM(p.latitudeDeg[ipnts] * Deg2Rad, p.longitudeDeg[ipnts] * Deg2Rad, ref northing, ref easting, ref p.UTMZone, true); p.Northing.Add(northing); p.Easting.Add(easting); if (northing > p.maxNorthing) p.maxNorthing = northing; if (northing < p.minNorthing) p.minNorthing = northing; if (easting > p.maxEasting) p.maxEasting = easting; if (easting < p.minEasting) p.minEasting = easting; if (p.latitudeDeg[ipnts] > p.maxLatDeg) p.maxLatDeg = p.latitudeDeg[ipnts]; if (p.latitudeDeg[ipnts] < p.minLatDeg) p.minLatDeg = p.latitudeDeg[ipnts]; if (p.longitudeDeg[ipnts] > p.maxLonDeg) p.maxLonDeg = p.longitudeDeg[ipnts]; if (p.longitudeDeg[ipnts] < p.minLonDeg) p.minLonDeg = p.longitudeDeg[ipnts]; //covariance elements used to compute the principal axes of the polygon //here we are assuming a north (X), east (Y), down (Z) corrdinatate system CovYY += (easting - p.meanEasting) * (easting - p.meanEasting); CovXX += (northing - p.meanNorthing) * (northing - p.meanNorthing); CovXY += (easting - p.meanEasting) * (northing - p.meanNorthing); } CovYY /= 1000*numLatLonPoints; CovXX /= 1000*numLatLonPoints; CovXY /= 1000*numLatLonPoints; //Compute the orientation of the principal axis of the covariance ellipse //this is used to determine the best flying direction //eigenvalues of covariance matrix -- see below site for equations for eigenvalues/vectors for 2D matrix // people.csail.mit.edu/bkph/articles/Eigenvectors.pdf double det = Math.Sqrt( (CovXX - CovYY)*(CovXX - CovYY) + 4.0 * CovXY * CovXY ); double lambda1 = ( CovXX + CovYY + det )/ 2.0; double lambda2 = ( CovXX + CovYY - det )/ 2.0; //the larger of these is the length of the major axis //smaller is length of the minor axis //the angle associated with the larger eigenvalue is the angle of the major axis //eigenvectors should be 90 deg apart if (lambda1 > lambda2) { //eigenvectors of covariance matrix double eigen1X = 2.0 * CovXY; double eigen1Y = CovYY - CovXX + det; double ang1 = Math.Atan(eigen1Y / eigen1X) * Constants.Rad2Deg; p.rotationAngleDeg = ang1; } else { //eigenvectors of covariance matrix double eigen2X = 2.0 * CovXY; double eigen2Y = CovYY - CovXX - det; double ang2 = Math.Atan(eigen2Y / eigen2X) * Constants.Rad2Deg; p.rotationAngleDeg = ang2; } //reflect a negative major axis direction by 180 deg (major axis can go either direction) if (p.rotationAngleDeg < 0) p.rotationAngleDeg += 180.0; //this must be done after creating the UTM Northing & Easting from the geodetic coordinates polygonMath polyMath = new polygonMath(p.Easting, p.Northing, datasetFolder); //compute rectangle bounds using the principal axes double maxAlongPA = -9999999999, minAlongPA = 9999999999, distanceA = 0.0; double maxOrthoPA = -9999999999, minOrthoPA = 9999999999, distanceO = 0.0; for (int ipnts = 0; ipnts < numLatLonPoints; ipnts++) { //note below that the X components is Northing and the Y components are Easting //The "line" is defined by its origin and rotation angle (positive east of north) //the "point" is the ipnts vertex of the polygon distanceO = polyMath.distanceFromPoint2Line( //distance orthogonal to the pricipal axis new PointD(p.meanNorthing, p.meanEasting), p.rotationAngleDeg, new PointD(p.Northing[ipnts], p.Easting[ipnts])); distanceA = polyMath.distanceFromPoint2Line( //distance orthogonal to the pricipal axis new PointD(p.meanNorthing, p.meanEasting), p.rotationAngleDeg + 90, new PointD(p.Northing[ipnts], p.Easting[ipnts])); if (distanceA > maxAlongPA) maxAlongPA = distanceA; if (distanceA < minAlongPA) minAlongPA = distanceA; if (distanceO > maxOrthoPA) maxOrthoPA = distanceO; if (distanceO < minOrthoPA) minOrthoPA = distanceO; } //length and width of a bounding box with long-side along principal axis //length should always be larger than the width double lengthKm = (maxAlongPA - minAlongPA) / 1000.0; double widthKm = (maxOrthoPA - minOrthoPA) / 1000.0; //Constants.Log("Principal Axis bounding box: " + lengthKm.ToString("F1") + " X " + widthKm.ToString("F1")); //if (lengthKm < widthKm) // MessageBox.Show("Principal axes of polygon not longer than minor axis"); //principal axis direction is defined by p.rotationAngleDeg //set the corner of the principal axes bounding box to be negative-going along the major and minor axis //this corner will also become the origion of the flight line reference coordinate system //this will correspond to a south-east corner for a north-going princpal axis p.boundingBoxCornerNorthing = Math.Cos(p.rotationAngleDeg * Constants.Deg2Rad) * (p.meanNorthing + minAlongPA) + Math.Sin(p.rotationAngleDeg * Constants.Deg2Rad) * (p.meanEasting + minOrthoPA); p.boundingBoxCornerEasting = -Math.Sin(p.rotationAngleDeg * Constants.Deg2Rad) * (p.meanNorthing + minAlongPA) + Math.Cos(p.rotationAngleDeg * Constants.Deg2Rad) * (p.meanEasting + minOrthoPA); //TODO: should just return the polygon points and fill the p structure after the return p.areasqkm = polyMath.areaOfPolygon(); p.areasqmi = p.areasqkm * 1000000.0 / ((0.3048 * 5280.0) * (0.3048 * 5280.0)); Constants.Log(""); if (p.polyCoverageType == COVERAGE_TYPE.polygon) { Constants.Log("Polygon area: " + p.areasqmi.ToString("F3") + " (sqmi) " + p.areasqkm.ToString("F3") + " (sqkm)"); } else if (p.polyCoverageType == COVERAGE_TYPE.linearFeature) { double pathRange = polyMath.pathLength(); Constants.Log("Path length: " + (pathRange/0.3048/5280.0).ToString("F2") + " (mi) " + (pathRange/1000.0).ToString("F2") + " (km)"); } Constants.Log(""); /////////////////////////////////////////////////////////////////////////////////////////////// //write the summary information for this polygon //outfile.WriteLine(); outfile.WriteLine("PolygonName: {0} PolyPoints= {1,4} Area= {2,9:####.00} (kmsq) {3,9:####.00} (sqmi)", p.PolygonName, numLatLonPoints.ToString(), p.areasqkm, p.areasqmi); //foreach (airport apt in apts) // outfile.WriteLine(" {0,50} distance (mi) {1,8:####.00}", apt.name, apt.distanceToPolyCenterMi); ///////////////////////////////////////////////////////////////////////////////////////////////// //we have modified a copy of the polygon object -- re-insert it into the polygon list polygons[i] = p; } //NOTE: here we are assuming that all the coverage types are identical //generally we only use a single coverage for a mission plan session return polygons[0].polyCoverageType; }
public ReflyPlanner(String _reflyKmlPathFilename, Polygon _p) { reflyKmlPathFilename = _reflyKmlPathFilename; p = _p; UTM2Geodetic utm = new UTM2Geodetic(); XmlTextReader textReader = new XmlTextReader(reflyKmlPathFilename); //associate the textReader with input file while (textReader.Read()) { textReader.MoveToElement(); if (textReader.IsStartElement() && textReader.Name == "coordinates") { ///////////////////////////////////////////////////////////////////// //read in and process the coordinates into refly line segments ///////////////////////////////////////////////////////////////////// textReader.Read(); //here we read the complete set of coordinates as a single string //textReader.Value contains a string with all the polygon coordinates char[] delimiterChars = { ',', ' ', '\t', '\n', '\r' }; //these delimiters were determined by looking at a file ... //the "value" is the text between the <coordinates> and </coordinates> //below we read the complete string value and split it into separate substrings -- ugly but it works //the substrings contain the individual coordinate values with some ""s and the heigts are "0". string[] coordinateValues = textReader.Value.ToString().Split(delimiterChars); //get the quantitative lat/long values from the text array ... there are a number of ""s in the text file ... //each Google point has three values : longitude, Latitude, height -- we assume the height is zero here int k = 0; int i = 0; double lat1=0.0, lat2=0.0, lon1=0.0, lon2=0.0; while (i < coordinateValues.Count()) { if (coordinateValues[i] != "") { lat1 = Convert.ToDouble(coordinateValues[i + 1]); lon1 = Convert.ToDouble(coordinateValues[i]); //odd-even numbered (k) points are the endpoints if ( (k+1) % 2 == 0) { //have found a segment pair of lat/lon points //order the start/end points so the start end is to the south endPoints ep = new endPoints(); if (lat1 > lat2) { ep.endLat = lat1; ep.endLon = lon1; ep.startLat = lat2; ep.startLon = lon2; } else { ep.endLat = lat2; ep.endLon = lon2; ep.startLat = lat1; ep.startLon = lon1; } utm.LLtoUTM(ep.startLat * Deg2Rad, ep.startLon * Deg2Rad, ref ep.startUTMY, ref ep.startUTMX, ref p.UTMZone, true); utm.LLtoUTM(ep.endLat * Deg2Rad, ep.endLon * Deg2Rad, ref ep.endUTMY, ref ep.endUTMX, ref p.UTMZone, true); reflySegments.Add(ep); } lat2 = lat1; lon2 = lon1; k++; //index of the storage array //increment the split array by 3 because the points are lat,lon,height i += 3; //increment by 3 to get the next coordinate } else i++; //here we skip the ""s in the text array } } } }