//////////////////////////////////////////////////////////// //all the work is done in the constructor for this class public InspectKMLforMissions(String _kmlFilename, String _datasetFolder, StreamWriter _outfile) { //instantiate the polygons class -- filled herein polygons = new List<Polygon>(); //List of polygons from the kml //class names from input names kmlFilename = _kmlFilename; //input kml file name with complete path datasetFolder = _datasetFolder; //folder containing the input kml file outfile = _outfile; //KMLDebugFile.txt written to the datasetFolder //filename of the input kml file -- may not be the polygon/path name stored inside the kml file String kmlName = Path.GetFileNameWithoutExtension(kmlFilename); XmlTextReader textReader = new XmlTextReader(kmlFilename); //associate the textReader with input file ///////////////////////////////////////////////////////////////////////////////////////// // top of the loop to read all folders and placemarks in the input kml // read to a folder or placemark element in the kml // determine if the "Polygon" tag or "lineString" tag is present // assume each placemark has a set of coordinates // read the lat/lon vertices from the coordinates ///////////////////////////////////////////////////////////////////////////////////////// //these are set to true when we detect a folder or placemark bool folderElementLocated = false; bool placemarkElementLocated = false; String PlacemarkName = ""; String PlacemarkFolderName = ""; //this will become the key exported polygon result from reviewing the input kml polygons = new List<Polygon>(); while (textReader.Read()) ///read xml elements sequentially in a while loop { //generally, there may be no folders -- but multiple placemarks may be inside a folder if (textReader.IsStartElement() && textReader.Name == "Folder") { folderElementLocated = true; } //for a single mission plan there will be only a single placemark -- this is most common //however, we can also allow to plan multiple polygon projects if (textReader.IsStartElement() && textReader.Name == "Placemark") { placemarkElementLocated = true; //found a placemark numberOfPlaceMarksThisKML++; } if (folderElementLocated || placemarkElementLocated) { //each placemark should have a "name" element if (textReader.IsStartElement() && textReader.Name == "name") { textReader.Read(); //read the next element -- should be a name if (folderElementLocated) { PlacemarkFolderName = (String)textReader.Value; outfile.WriteLine(); outfile.WriteLine(PlacemarkFolderName); folderElementLocated = false; } //this where the work occurs --- this placemark name has the coordinates for the client polygons if (placemarkElementLocated) { PlacemarkName = (String)textReader.Value; Constants.Log(" Polygon name: " + PlacemarkName); placemarkElementLocated = false; //at this point we have a placmark and its name //fill this structure and place into the polygon list Polygon p = new Polygon(); //coverage type must be set below from info in the kml file p.polyCoverageType = COVERAGE_TYPE.notSet; //now locate the polygon Coordinates associated with this Placemark. //the below coordinate reader also works with a linear feature //These are contained in a <coordinates> element while (textReader.Read()) { ///////////////////////////////////////////////////////////////////////////////////////// //we cycle through subsequent elements for every detected Placemark tag //below code works for either the polygon coverage or for a linear feature coverage ///////////////////////////////////////////////////////////////////////////////////////// textReader.MoveToElement(); //we will distinguish a "polygon" coverage using the Google Earth tag Polygon. if (textReader.IsStartElement() && textReader.Name == "Polygon") { p.polyCoverageType = COVERAGE_TYPE.polygon; outfile.WriteLine("Found a Placemark and Polygon tag in the unut XML file: assume polygon coverage \n"); Constants.Log("Found a Polygon tag in the input kml file -- assuming polygon coverage"); } if (textReader.IsStartElement() && textReader.Name == "LineString") { //set flag for detection of a linear feature coverage p.polyCoverageType = COVERAGE_TYPE.linearFeature; outfile.WriteLine("Found a Placemark and lineString tag in the input XML file: assume linestring coverage \n"); Constants.Log("Found a lineString tag in the input kml file -- assuming linearFeature coverage"); } if (textReader.Name == "coordinates") //this will be the coordinates of the client poygon { //outfile.WriteLine(" Found <coordinates> tag for the polygon data "); 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 heights are "0". string[] coordinateValues = textReader.Value.ToString().Split(delimiterChars); p.meanLatDeg = 0.0; p.meanLonDeg = 0.0; p.Easting = new List<double>(); p.Northing = new List<double>(); p.latitudeDeg = new List<double>(); p.longitudeDeg = new List<double>(); //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; while (i < coordinateValues.Count()) //loop through the number of coordinates in the poloygon { if (coordinateValues[i] != "") { double lat = 0.0; double lon = 0.0; try { lat = Convert.ToDouble(coordinateValues[i + 1]); lon = Convert.ToDouble(coordinateValues[i]); } catch { Constants.Log("Cant convert the lat or long value to decimal values "); } //Constants.Log( lat.ToString("F5") + " " + lon.ToString("F5") ); p.latitudeDeg.Add(lat); p.longitudeDeg.Add(lon); //String ooo = String.Format("{0,2} {1,13:####.00000000} {2,13:####.00000000}", k.ToString(), latitude[k], longitude[k]); //outfile.WriteLine(ooo); //outfile.Flush(); p.meanLatDeg += lat; p.meanLonDeg += lon; 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 } //completed storing the lat/lon array from the kml file int numLatLonPoints = k; //final number of lat/lon points if (p.polyCoverageType == COVERAGE_TYPE.linearFeature) Constants.Log("Input kml file contains a linearFeature with " + numLatLonPoints.ToString() + " points"); else if(p.polyCoverageType == COVERAGE_TYPE.polygon) Constants.Log("Input kml file contains a polygon with " + numLatLonPoints.ToString() + " points"); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //at this point we have the placemark name and the list of geodetic coordinates for the polygon from this placemark. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// p.meanLatDeg /= numLatLonPoints; //geodetic mean of the polygon p.meanLonDeg /= numLatLonPoints; //////////////////////////////////////////////////////////////////////////////////////////////////////////// //we will allow three levels of naming: job, project, polygon //the original idea was that job = the overall job containing projects and individual polygons in a job //an example would be //job: agricultureFieldsForDate //Project: FarmerJohn, FarmerJoe //polygon: field#1, field#2, etc //However, the current setup is that we have a single polygon in the input kml file ///////////////////////////////////////////////////////////////////////////////////////////////////////////// p.JobName = kmlFilename; p.ProjectName = PlacemarkFolderName; p.PolygonName = PlacemarkName; if (p.polyCoverageType == COVERAGE_TYPE.notSet) { MessageBox.Show("There were no Polygon or LinearPath coverages found in this input kml file -- exiting"); Application.Exit(); } polygons.Add(p); break; } //end of parsing the coordinates element value into individual polygon lat/lon indices }//end of accessing the <coordinates> element of the placemark } // end of processing Placemark }//end of if (textReader.IsStartElement() && textReader.Name == "name") }// end of if (folderElementLocated || placemarkElementLocated) } //end of while (textReader.Read()) //need to provide a messageBox if there were no polygon or lineString tags in this kml file //outfile.WriteLine(" Finished reading the polygon points. Number of points = " /*+ numLatLonPoints.ToString()*/); outfile.Flush(); textReader.Close(); }
public void getGoogleMapToCoverProject(ref Polygon p, bool downloadMap) { double maxLat = p.maxLatDeg; double minLat = p.minLatDeg; double maxLon = p.maxLonDeg; double minLon = p.minLonDeg; if (p.airports[p.selectedAirportIndex].lat > maxLat) maxLat = p.airports[p.selectedAirportIndex].lat; if (p.airports[p.selectedAirportIndex].lat < minLat) minLat = p.airports[p.selectedAirportIndex].lat; if (p.airports[p.selectedAirportIndex].lon > maxLon) maxLon = p.airports[p.selectedAirportIndex].lon; if (p.airports[p.selectedAirportIndex].lon < minLon) minLon = p.airports[p.selectedAirportIndex].lon; /* //get the bounding rectangle for the map without any buffer foreach (airport apt in p.airports) //map covers airports used in the planning { if (apt.lat > maxLat) maxLat = apt.lat; if (apt.lat < minLat) minLat = apt.lat; if (apt.lon > maxLon) maxLon = apt.lon; if (apt.lon < minLon) minLon = apt.lon; } //make sure the map covers all the polygon if (p.maxLatDeg > maxLat) maxLat = p.maxLatDeg; if (p.minLatDeg < minLat) minLat = p.minLatDeg; if (p.maxLonDeg > maxLon) maxLon = p.maxLonDeg; if (p.minLonDeg < minLon) minLon = p.minLonDeg; */ PointD mapCenter = new PointD( (maxLon + minLon)/2.0, (maxLat + minLat)/2.0 ); //set a buffer around the map size that will contain the project polygon and airports plus a border double bufferPercent = 0.05; double latBuffer = bufferPercent * (maxLat - minLat); double lonBuffer = bufferPercent * (maxLon - minLon); // get the google maps zoom level that will encompass this map within a 640X480 pixel size int selectedZoom = 1; //note: the returned image size is reduced as the zooom is increased //so increase til the the image no longer fits -- image size assumed to be 640X480 //640 X 480 is larges image that can be retreived frn Google Maps for FGree. for (int zoom = 5; zoom < 20; zoom++) { setMap(zoom, mapCenter); PointD mapNWCordinates = getNWMapCoordinates(); if (mapNWCordinates.X > (minLon - lonBuffer) || mapNWCordinates.Y < (maxLat + latBuffer)) { //test to see if the desired boundary rect is no longer contained in the Google Maps image //set the desired zoom level to the last good zoom level selectedZoom = zoom - 1; break; } } //this URL returns a valid image //http://maps.googleapis.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=12&size=400x400&sensor=false //bing maps web server example: http://bing.com/maps/default.aspx?cp=43.901683~-69.522416&lvl=12&style=r //build up the URL to retrieve the Google Map -- see the above model for the syntax String GoogleMapsURL = "http://maps.googleapis.com/maps/api/staticmap?center=" + mapCenter.Y.ToString() + "," + mapCenter.X.ToString() + "&zoom=" + selectedZoom.ToString() + "&size=640x480&sensor=false"; //this downloads a map that can be opened --- need to figure out the scaling... //GoogleMapsURL = "http://dev.virtualearth.net/REST/v1/Imagery/Map/Road/Routes?wp.0=Seattle,WA;64;1&wp.1=Redmond,WA;66;2&key=" + // Constants.bingKey; //set the file storage location String backgroundImageFolder = datasetFolder + "\\" + polygonName + "_Background"; if (!Directory.Exists(backgroundImageFolder)) Directory.CreateDirectory(backgroundImageFolder); String SaveToFile = backgroundImageFolder + "\\ProjectMap.png"; if (downloadMap) { //get the file from the web using a webClient getFileFromWeb(GoogleMapsURL, SaveToFile); Image img = Image.FromFile(SaveToFile); //get an image object from the stored file //must convert this image into a non-indexed image in order to draw on it -- saved file PixelFormat is "Format8bppindexed" Bitmap bm = new Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(bm); //create a graphics object g.DrawImage(img, 0, 0); //now draw the original indexed image (from the file) to the non-indexed image } setMap(selectedZoom, mapCenter); p.NWMapCorner = getNWMapCoordinates(); p.SEMapCorner = getSEMapCoordinates(); p.mapName = polygonName + "_Background\\ProjectMap.png"; ////draw the overall polygon onto the map //for (int j = 0; j < p.latitudeDeg.Count; j++ ) //{ // int jp1 = (j+1) % p.latitudeDeg.Count; // Point startPix = FromCoordinatesToLocalMapPixels(new PointD(p.longitudeDeg[j], p.latitudeDeg[j] )); //first convert center to Google Map pixels // Point endPix = FromCoordinatesToLocalMapPixels(new PointD(p.longitudeDeg[jp1], p.latitudeDeg[jp1])); //first convert center to Google Map pixels // //startPix.X = 0; startPix.Y = 0; endPix.X = 640; endPix.Y = 480; // g.DrawLine(new Pen(Color.Red, 3), startPix, endPix); //} //String missionImageFolder = datasetFolder + "\\" + polygonName + "_Summary"; //if (!Directory.Exists(missionImageFolder)) Directory.CreateDirectory(missionImageFolder); //bm.Save(datasetFolder + "\\" + p.mapName); }
public List<airport> airportsNearPolygon(Polygon p) { //////////////////////////////////////////////////////////////////////////////////// //this procedure gets a List of airports close to a client polygon //we break the polygpon rectangular boundary into rectangular chunks 50kmx50km //the Google "Places" API will return airports within a 50km radius of a point //so we call the API using the 50 mi squares .. // this is only approximate and should be rethought !!! /////////////////////////////////////////////////////////////////////////////////// //this will become the returned airports list airports = new List<airport>(); //we will also get the elevation at the airport lat/lon using the Google Elevation API //below is the prefix and postfix where the lat/lon of the request is in the middle String elevationAPIPrefix = "http://maps.googleapis.com/maps/api/elevation/xml?locations="; String elevationAPIPostfix = "&sensor=false"; //get poly NS and EW height/width for the poly bounding rectangle double NSPolyHeightMeters = p.maxNorthing - p.minNorthing; double EWPolyWidthMeters = p.maxEasting - p.minEasting; //find number of 50km boxes to cover the NS height + 100km added to top and bottom int numberNSDivisions = Convert.ToInt32((NSPolyHeightMeters + 100000.0) / 50000.0) + 1; int numberEWDivisions = Convert.ToInt32((EWPolyWidthMeters + 100000.0) / 50000.0) + 1; double aptLat = 0.0, aptLon = 0.0; //start 100km below the S edge but by 25km //we will step in 50km chunks cause the Places API only gives us places within 50km of a point double northing = p.minNorthing - 75000.0; //start at the south edge and move north try { //cycle through all the 50km square regions covering the polygon for (int i = 0; i < numberNSDivisions; i++) { double easting = p.minEasting - 75000.0; //start at the westmost and move east (+) for (int j = 0; j < numberEWDivisions; j++) { List<airport> localAirports = new List<airport>(); //local airports for this 50km square utm.UTMtoLL(northing, easting, p.UTMZone, ref aptLat, ref aptLon); localAirports = airportsWitin50kmRadius(aptLat, aptLon, p.meanLatDeg, p.meanLonDeg); //get local list for 50km square //Console.WriteLine("new code " + i.ToString() + " " + j.ToString() + " " + localAirports.Count.ToString()); //localAirports = airportsWitin50kmRadius(meanLatDeg, meanLonDeg, meanLatDeg, meanLonDeg); easting += 50000.0; // move east by 50km if (localAirports.Count == 0) continue; //if this 50km square has NO airports -- go to the next box to the east //check the new airports from the local list against the complete list --- dont add airports twice for (int iap = 0; iap < localAirports.Count; iap++) //not using a foreach cause we want to add the altitude to the airport struct { bool repeated = false; foreach (airport ap in airports) //cycle through the local airports { //if the airport name in the local list is same as airport name in total list break and label as a repeat if (ap.name == localAirports[iap].name) { //Console.WriteLine("repeated airport: " + ap.name); repeated = true; break; } } if (!repeated) //use this airport in the exported list { //form the Google Elevation API request for the elevation at this airport XmlTextReader tr = new XmlTextReader(elevationAPIPrefix + localAirports[iap].lat + "," + localAirports[iap].lon + elevationAPIPostfix); while (tr.Read()) //read the response xml reply from the elevation API to get the elevation { if (tr.IsStartElement() && tr.Name == "elevation") { tr.Read(); airport temp = localAirports[iap]; //cannot modify localAirports[iap].altitude directly!!!! temp.altitude = Convert.ToDouble(tr.Value); //modify a temp version of the airport struct and then re-insert to the list localAirports[iap] = temp; } } String lcName = localAirports[iap].name.ToLower(); //Console.WriteLine("new non-repeated airport: " + lcName); if ((lcName.Contains("air") || lcName.Contains("field") || lcName.Contains("aero"))) { airports.Add(localAirports[iap]); Console.WriteLine("adding new airport: " + lcName); } else { //Console.WriteLine("rejected non-repeated airport: " + lcName); } } } } northing += 50000.0; //move to the north by 50km } } catch { //int a = 0; } airports.Sort( (apt1, apt2) => string.Compare(apt1.name, apt2.name)); return airports; }
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 } } } }
private COVERAGE_SUMMARY getPolygonFlightPathCoverage(double rotationRad, Polygon p, double swathWidthKm, double maxFLLengthKm, double minLineLength, double DRImageHeightKm, ref int numSets, ref int numFlightLines, ref double[,,] XAllSets, ref double[,,] YAllSets) { ///////////////////////////////////////////////////////////////////////// //this procedure defines a set of flight lines that cover a polygon //The flight lines are returned on the arrays XallSets, YAllSets ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////// //For coverages other than North-South, //rotate the input easting, Northing polygon vertices by angle about the polygon centroid //cover the rotated polygon with a North-South set of flight lines //This will result in a YAllSets[i,k,j], XAllsets[i,k,j] set of flight line endpoints //where i=0,1 to represent the start or end // j= the set number // k= flightline number within the set //now rotate the flight line endpoints by -theta about the polygon centroid. //now YAllSets, XAlSets are rotated vectors //go through the set-based flight lines, tossing zero-length lines //and to produce a 1D (non-set-based) list of flight lines //The flightline-length computation must use the generalized UTM flight line endpoints. ////////////////////////////////////////////////////////////////////////////////////////////////////////// // //rotate the input polygon List<double> originalPolygonEasting = new List<double>(); List<double> originalPolygonNorthing = new List<double>(); for (int i = 0; i < p.Northing.Count; i++) { originalPolygonNorthing.Add(p.Northing[i]); originalPolygonEasting.Add(p.Easting[i]); } //rotate the original coordinates p.maxEasting = -999999999999.0; p.maxNorthing = -9999999999999.0; p.minEasting = 999999999999.0; p.minNorthing = 9999999999999.0; //double rotation = (-p.rotationAngleDeg) * Constants.Deg2Rad; for (int i = 0; i < p.Northing.Count; i++) { double delEasting = originalPolygonEasting[i] - p.meanEasting; double delNorthing = originalPolygonNorthing[i] - p.meanNorthing; p.Easting[i] = p.meanEasting + Math.Cos(rotationRad) * delEasting + Math.Sin(rotationRad) * delNorthing; p.Northing[i] = p.meanNorthing - Math.Sin(rotationRad) * delEasting + Math.Cos(rotationRad) * delNorthing; //recompute the maximums for the rotated vertices if (p.Easting[i] > p.maxEasting) p.maxEasting = p.Easting[i]; if (p.Northing[i] > p.maxNorthing) p.maxNorthing = p.Northing[i]; if (p.Easting[i] < p.minEasting) p.minEasting = p.Easting[i]; if (p.Northing[i] < p.minNorthing) p.minNorthing = p.Northing[i]; } //compute the north-to-south distance wherein we will space the flightlines double NSDstanceKm = (p.maxNorthing - p.minNorthing) / 1000.0; double EWDstanceKm = (p.maxEasting - p.minEasting) / 1000.0; double NSDstanceMi = (p.maxNorthing - p.minNorthing) / 0.3048 / 5280.0; double EWDstanceMi = (p.maxEasting - p.minEasting) / 0.3048 / 5280.0; //must also recompute the maxNorthing and maxEasting for the rotated vertices //int numFlightLines = 0, numSets = 0; double maxFLLengthMet = 0.0; double E2WKm = 0.0; numFlightLines = Convert.ToInt32(Math.Floor(EWDstanceKm / swathWidthKm)); //flight lines should always be inside the polygon //input flight line length is reduced to cause an integer number of equal-length flight lines to span the NS extent numSets = Convert.ToInt32(Math.Floor(NSDstanceKm / maxFLLengthKm)) + 1; maxFLLengthKm = NSDstanceKm / numSets; //make maxFLLength equal-to or smaller-than max so each set has constant NS length double maxFLLengthMi = maxFLLengthKm * 1000.0 / 0.3048 / 5280.0; maxFLLengthMet = maxFLLengthKm * 1000.0; E2WKm = swathWidthKm * numFlightLines; //redefine the E2W coverage distance so that flightline spacing fixed by camera FOV and sidelap //this is the final UTM set of flight line endpointd //XAllSets[k,i,j], YAllSets[k,i,j] whete k=0,1 (start,end of flight line), i=Set, j=FlightlineInSet //X is the Easting and Y is the Northing //the Start is always below (to the south) from the end for a flight line //double[, ,] XAllSets; //double[, ,] YAllSets; //set up for the polygon math polygonMath polyMath = new polygonMath(p.Easting, p.Northing, datasetFolder); //these arrays store the endpoints of the flight lines not-terminated by max line length constraint double[,] XintAllFL = new double[2, numFlightLines + 1]; //beginning and end of each flight line double[,] YintAllFL = new double[2, numFlightLines + 1]; double EastingStart = 0, EastingEnd = 0, NorthingStart = 0, NorthingEnd = 0; //set the first NS flight line on the eastmost side -- no more than 1/2 swathwidth inside the polygon //because the flight lines are N-S, the eastings for the endpoints will be the same //NOTE: EWDstanceKm is the max extent of the polygon rectangular boundary //E2WKm will be <= EWDstanceKm EastingStart = p.minEasting + 1000.0 * (EWDstanceKm - E2WKm) / 2.0; //fight lines will always be inside the polygon EastingEnd = EastingStart; // all NS flight lines have the same EW coordinates (they are exactly NS) //initialize the initial flight line ends as the max of the polygon rectangular envelope NorthingStart = p.maxNorthing; NorthingEnd = p.minNorthing; outfile.WriteLine(); outfile.WriteLine(" Precompute the flight line lengths without maxLength consideration"); ///////////////////////////////////////////////////////////////////////////////////////////////////// //without the below statement, the last flight line will be too far inside the right max extent //something fishy about the above integer math!!!!! ///////////////////////////////////////////////////////////////////////////////////////////////////// numFlightLines++; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //pre compute all the flightline intersections with the polygon without consideration of Sets that will act to reduce the flight line length //this initial step computes the flightlines as though they can have infinite length // Sets: vertically divide the Project polygon into equal-distance regions where we will look for non-zero flight lines ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// double maxUnterminatedLineLength = -999.0; for (int i = 0; i < numFlightLines; i++) { //flight line intersections wthout consideration of line length double[] Xint = new double[2]; double[] Yint = new double[2]; //compute the north-most and south-most flightline ends by the intersection of the flightline with the polygon //here we only return two intersections -- internal to this procedure, we compute the most-north and most-south of all intersections //note the polygons may contain concave areas that would cause more than two intersections int intersectionCounter = polyMath.intersectionOfLineWithPolygon(EastingStart, NorthingStart, EastingEnd, NorthingEnd, ref Xint, ref Yint); XintAllFL[0, i] = Xint[0]; //west-most line intersection with poly for Flightline i XintAllFL[1, i] = Xint[1]; //east-most line intersection with poly YintAllFL[0, i] = Yint[0]; //south-most line intersection with poly YintAllFL[1, i] = Yint[1]; //north-most line intersection with poly double lineLength = 0.0; lineLength = (YintAllFL[1, i] - YintAllFL[0, i]); outfile.WriteLine(String.Format("line number: {0,2} non-terminated linelength = {1,12:#.00} (km) {2,12:#.00} (Mi)", i, lineLength / 1000.0, lineLength / 0.3048 / 5280.0)); outfile.Flush(); // if (lineLength > maxUnterminatedLineLength) maxUnterminatedLineLength = lineLength; //increment the NS flight line by the swath width -- assume North-South flightline that moves to the east EastingStart += (swathWidthKm * 1000.0); EastingEnd = EastingStart; } ///////////////////////////////////////////////////////////////////////////////////////////////////// //completed locating the coverage flight lines without consideration of maximum flight line length ///////////////////////////////////////////////////////////////////////////////////////////////////// outfile.WriteLine(); outfile.WriteLine(); //input flight line length is reduced to cause an integer number of equal-length flight lines to span the NS extent outfile.WriteLine(" Break up the non-terminated lines using the adjusted flightline maxLength: " + maxFLLengthMi.ToString("F2") + " (mi)"); outfile.WriteLine(" Number of Sets: " + numSets.ToString()); outfile.WriteLine(); outfile.WriteLine(); outfile.Flush(); //these will hold the flight line endpoints that are broken into sets to reduce flight line length XAllSets = new double[2, numSets, numFlightLines + 1]; YAllSets = new double[2, numSets, numFlightLines + 1]; ///////////////////////////////////////////////////////////// //takes care of the case when there is only a single set ///////////////////////////////////////////////////////////// //handle the case where the non-terminated line length is less than the client-requested max line length //if (maxUnterminatedLineLength < maxFLLengthMet) numSets = 1; for (int i = 0; i < numSets; i++) for (int j = 0; j < numFlightLines; j++) for (int k = 0; k < 2; k++) { XAllSets[k, i, j] = XintAllFL[k, j]; YAllSets[k, i, j] = YintAllFL[k, j]; } FLSum = new FlightlineSummary[numSets * numFlightLines + 1]; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //now break up the non-terminated (too long) NS flight lines into the subsets defined by the user-set max flight line length // the subset regions will have equal vertical (NS) height if lines end at a set boundary //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //cycle through the number of sets as determined by the max flight line length for (int i = 0; i < numSets; i++) //numsets breaks the too-long flight lines to be less than the max flight line { outfile.WriteLine(); outfile.WriteLine(" Set: " + i.ToString()); outfile.WriteLine(); //define flight line candidate endpoints based on only the set start points //these are adjusted based on line-end intersections with the client polygon double startSetY = 0, endSetY = 0, startSetX = 0, endSetX = 0; //define the vertical locations of the Set endpoints -- starts at the north-most set and moves south startSetY = p.maxNorthing - i * maxFLLengthKm * 1000.0; //test subset flight line Ystart at the Set start endSetY = startSetY - maxFLLengthKm * 1000.0; //test subset flight line Yend at the Set end //cycle through the number of flight lines spaced across the plan pattern for each along-line set for (int j = 0; j < numFlightLines; j++) { //start & end X (easting) values are equal and increment to the east with flight line counter startSetX = p.minEasting + 1000.0 * (EWDstanceKm - E2WKm) / 2.0 + j * swathWidthKm * 1000.0; //EW coordinates of NS flightlines are identical endSetX = startSetX; //at this point we have a set of flight lines defined by the along-line set and across-line FL spacing //based solely on a rectangular region defined by the max/min coverage boundaries //we need to terminate these lines based on their intersections with the client polygon //the below arrays are the intersections of the flight line with the client polygon double[] Xint = new double[2]; double[] Yint = new double[2]; //compute the number of intersections of the per-set line segment with the client polygon //this can be zero, 1, 2, .... int numIntersects = polyMath.intersectionOfLineWithPolygon(startSetX, startSetY, endSetX, endSetY, ref Xint, ref Yint); //test to see if both ends of the test flight line are outside the total Project polygon external hull -- if so skip remainder of this loop -- //NOTE: endSetY will always be to the North of startSetY -- End and Start have a geographic order //endsetY is south of startSetY -- YintAllFL[1, j] is south of YintAllFL[0, j] //YintAllFL[0, i] south-most line intersection with poly //YintAllFL[1, i] north-most line intersection with poly if (endSetY > YintAllFL[1, j] || startSetY < YintAllFL[0, j]) { outfile.WriteLine("both ends of FL= " + j.ToString() + " outside set " + i.ToString()); //force this line length for this set to zero -- else it will be counted again on another set YAllSets[1, i, j] = YintAllFL[0, j]; YAllSets[0, i, j] = YintAllFL[0, j]; continue; } //if we get here -- at least one line end is inside the current set boundary extent // numIntersects is the number of intersections of the set-terminated flight line with the client polygon //there can be zero intersects even with both ends outside or inside the polygon if (numIntersects == 0) { //test to see if one end is inside the polygon -- since there are zero intersects, this is the case where both ends are inside the polygon if (polyMath.pointInsidePolygon(p.Easting.Count, endSetX, endSetY, p.Easting, p.Northing)) //both endpoints are inside the polygon { // set the desired flight line ends to fall on the set boundaries YAllSets[1, i, j] = startSetY; YAllSets[0, i, j] = endSetY; outfile.WriteLine(" both line ends are inside the client poly "); } //the below "continue" is redundant with the first test above -- should never get here //there are zero set-line intersections and neither end is inside the client poly else { outfile.WriteLine("set line has at least one intersections with client poly -- but neither end is inside the poly"); continue; //this is the case where the poly is concave so that the complete terminated line is outside the polygon } } else if (numIntersects % 2 == 1) //one of the set-boundary-terminated flight lines is outside the client polygon { outfile.WriteLine(" one line end is inside the client poly "); //test to see if the endpoint is inside the client polygon //if so, then this end is on the desired flight line //the other end is at the set boundary if (polyMath.pointInsidePolygon(p.Easting.Count, endSetX, endSetY, p.Easting, p.Northing)) { YAllSets[1, i, j] = Yint[1]; YAllSets[0, i, j] = endSetY; } else //the startpoint is inside the client polygon { YAllSets[1, i, j] = startSetY; YAllSets[0, i, j] = Yint[0]; } } //here we have an even number of intersctions else //either both of the unterminated lines are outside the polygon or there is a concave polygon section // must also consider a jut-out where both ends are outside --- { if (!polyMath.pointInsidePolygon(p.Easting.Count, endSetX, endSetY, p.Easting, p.Northing)) { outfile.WriteLine("even number of intersections -- both ends are outside the client poly "); //both ends must be outside the polygon YAllSets[1, i, j] = Yint[1]; YAllSets[0, i, j] = Yint[0]; } else //both ends must be inside the polygon { outfile.WriteLine("even number of intersections -- both ends are inside the client poly "); YAllSets[1, i, j] = startSetY; YAllSets[0, i, j] = endSetY; } } double lineLengthKm = 0, lineLengthMi = 0; //this becomes a list of the flight lines on a Set grouping that cover the polygon //need to keep the order of the line within the set ... e.g., track those on the ends XAllSets[0, i, j] = startSetX; //start of the line (this is to the South for a NS flightline) XAllSets[1, i, j] = endSetX; //end of the line lineLengthKm = (YAllSets[1, i, j] - YAllSets[0, i, j]) / 1000.0; lineLengthMi = (YAllSets[1, i, j] - YAllSets[0, i, j]) / 0.3048 / 5280.0; outfile.WriteLine(String.Format("lines terminated at Set boundaries: set= {0,2} FL= {1,2} length: {2,10:#.00} Km {3,10:#.00} Mi intersects = {4}", i, j, lineLengthKm, lineLengthMi, numIntersects)); outfile.Flush(); //below we do the following: //if a line is too short, add the segment to either the next or last extension of the line //if the short line is at the bottom of this Set, add to the top of the next set //if the short line is at the top of this Set, add to the bottom of the last set } } outfile.WriteLine(); outfile.WriteLine(); outfile.WriteLine("Extend the flight lines slightly to account for the descretization resulting from the swath width"); /////////////////////////////////////////////////////////////////////////////////////////// //we have a set of flight lines that cover the polygon spaced per the swath width //However, if we use these flight lines, we will end up with gaps at the polygon edges //due to the discretization of the flight lines. A short flight line prior to a //longer flight line must be extended so that its edge covers the polygon. //this should really be done by locating the polygon intersections at the halfway points between //each of the flightline. We use the 0.75 below to account for this approximation. //if the polygon edges were linear between the endpoints, we would use 0.5 //if the edge bewteen the endpoint has a vertices with convex shape it should be bigger than 0.5 ////////////////////////////////////////////////////////////////////////////////////////// //save the old endpoints double[, ,] YAllSetsTemp = new double[2, numSets, numFlightLines]; for (int i = 0; i < numSets; i++) for (int j = 0; j < numFlightLines; j++) for (int k = 0; k < 2; k++) YAllSetsTemp[k, i, j] = YAllSets[k, i, j]; double[, ,] XAllSetsTemp = new double[2, numSets, numFlightLines]; for (int i = 0; i < numSets; i++) for (int j = 0; j < numFlightLines; j++) for (int k = 0; k < 2; k++) XAllSetsTemp[k, i, j] = XAllSets[k, i, j]; for (int i = 0; i < numSets; i++) { for (int j = 0; j < numFlightLines; j++) { double lineLengthKm = lineLengthKm = (YAllSets[1, i, j] - YAllSets[0, i, j]) / 1000.0; //discard zero-length lines if (lineLengthKm == 0.0) { outfile.WriteLine(" discard set {0} FL {1} due to zero length", i, j); continue; } else { outfile.WriteLine(" keep set {0,2} FL {1,2} length = {2,8:#.00} (km) {3,8:#.00} (mi) ", i, j, lineLengthKm, lineLengthKm * 1000.0 / 0.3048 / 5280.0); } if (j < (numFlightLines - 2)) { double lineLengthKm2 = 0; lineLengthKm2 = (YAllSetsTemp[1, i, j + 1] - YAllSetsTemp[0, i, j + 1]) / 1000.0; if (lineLengthKm2 == 0.0) continue; //test to see if the next flight line end is above the current flight line end //if so, then extend this flight line halfway to the next line end if (YAllSetsTemp[1, i, j] < YAllSetsTemp[1, i, j + 1]) YAllSets[1, i, j] += 0.75 * (YAllSetsTemp[1, i, j + 1] - YAllSets[1, i, j]); //test to see if the next flight line start is below the current flight line start //if so, then extend this flight line start point halfway to the next line startpoint if (YAllSetsTemp[0, i, j] > YAllSetsTemp[0, i, j + 1]) YAllSets[0, i, j] -= 0.75 * (YAllSetsTemp[0, i, j] - YAllSetsTemp[0, i, j + 1]); } if (j > 0) { double lineLengthKm2 = lineLengthKm2 = (YAllSetsTemp[1, i, j - 1] - YAllSetsTemp[0, i, j - 1]) / 1000.0; if (lineLengthKm2 == 0.0) continue; //test to see if the prior flight line end is above the current flight line end //if so, then extend this flight line end point halfway to the prior line end point if (YAllSetsTemp[1, i, j] < YAllSetsTemp[1, i, j - 1]) YAllSets[1, i, j] += 0.75 * (YAllSetsTemp[1, i, j - 1] - YAllSetsTemp[1, i, j]); //test to see if the prior flight line start is below the current flight line start //if so, then extend this flight line start point halfway to the prior line start point if (YAllSetsTemp[0, i, j] > YAllSetsTemp[0, i, j - 1]) YAllSets[0, i, j] -= 0.75 * (YAllSetsTemp[0, i, j] - YAllSetsTemp[0, i, j - 1]); } } } //output a summary of the above computations for (int i = 0; i < numSets; i++) { outfile.WriteLine("Set = " + i.ToString("D2")); for (int j = 0; j < numFlightLines; j++) { double newlineLengthKm = (YAllSets[1, i, j] - YAllSets[0, i, j]) / 1000.0; double oldlineLengthKm = (YAllSetsTemp[1, i, j] - YAllSetsTemp[0, i, j]) / 1000.0; outfile.WriteLine("FL = {0,2} oldLength = {1,8:#.00} (km) newLength = {2,8:#.00} ", j, oldlineLengthKm, newlineLengthKm); } } outfile.WriteLine(); outfile.WriteLine(); outfile.WriteLine("Go back through the terminated flight lines and add too short lines to prior set "); outfile.Flush(); ////////////////////////////////////////////////////////////////////////////////////////////////////////////// //go back through the flight line segments, ordered by set & flightline, and cull the "zero-length" segments //also add the too-short segments to the adjoining (to the South) Set //Caveat: adding the too-short segments to the South Set can result in some odd outlier flightline groupings!!! //Resolution: at the start of a set: once we "keep" a segment, dont adjoin anymore short lines to the south // but at the end of a Set, once we adjoin a segment, must also adjoin the remainder // thus we have to look for short flightlines going forward -- terminating after we find a sufficiently long line // and then go backward, again stopping after we have found a sufficiently long line ////////////////////////////////////////////////////////////////////////////////////////////////////////////// int numTooShortLines = 0; for (int i = 0; i < numSets - 2; i++) //TEMPORARY ??? { //define the vertical locations of the Set endpoints double startSetY = p.maxNorthing - i * maxFLLengthKm * 1000.0; //test subset flight line Ystart at the Set start double endSetY = startSetY - maxFLLengthKm * 1000.0; //test subset flight line Yend at the Set end int jStart = 0, jEnd = numFlightLines, jDelta = 1; //conditions that cause going forward for each set for (int k = 0; k < 2; k++) //causes going backward and forward through the flightlines in a Set -- see above Caveat. { if (k == 1) //going backward -- lines go from largest to smallest { jStart = numFlightLines - 1; jEnd = 0; jDelta = -1; } //conditions that cause going backward //multiplying by "-1" on the back pass flips the equality!! -- so start at the end going down to "0" (j>-1) for (int j = jStart; (jDelta * j) < (jDelta * jEnd - 1); j += jDelta) { //test to see if both ends of the test flight line are outside the polygon -- if so skip remainder of this loop -- if (endSetY > YintAllFL[1, j] || startSetY < YintAllFL[0, j]) continue; double lineLengthKm = (YAllSets[1, i, j] - YAllSets[0, i, j]) / 1000.0; double lineLengthMi = (YAllSets[1, i, j] - YAllSets[0, i, j]) / 0.3048 / 5280.0; outfile.WriteLine(String.Format("set {0} FL {1} linelength = {2,15:#.00} Km {3,15:#.00} Mi", i, j, lineLengthKm, lineLengthMi)); outfile.Flush(); if (lineLengthKm == 0) continue; //if zero length -- continue //if a line is too short, add the segment to either the next or last extension of the line if (lineLengthKm < minLineLength) { numTooShortLines++; //if the short line is at the bottom of this Set, add to the top of the next set if (YAllSets[0, i, j] == endSetY && i < (numSets - 1)) //bottom of this short line is at the bottom of the Set { outfile.WriteLine(String.Format("remove and add to top of next set:: {0} {1} {2,15:#.00} Km", i, j, lineLengthKm)); outfile.Flush(); YAllSets[1, i + 1, j] += lineLengthKm * 1000.0; //bottom this segment added to top of next segment YAllSets[0, i, j] = 0; //set old short segment to zero YAllSets[1, i, j] = 0; //set old short segment to zero } //if the short line is at the top of this Set, add to the bottom of the last set else if (YAllSets[1, i, j] == startSetY && i > 0) ////top of this short line is at the top of the Set { outfile.WriteLine(String.Format("remove and add to bottom of last set:: {0} {1} {2,15:#.00} Km", i, j, lineLengthKm)); outfile.Flush(); YAllSets[0, i - 1, j] -= lineLengthKm * 1000.0; //top this segment added to bottom of last segment YAllSets[0, i, j] = 0; //set old short segment to zero YAllSets[1, i, j] = 0; //set old short segment to zero } else { //if both ends of the short line are on the polygon, then do nothing. if (k == 0) outfile.WriteLine(String.Format("forward Pass: short line contained within the polygon:: {0} {1} {2,15:#.00} Km", i, j, lineLengthKm)); if (k == 1) outfile.WriteLine(String.Format("backward Pass: short line contained within the polygon:: {0} {1} {2,15:#.00} Km", i, j, lineLengthKm)); outfile.Flush(); } } //below break causes the short flight lies to be merged with the Southern set either at the start or end of a Set else break; //this breaks after we have found the first occurrence of a sufficient long fliight line } } //end of the k-look --- k=0 is forward and k=1 is backward } outfile.WriteLine("Number of moved too-short flight lines = {0,2}", numTooShortLines); ///////////////////////////////////////////////////////////////////////////////////////// //adjust the photocenters so that they fall on a rectangular grid //grid origin is always the NW corner of the bounding rectangle //note that the rotated flight lines are still North-South //make adjustments to the UTM north and south flight line endpoints /////////////////////////////////////////////////////////////////////////////// outfile.WriteLine(); outfile.WriteLine("Adjust the flight line endoints so they fall on a rectangular grid"); COVERAGE_SUMMARY covSummary = new COVERAGE_SUMMARY(); covSummary.numTotalFlightLines = 0; covSummary.totalFlightLineLength = 0.0; for (int i = 0; i < numSets; i++) { outfile.WriteLine("Set= {0,2} ", i); for (int j = 0; j < numFlightLines; j++) { double distanceBetweenTriggersMeters = DRImageHeightKm * 1000.0; double originalFLlength = YAllSets[1, i, j] - YAllSets[0, i, j]; //make FL start fall on a grid with downrange spacing of distanceBetweenTriggers //the start end is always below the end (to the south for a NS flightline) //compute the number of grid cells from the origin -- the "+1" ensures it covers (is above) the non-gridded flight line int numGridsFromMaxNorthingS = (int)((p.maxNorthing - YAllSets[0, i, j]) / distanceBetweenTriggersMeters); //the "+1" below always ensures the quantized start points are outside (below) the polygon. //this will result in a single frame duplication between sets. YAllSets[0, i, j] = p.maxNorthing - (numGridsFromMaxNorthingS + 1) * distanceBetweenTriggersMeters; //make FL end latitude fall on a grid with downrange spacing of distanceBetweenTriggers //compute the number of grid cells from the origin -- the "-1" ensures it covers the non-gridded flight line int numGridsFromMaxNorthingE = (int)((p.maxNorthing - YAllSets[1, i, j]) / distanceBetweenTriggersMeters) + 1; //the "-1" below always ensures the quantized endponts points are outside (above) the polygon (note the double negative. //this will result in a songle frame duplication between sets. YAllSets[1, i, j] = p.maxNorthing - (numGridsFromMaxNorthingE - 1) * distanceBetweenTriggersMeters; double finalFLlength = YAllSets[1, i, j] - YAllSets[0, i, j]; outfile.WriteLine("FL = {0,2} original length = {1,8:#.00} (km) adjusted length = {2,8:#.00} ", j, originalFLlength, finalFLlength); if (finalFLlength > 0) { covSummary.numTotalFlightLines++; covSummary.totalFlightLineLength += finalFLlength; } } } covSummary.totalFlightLineLength /= 1000.0; covSummary.averageFlightLineLength = covSummary.totalFlightLineLength / covSummary.numTotalFlightLines; ///////////////////////////////////////////////////////////////////////////////////////////////////////////// //all the above was performed assuming the input polygon was rotated to a North-South orientation //NS flightlines were computer to cover the polygon //we must now counter-rotate the resulting flight lines back to their original geometry //this approach preseves the flightline relationship with the original polygon //The resulting flight lines are now arbitrarily rotated relative to north ///////////////////////////////////////////////////////////////////////////////////////////////////////////// //retrieve the original non-rotated UTM input coordinates for (int i = 0; i < p.Northing.Count; i++) { p.Easting[i] = originalPolygonEasting[i]; p.Northing[i] = originalPolygonNorthing[i]; } //rotate the flight line endpoints back to the original non-rotated coordinate frame //X corresponds to Eastng, Y to Northing -- //note that in the initial rotation, the variable "rotation" was set to p.rotationDeg //below we rotation with -rotation to get back to the original coordinates for (int i = 0; i < numSets; i++) { for (int j = 0; j < numFlightLines; j++) { for (int k = 0; k < 2; k++) { double delEasting = XAllSets[k, i, j] - p.meanEasting; double delNorthing = YAllSets[k, i, j] - p.meanNorthing; XAllSets[k, i, j] = p.meanEasting + Math.Cos(-rotationRad) * delEasting + Math.Sin(-rotationRad) * delNorthing; YAllSets[k, i, j] = p.meanNorthing - Math.Sin(-rotationRad) * delEasting + Math.Cos(-rotationRad) * delNorthing; XAllSets[k, i, j] = p.meanEasting + Math.Cos(-rotationRad) * delEasting + Math.Sin(-rotationRad) * delNorthing; YAllSets[k, i, j] = p.meanNorthing - Math.Sin(-rotationRad) * delEasting + Math.Cos(-rotationRad) * delNorthing; } } } //return sufficient information to compare various coverage options return covSummary; }
//individual mission (collection of flight lines) parameters private void writeKmlPolygonMissonPlan(Polygon p, double DRImageHeightKm, //the downrange distance between camera triggers to achieve overlap int missionNumber, //Total number of missions for this project double flightAlt, //flight altitude agll to achieve the requested resoltion double swathWidthKm, //swath width constant over the project double totalFlightlineDistance, //total flight line distance over the project double totalFerryTime, //total ferry time for the project double aircraftSpeedKPH, //average airspeed double totalTimeOverTarget, //total time over the target int numFL, //total number of flight lines for the project int totalImages, //total images for the project List<MissionSummary> MissionSumA) { /////////////////////////////kml header and re-write the polygon points///////////////////////////////////////// // PolygonName is the Placemark name from the input kml file writekmlHeader("Polygon"); kmlFlightLines.WriteLine(); //write the placemark for the original polygon kmlFlightLines.WriteLine("<Placemark> <name>" + p.PolygonName + "</name> <styleUrl>#redLine</styleUrl> <LineString> <tessellate>1</tessellate>"); kmlFlightLines.WriteLine("<coordinates>"); for (int i = 0; i < p.longitudeDeg.Count; i++) //write out the original polygon from the initial input .kml kmlFlightLines.WriteLine(String.Format("{0:#.000000000},{1:#.000000000},{2,1}", p.longitudeDeg[i], p.latitudeDeg[i], 0)); kmlFlightLines.WriteLine("</coordinates> </LineString> </Placemark>"); kmlFlightLines.WriteLine(); //show the summary image for this Project that was extracted from Google Maps kmlFlightLines.WriteLine("<GroundOverlay> <name>Project Map</name> <visibility>0</visibility> <drawOrder> 10 </drawOrder> <LatLonAltBox>"); kmlFlightLines.WriteLine("<north>{0}</north> <south>{1}</south>", p.NWMapCorner.Y, p.SEMapCorner.Y); kmlFlightLines.WriteLine("<east>{0} </east> <west>{1} </west>", p.SEMapCorner.X, p.NWMapCorner.X); kmlFlightLines.WriteLine("</LatLonAltBox> <Lod> <minLodPixels>128</minLodPixels>"); kmlFlightLines.WriteLine("<maxLodPixels>-1</maxLodPixels> </Lod> <Icon> <href>{0}</href> </Icon>", p.mapName); kmlFlightLines.WriteLine(" </GroundOverlay>"); /////////////////////////////end of the initial kml material/////////////////////////////////////////////////////// //below we first write the top folder that will contain the missions //we also summarize the complete coverage of the Client polygon here kmlFlightLines.WriteLine("<Folder> <name> {0}MissionPlan </name>", p.PolygonName); //PlaceMarkName is from the original input kml kmlFlightLines.WriteLine("<downRangePhotoSpacingMeters> {0} </downRangePhotoSpacingMeters>", DRImageHeightKm * 1000.0); //photoSpacing in meters kmlFlightLines.WriteLine("<crossRangeSwathWidth> {0} </crossRangeSwathWidth>", swathWidthKm * 1000.0); //photoSpacing in meters kmlFlightLines.WriteLine("<numberOfMissons> {0} </numberOfMissons>", missionNumber); //total number of missions kmlFlightLines.WriteLine("<UTMZone>{0}</UTMZone>", p.UTMZone); //UTM zone kmlFlightLines.WriteLine("<gridOriginUTMNorthing> {0} </gridOriginUTMNorthing>", p.maxNorthing); //grid origin NW corner: maximum northing kmlFlightLines.WriteLine("<gridOriginUTMEasting> {0} </gridOriginUTMEasting>", p.minEasting); //grid origin NW corner: minEasting ////////////////////////////////START OF SUMMARY TABLE///////////////////////////////////////////////////////////// kmlFlightLines.WriteLine("<description> <![CDATA["); //open of the html segment kmlFlightLines.WriteLine(String.Format("Area of Polygon: {0:#.0} sqKm ({1:#.0} sqMi) <br>", p.areasqkm, p.areasqmi)); kmlFlightLines.WriteLine(String.Format("Flight Altitude: {0:#} ft (AGL) <br>", flightAlt)); kmlFlightLines.WriteLine(String.Format("Ground Sample Distance: {0:#.0}cm ({1:#.0}in) <br>", resolutionCm, resolutionCm / 100.0 / 0.3048 * 12.0)); kmlFlightLines.WriteLine(String.Format("Crossrange Swath: {0:0.00}km ({1:0.00}mi) <br>", swathWidthKm, swathWidthKm * 1000.0 / 0.3048 / 5280.0)); kmlFlightLines.WriteLine(String.Format("Total # Missions: {0} <br>", missionNumber)); kmlFlightLines.WriteLine("______________________________________________<br>"); kmlFlightLines.WriteLine(String.Format("Total Distance over target {0:#.0}km ({1:#.0}mi) <br>", totalFlightlineDistance, totalFlightlineDistance * 1000.0 / 0.3048 / 5280.0)); //TODO: this isnt right!! speed isnt constant due to the forward speed during climb kmlFlightLines.WriteLine(String.Format("Total Flight Distance with Ferry {0:#.0}km ({1:#.0}mi) <br>", totalFlightlineDistance + totalFerryTime * aircraftSpeedKPH, (totalFlightlineDistance + totalFerryTime * aircraftSpeedKPH) * 1000.0 / 0.3048 / 5280.0)); //TODO: same comment as above kmlFlightLines.WriteLine(String.Format("Average 2-way Ferry Distance {0:#.0}km ({1:#.0}mi) <br>", totalFerryTime * aircraftSpeedKPH / missionNumber, (totalFerryTime * aircraftSpeedKPH / missionNumber) * 1000.0 / 0.3048 / 5280.0)); kmlFlightLines.WriteLine(String.Format("Total Flight Time with Ferry (hr): {0:0.0} <br>", totalTimeOverTarget + totalFerryTime)); kmlFlightLines.WriteLine(String.Format("Total Number of Flightlines: {0} <br>", numFL)); kmlFlightLines.WriteLine(String.Format("Average Flightline Length: {0:0.0}km ({1:0.0}mi) <br>", totalFlightlineDistance / numFL, (totalFlightlineDistance / numFL) * 1000.0 / 0.3048 / 5280.0)); kmlFlightLines.WriteLine(String.Format("Total Images: {0} <br>", totalImages)); kmlFlightLines.WriteLine("______________________________________________<br> <br>"); kmlFlightLines.WriteLine("<b> <center>Flight Line Summary </center></b> <br>"); kmlFlightLines.WriteLine(@"<table border=""1"" padding=""2"">"); kmlFlightLines.WriteLine("<tr> <td><center> Mission </center> </td> <td><center> Total Time (hr) </center> </td> <td><center> Time at Site (hr) </center> </td> <td><center> images </center> </td><td><center> alt (ft MSL) </center> </td></tr>"); //form an html table to show the summary of each mission foreach (MissionSummary msn in MissionSumA) { String frmt = "<tr>"; frmt += "<td><center> {0} </center></td> <td><center> {1,10:####0.0} </center></td>"; frmt += "<td><center> {2,10:####0.0} </center></td> <td><center> {3} </center> </td>"; frmt += "<td><center> {4,10:#####0}</center></td> </tr>"; kmlFlightLines.WriteLine(frmt, msn.missionNumber, msn.TimeOverTarget + msn.FerryTime, msn.TimeOverTarget, msn.totalImages, msn.ts.terrain90percentileHeight / 0.3048 + flightAlt); } kmlFlightLines.WriteLine("</table>"); outfile.Flush(); // <table border="1" padding="2"> //<tr><td>234.1</td><td>234.1</td><td>234.1</td><td>234.1</td><td>234.1</td></tr> //<tr><td>234.1</td><td>234.1</td><td>234.1</td><td>234.1</td><td>234.1</td></tr> //</table> //end of the html summary information/////////////////////////////////// kmlFlightLines.WriteLine("]]> </description>"); ////////////////////////END OF SUMMARY TABLE///////////////////////////////// //we also write a placemark in the polygon project folder that will contain the airports that we have used for the overall polygon kmlFlightLines.WriteLine("<Folder> <name> Airports Used for This Plan</name>"); //for (int j = 0; j < p.airports.Count; j++) { kmlFlightLines.WriteLine(String.Format("<Placemark> <name>{0}</name> <styleUrl>#airports</styleUrl>", p.airports[p.selectedAirportIndex].name)); kmlFlightLines.WriteLine(String.Format("<Point> <coordinates>{0},{1},{2} </coordinates> </Point> </Placemark>", p.airports[p.selectedAirportIndex].lon, p.airports[p.selectedAirportIndex].lat, 0)); } kmlFlightLines.WriteLine("</Folder>"); //write a folder to contain the summary background images for the flight system kmlFlightLines.WriteLine("<Folder> <name> Summary Images</name>"); foreach (MissionSummary msn in MissionSumA) { //show the summary image for this mission that was extracted from Google Maps kmlFlightLines.WriteLine("<GroundOverlay> <name> Summary_{0:000} </name> <visibility>0</visibility> <drawOrder> 10 </drawOrder> <LatLonAltBox>", msn.missionNumber); kmlFlightLines.WriteLine("<north>{0}</north> <south>{1}</south>", msn.backgroundImageNWCorner.Y, msn.backgroundImageSECorner.Y); kmlFlightLines.WriteLine("<east>{0}</east> <west>{1}</west>", msn.backgroundImageSECorner.X, msn.backgroundImageNWCorner.X); kmlFlightLines.WriteLine("</LatLonAltBox> <Lod> <minLodPixels>128</minLodPixels>"); kmlFlightLines.WriteLine("<maxLodPixels>-1</maxLodPixels> </Lod> <Icon> <href>{0}</href> </Icon>", msn.backgroundImageFilename); kmlFlightLines.WriteLine(" </GroundOverlay>"); } kmlFlightLines.WriteLine("</Folder>"); //next write the folders that represent the missions foreach (MissionSummary msn in MissionSumA) { //write the kml folder information for this mission kmlFlightLines.WriteLine(String.Format("<Folder> <name>Mission Number {0}</name> <missionNumber>{1}</missionNumber><numFLs>{2}</numFLs> <flightAltMSL> {3:#####} </flightAltMSL>", msn.missionNumber, msn.missionNumber, msn.numberFlightlines, msn.FlightAltMSLft)); kmlFlightLines.WriteLine("<description> <![CDATA["); kmlFlightLines.WriteLine(String.Format("Airport: {0} ({1}) <br>", msn.takeoffAirport.name, msn.takeoffAirport.ICAO_designator)); kmlFlightLines.WriteLine(String.Format("Ferry Time: {0:0.00} <br>", msn.FerryTime)); kmlFlightLines.WriteLine(String.Format("number of Flightlines: {0} <br>", msn.numberFlightlines)); kmlFlightLines.WriteLine(String.Format("Total Flightline Length (mi): {0:###.00} <br>", msn.LengthMi)); kmlFlightLines.WriteLine(String.Format("Total Time-Over-Target (hr): {0:#.00} <br>", msn.TimeOverTarget)); kmlFlightLines.WriteLine(String.Format("Total Flight Time (hr): {0:#.00} <br>", msn.TimeOverTarget + msn.FerryTime)); kmlFlightLines.WriteLine(String.Format("Total Images: {0} <br>", msn.totalImages)); kmlFlightLines.WriteLine("]]> </description>"); //write a Placemark for each mission that is the mission-specific polygon boundary kmlFlightLines.WriteLine(String.Format("<Placemark> <name> MissionPolygon_{0:00} </name> <styleUrl>#blueLine</styleUrl> <LineString> <tessellate>1</tessellate>", msn.missionNumber)); kmlFlightLines.WriteLine("<coordinates>"); foreach (PointD pt in msn.missionPolygon) kmlFlightLines.WriteLine(String.Format("{0:#.00000000},{1:#.00000000},{2,1}", pt.X, pt.Y, 0)); kmlFlightLines.WriteLine("</coordinates> </LineString> </Placemark>"); kmlFlightLines.WriteLine(); String flightLineStyle; if (msn.missionNumber % 2 == 0) { if (msn.setNumber % 2 == 0) flightLineStyle = "evenMsnNumberEvenSet"; else flightLineStyle = "evenMsnNumberOddSet"; } else { if (msn.setNumber % 2 == 0) flightLineStyle = "oddMsnNumberEvenSet"; else flightLineStyle = "oddMsnNumberOddSet"; } flightLineStyle = "whiteLine"; //write out the flightline information as separate placemarks within the mission folder for (int j = msn.startFlightLine; j < (msn.startFlightLine + msn.numberFlightlines); j++) { kmlFlightLines.WriteLine(String.Format("<Placemark> <name> FlightlineNumber_{0:000} </name> <styleUrl>#{1}</styleUrl> <lengthMeters>{2:#.000}</lengthMeters>", FLSum[j].lineNumberInPolygon, flightLineStyle, FLSum[j].flightlineLengthMi * 5280.0 * 0.3048)); kmlFlightLines.WriteLine("<description> <![CDATA["); kmlFlightLines.WriteLine(String.Format(" FlightLine Number {0} <br> ", FLSum[j].lineNumberInPolygon)); kmlFlightLines.WriteLine(String.Format(" FlightLine Length {0:##.00} mi <br> ", FLSum[j].flightlineLengthMi)); kmlFlightLines.WriteLine(String.Format(" NumberImages {0} <br> ", FLSum[j].numImages)); kmlFlightLines.WriteLine("]]> </description>"); kmlFlightLines.WriteLine("<LineString> <coordinates>"); kmlFlightLines.WriteLine(String.Format("{0:#.00000000},{1:#.00000000},{2,1} {3:#.00000000},{4:#.00000000},{5,1}", FLSum[j].planned.startLon, FLSum[j].planned.startLat, 0, FLSum[j].planned.endLon, FLSum[j].planned.endLat, 0)); kmlFlightLines.WriteLine("</coordinates></LineString> </Placemark>"); outfile.WriteLine(String.Format(" {0,5} {1:#.00000000},{2:#.00000000},{3,3} {4:#.00000000},{5:#.00000000},{6,3}", j, FLSum[j].planned.startLon, FLSum[j].planned.startLat, 0, FLSum[j].planned.endLon, FLSum[j].planned.endLat, 0)); outfile.Flush(); } // 152.130789946619,-32.8074906634451,0 152.132154176824, -32.6668497,0 //closing tag for this mission Folder kmlFlightLines.WriteLine("</Folder>"); } //closing tag for the overall Client Polygon Mission plan kmlFlightLines.WriteLine("</Folder>"); //closing tag for the overall kml document kmlFlightLines.WriteLine("</Document> </kml>"); }
//individual mission (collection of paths) parameters private void writeKmlLinearFeatureMissonPlan( Polygon p, //contains general information regarding the geometric path (prior used for polygon mission) double DRImageHeightKm, //the downrange distance between camera triggers to achieve overlap int parallelPaths, //Total number of paths for this project double flightAlt, //flight altitude agl to achieve the requested resolution double swathWidthKm, //swath width that is constant over the project double totalPathDistance, //total flight line distance over the project double totalFerryDistance, //total ferry time for the project double aircraftSpeedKPH, //average airspeed double totalTimeOverTarget, //total time over the target int totalImages, //total images for the project List<LINEAR_FEATURE> linearFeatures) { ////////////////////////////////////////////////////////////////////////////////////////////// //open the file to write the kml Project results ////////////////////////////////////////////////////////////////////////////////////////////// kmlOutputFilename = datasetFolder + "\\" + p.PolygonName + ".kml"; FileStream fs2 = null; try { fs2 = new FileStream(kmlOutputFilename, FileMode.Create, FileAccess.Write, FileShare.None); } catch { MessageBox.Show("cant open the kml Project results file -- input file cannot be same as output file "); } kmlFlightLines = new StreamWriter(fs2); //kml file where we will write kmlFlightLines.AutoFlush = true; /////////////////////////////kml header ///////////////////////////////////////// writekmlHeader("LinearFeature"); ///////////////////////// header denotes that this is a LinearFeature coverage kmlFlightLines.WriteLine(); //write the placemark for the original linear Feature input file (linearFeature name) kmlFlightLines.WriteLine("<Placemark> <name>" + p.PolygonName + "</name> <styleUrl>#redLine</styleUrl> <LineString> <tessellate>1</tessellate>"); kmlFlightLines.WriteLine("<coordinates>"); for (int i = 0; i < p.longitudeDeg.Count; i++) //write out the original path from the initial input .kml kmlFlightLines.WriteLine(String.Format("{0:#.000000000},{1:#.000000000},{2,1}", p.longitudeDeg[i], p.latitudeDeg[i], 0)); kmlFlightLines.WriteLine("</coordinates> </LineString> </Placemark>"); kmlFlightLines.WriteLine(); //show the summary image bounds for this Project that was extracted from Google Maps kmlFlightLines.WriteLine("<GroundOverlay> <name>Project Map</name> <visibility>0</visibility> <drawOrder> 10 </drawOrder> <LatLonAltBox>"); kmlFlightLines.WriteLine("<north>{0}</north> <south>{1}</south>", p.NWMapCorner.Y, p.SEMapCorner.Y); //lat for the NW and SE corners kmlFlightLines.WriteLine("<east>{0} </east> <west>{1} </west>", p.SEMapCorner.X, p.NWMapCorner.X); kmlFlightLines.WriteLine("</LatLonAltBox> <Lod> <minLodPixels>128</minLodPixels>"); kmlFlightLines.WriteLine("<maxLodPixels>-1</maxLodPixels> </Lod> <Icon> <href>{0}</href> </Icon>", p.mapName); kmlFlightLines.WriteLine(" </GroundOverlay>"); /////////////////////////////end of the initial kml material/////////////////////////////////////////////////////// //folder containing the individual path data //first write some misc data that will be read by the Waldo_FCS kmlFlightLines.WriteLine("<Folder> <name> {0} Mission Plan </name>", p.PolygonName); //PlaceMarkName is from the original input kml kmlFlightLines.WriteLine("<downRangePhotoSpacingMeters> {0} </downRangePhotoSpacingMeters>", DRImageHeightKm * 1000.0); //photoSpacing in meters kmlFlightLines.WriteLine("<numberOfPaths> {0} </numberOfPaths>", parallelPaths); //total number of parallel Paths for a linear feature kmlFlightLines.WriteLine("<UTMZone>{0}</UTMZone>", p.UTMZone); //UTM zone kmlFlightLines.WriteLine("<gridOriginUTMNorthing> {0} </gridOriginUTMNorthing>", p.maxNorthing); //grid origin NW corner: maximum northing kmlFlightLines.WriteLine("<gridOriginUTMEasting> {0} </gridOriginUTMEasting>", p.minEasting); //grid origin NW corner: minEasting kmlFlightLines.WriteLine("<proNavGain> {0:#.} </proNavGain>", linearFeatures[0].proNavGain); //pronav gain constant for all paths kmlFlightLines.WriteLine("<rabbitAheadDistance> {0:#.0} </rabbitAheadDistance>", linearFeatures[0].rabbitAheadDistance); //target ahead distance for all paths kmlFlightLines.WriteLine("<flightAltAGLft> {0:#.0} </flightAltAGLft>", flightAlt); //agl flight alt in ft //next prepare the html table summarizing the mission ////////////////////////////////START OF SUMMARY TABLE///////////////////////////////////////////////////////////// kmlFlightLines.WriteLine("<description> <![CDATA["); //open of the html segment kmlFlightLines.WriteLine(String.Format("Total Path Length: {0:#.0} Km ({1:#.0} Mi) <br>", totalPathDistance / 1000.0, totalPathDistance / 0.3048 / 5280.0)); kmlFlightLines.WriteLine(String.Format("Flight Altitude: {0:#} ft (AGL) <br>", flightAlt)); kmlFlightLines.WriteLine(String.Format("Ground Sample Distance: {0:#.0}cm ({1:#.0}in) <br>", resolutionCm, resolutionCm / 100.0 / 0.3048 * 12.0)); kmlFlightLines.WriteLine(String.Format("Crossrange Swath: {0:0.00}km ({1:0.00}mi) <br>", swathWidthKm, swathWidthKm * 1000.0 / 0.3048 / 5280.0)); kmlFlightLines.WriteLine("Takeoff Airport: " + airport.Name); //TODO: this isnt right!! speed isnt constant due to the forward speed during climb kmlFlightLines.WriteLine(String.Format("Total Flight Distance with Ferry {0:#.0}km ({1:#.0}mi) <br>", (totalPathDistance + totalFerryDistance)/1000.0, (totalPathDistance + totalFerryDistance) / 0.3048 / 5280.0)); kmlFlightLines.WriteLine(String.Format("Total Flight Time with Ferry (hr): {0:0.00} <br>", totalTimeOverTarget + totalFerryDistance/1000.0/aircraftSpeedKPH)); //kmlFlightLines.WriteLine(String.Format("Total Number of Flightlines: {0} <br>", numFL)); //kmlFlightLines.WriteLine(String.Format("Average Flightline Length: {0:0.0}km ({1:0.0}mi) <br>", // totalFlightlineDistance / numFL, (totalFlightlineDistance / numFL) * 1000.0 / 0.3048 / 5280.0)); kmlFlightLines.WriteLine(String.Format("Total Images: {0} <br>", totalImages)); kmlFlightLines.WriteLine("______________________________________________<br> <br>"); kmlFlightLines.WriteLine("<b> <center>Path Summary </center></b> <br>"); kmlFlightLines.WriteLine(@"<table border=""1"" padding=""2"">"); String tableHeader = "<tr> <td><center> path </center> </td> <td><center> length (mi) </center> </td> <td><center> Path Time (hr) "; tableHeader += "</center> </td> <td><center> images </center> </td><td><center> maxBank (deg) </center> </td> <td><center> delAlt (ft) </center> </td></tr>"; kmlFlightLines.WriteLine(tableHeader); //form an html table to show the summary of each parallel path //List<> linearFeatures will contain description of each parallel path foreach (LINEAR_FEATURE linearFeature in linearFeatures) { //table Column Headers: path, length (mi), Path Time (hr), images, del-alt (ft) String frmt = "<tr>"; frmt += "<td><center> {0} </center></td> <td><center> {1,10:####0.0} </center></td>"; frmt += "<td><center> {2,10:####0.0} </center></td> <td><center> {3} </center> </td>"; frmt += "<td><center> {4,10:####0.0} </center></td>"; frmt += "<td><center> {5,10:####0.0} </center></td> </tr>"; kmlFlightLines.WriteLine(frmt, linearFeature.pathNumber, linearFeature.pathLength/0.3048 / 5280.0, linearFeature.pathLength /1000.0 / aircraftSpeedKPH, linearFeature.numImages, linearFeature.maximumMeasuredBank, linearFeature.maxAltitude - linearFeature.minAltitude); } kmlFlightLines.WriteLine("</table>"); outfile.Flush(); // <table border="1" padding="2"> //<tr><td>234.1</td><td>234.1</td><td>234.1</td><td>234.1</td><td>234.1</td></tr> //<tr><td>234.1</td><td>234.1</td><td>234.1</td><td>234.1</td><td>234.1</td></tr> //</table> //end of the html summary information/////////////////////////////////// kmlFlightLines.WriteLine("]]> </description>"); ////////////////////////END OF SUMMARY TABLE FOR LINEAR FEATURE///////////////////////////////// //Write a placemark in the project folder that will contain the airport that we have used for the overall project kmlFlightLines.WriteLine("<Placemark> <name> Airports Used for This Plan</name>"); //for (int j = 0; j < p.airports.Count; j++) //uncomment to show all airports { kmlFlightLines.WriteLine(String.Format("<name>{0}</name> <styleUrl>#airports</styleUrl>", p.airports[p.selectedAirportIndex].name)); kmlFlightLines.WriteLine(String.Format("<Point> <coordinates>{0},{1},{2} </coordinates> </Point> ", p.airports[p.selectedAirportIndex].lon, p.airports[p.selectedAirportIndex].lat, 0)); } kmlFlightLines.WriteLine("</Placemark>"); //write a folder to contain the summary background image bounds for the flight system //we will have numerous background images spaced along the path for each parallel path kmlFlightLines.WriteLine("<Folder> <name> Summary Images</name>"); foreach (LINEAR_FEATURE linearFeature in linearFeatures) { kmlFlightLines.WriteLine(String.Format("<numBGImagesThisPath> {0} </numBGImagesThisPath>", linearFeature.NWMapCorner.Count)); for (int i=0; i<linearFeature.NWMapCorner.Count; i++) { //show the summary image for this mission that was extracted from Google Maps kmlFlightLines.WriteLine("<GroundOverlay> <name> BG_{0:00}_{1:00} </name> <visibility>0</visibility> <drawOrder> 10 </drawOrder> <LatLonAltBox>", linearFeature.pathNumber, i); kmlFlightLines.WriteLine("<north>{0}</north> <south>{1}</south>", linearFeature.NWMapCorner[i].Y, linearFeature.SEMapCorner[i].Y); kmlFlightLines.WriteLine("<east>{0}</east> <west>{1}</west>", linearFeature.SEMapCorner[i].X, linearFeature.NWMapCorner[i].X); kmlFlightLines.WriteLine("</LatLonAltBox> <Lod> <minLodPixels>128</minLodPixels>"); kmlFlightLines.WriteLine("<maxLodPixels>-1</maxLodPixels> </Lod> <Icon> <href>{0}</href> </Icon>", linearFeature.mapNames[i]); kmlFlightLines.WriteLine(" </GroundOverlay>"); } } kmlFlightLines.WriteLine("</Folder>"); //next write the folders that represent the data for eath path foreach (LINEAR_FEATURE linearFeature in linearFeatures) { //write the kml folder containing information for this path kmlFlightLines.WriteLine(String.Format("<Folder> <name>Path Number {0}</name> <pathNumber>{1}</pathNumber><numPaths>{2}</numPaths> <flightAltMSL> {3:#####} </flightAltMSL>", linearFeature.pathNumber, linearFeature.pathNumber, parallelPaths, linearFeature.meanAltitude + flightAlt)); airport myAirport = p.airports[p.selectedAirportIndex]; kmlFlightLines.WriteLine("<description> <![CDATA["); kmlFlightLines.WriteLine(String.Format("Airport: {0} ({1}) <br>", myAirport.name, myAirport.ICAO_designator)); kmlFlightLines.WriteLine(String.Format("Ferry Time: {0:0.00} <br>", totalFerryDistance/1000.0/aircraftSpeedKPH)); kmlFlightLines.WriteLine(String.Format("Path Length (mi): {0:###.00} <br>", linearFeature.pathLength/0.3048/5280.0)); kmlFlightLines.WriteLine(String.Format("Total Path (hr): {0:#.00} <br>", linearFeature.pathLength/1000.0/aircraftSpeedKPH)); kmlFlightLines.WriteLine(String.Format("Number Images: {0} <br>", linearFeature.pathLength/DRImageHeightKm/1000.0)); kmlFlightLines.WriteLine("]]> </description>"); //write a Placemark for each Path that is the smoothed trajectory that will be followed (red line) kmlFlightLines.WriteLine(@" <Placemark> <name>Smoothed Linear Feature Trajectory</name> <styleUrl>#redLine</styleUrl> "); kmlFlightLines.WriteLine(@" <LineString> <tessellate>1</tessellate> <coordinates>"); for (int i = 0; i < linearFeature.NorthingProNavS.Count; i++) { double latitude0 = 0.0, longitude0 = 0.0; utm.UTMtoLL(linearFeature.NorthingProNavS[i], linearFeature.EastingProNavS[i], p.UTMZone, ref latitude0, ref longitude0); //store the X, Y in a kml lineString file kmlFlightLines.WriteLine(String.Format(" {0:#.00000000},{1:#.00000000},{2:#.00} ", longitude0, latitude0, linearFeature.alongPathAltitude[i] + flightAlt)); kmlFlightLines.Flush(); } kmlFlightLines.WriteLine(@" </coordinates> </LineString> </Placemark>"); //write a Placemark for each projected image point along the path (blue line) kmlFlightLines.WriteLine(@" <Placemark> <name>Image Center Projection</name> <styleUrl>#blueLine</styleUrl> "); kmlFlightLines.WriteLine(@" <LineString> <tessellate>1</tessellate> <coordinates>"); for (int i = 0; i < linearFeature.NorthingProjection.Count; i++) { double latitude0 = 0.0, longitude0 = 0.0; utm.UTMtoLL(linearFeature.NorthingProjection[i], linearFeature.EastingProjection[i], p.UTMZone, ref latitude0, ref longitude0); //store the X, Y in a kml lineString file kmlFlightLines.WriteLine(String.Format(" {0:#.00000000},{1:#.00000000},{2:#.00} ", longitude0, latitude0, 0)); kmlFlightLines.Flush(); } kmlFlightLines.WriteLine(@" </coordinates> </LineString> </Placemark>"); //write a Placemark containing line segments showing the right-left edge extent of each image (white lines) kmlFlightLines.WriteLine(@" <Placemark> <name>Linear Feature Image Extents</name> <MultiGeometry>"); for (int i = 0; i < linearFeature.NorthingImage1.Count; i++) { kmlFlightLines.WriteLine(@" <LineString> <coordinates>"); kmlFlightLines.WriteLine(@" "); double latitude1 = 0.0, longitude1 = 0.0, latitude2 = 0.0, longitude2 = 0.0; utm.UTMtoLL(linearFeature.NorthingImage1[i], linearFeature.EastingImage1[i], p.UTMZone, ref latitude1, ref longitude1); utm.UTMtoLL(linearFeature.NorthingImage2[i], linearFeature.EastingImage2[i], p.UTMZone, ref latitude2, ref longitude2); //correct format for point pair: 152.130789946619,-32.8074906634451,0 152.132154176824, -32.6668497,0 kmlFlightLines.WriteLine(String.Format(" {0:#.00000000},{1:#.00000000},{2:#.00} {3:#.00000000},{4:#.00000000},{5:#.00}", longitude1, latitude1, 0, longitude2, latitude2, 0)); kmlFlightLines.WriteLine(@" </coordinates> </LineString>"); } kmlFlightLines.WriteLine(@" </MultiGeometry> </Placemark>"); //closing tag for this paths Folder kmlFlightLines.WriteLine("</Folder>"); } //closing tag for the overall Mission plan kmlFlightLines.WriteLine("</Folder>"); //closing tag for the overall kml document kmlFlightLines.WriteLine("</Document> </kml>"); }
void LinearFeaturePlanner(Polygon p, double flightAlt, double swathWidthKm, double aircraftSpeedKnots, double aircraftSpeedKPH, double DRImageHeightKm) { //if (p.polyCoverageType == COVERAGE_TYPE.linearFeature) //{ //show the "PLAN" button button3.Visible = true; button3.Enabled = true; /////////////////////////////////////////////////////////////////////////////////// // wait here til user clicks considers the coverage options and clicks: "PLAN" /////////////////////////////////////////////////////////////////////////////////// while (!MissionPlanningInitiated) { Application.DoEvents(); //wait for user inputs } //TODO //get the flyable aircraft paths that will cover the input linear feature path //there will be one linearFeature object for each of the parallel paths List<LINEAR_FEATURE> linearFeatures = linearFeatureCoverage(numParallelPaths, linearPathCentering, flightAlt, swathWidthKm * 1000.0, aircraftSpeedKnots * 0.514); //the above procedure writes kml files for forward, backward, & smoothed trajectory //also writes out the image boresight projection and the image endpoints as kml files GoogleMaps ggl = new GoogleMaps(datasetFolder, p.PolygonName); //get the project map for the path -- shows complete extent of the path and takeoff airport ggl.getGoogleMapToCoverProject(ref p, downloadMap); //get the path-segment maps that will be used to form a aircraft-centered sliding window map //get rectangular maps sized 10mi x 10mi at 5mi intervals along the path ggl.createBackgroundMapsForLinearFeature(linearFeatures, p.UTMZone, downloadMap); double totalPathDistance = 0.0; polygonMath polymath = new polygonMath(linearFeatures[0].EastingProNavS, linearFeatures[0].NorthingProNavS, datasetFolder); //get the along-path altitudes for (int iLF = 0; iLF < linearFeatures.Count; iLF++) { totalPathDistance += linearFeatures[iLF].pathLength; List<PointD> pointsAlongPath = new List<PointD>(); UTM2Geodetic utm = new UTM2Geodetic(); //create a sequence of points along path based on the smoothed trajectory for (int i = 0; i < linearFeatures[iLF].NorthingProNavS.Count; i++) { double lat = 0.0, lon = 0.0; utm.UTMtoLL(linearFeatures[iLF].NorthingProNavS[i], linearFeatures[iLF].EastingProNavS[i], p.UTMZone, ref lat, ref lon); pointsAlongPath.Add(new PointD(lon, lat)); } //get the terrain heights using the Google Elevation API linearFeatures[iLF].alongPathAltitude = polymath.getTerrainHeightsFromPointList(pointsAlongPath); //number of triggered images along the path linearFeatures[iLF].numImages = (int)(linearFeatures[iLF].pathLength / (DRImageHeightKm * 1000.0)); //get stats of the along-path altitudes double maxAlt = -99999.0, minAlt = 99999.0, meanAlt = 0.0; foreach (double alt in linearFeatures[iLF].alongPathAltitude) { if (alt > maxAlt) maxAlt = alt; if (alt < minAlt) minAlt = alt; meanAlt += alt; } meanAlt /= linearFeatures[iLF].alongPathAltitude.Count; linearFeatures[iLF].maxAltitude = maxAlt; linearFeatures[iLF].minAltitude = minAlt; linearFeatures[iLF].meanAltitude = meanAlt; linearFeatures[iLF].pathNumber = iLF; } //get the 2way ferry distance double total2WayFerryDistance = 0.0; //start: always start for 0th path to the selected airport //distance from selected airport to start of first line double delNS = linearFeatures[0].NorthingProNavS[0] - p.airports[selectedAirportIndex].northing; double delES = linearFeatures[0].EastingProNavS[0] - p.airports[selectedAirportIndex].easting; double distanceToAptS = Math.Sqrt(delNS * delNS + delES * delES); total2WayFerryDistance += distanceToAptS; //always return to the airport from the end of the last path int endIndex = linearFeatures[linearFeatures.Count - 1].NorthingProNavS.Count - 1; //distance from selected airport to start of first line delNS = linearFeatures[linearFeatures.Count - 1].NorthingProNavS[endIndex] - p.airports[selectedAirportIndex].northing; delES = linearFeatures[linearFeatures.Count - 1].EastingProNavS[endIndex] - p.airports[selectedAirportIndex].easting; distanceToAptS = Math.Sqrt(delNS * delNS + delES * delES); //final 2-way ferry distance is the sum of the to- and from- ferry distances total2WayFerryDistance += distanceToAptS; double totalPathTime = totalPathDistance / (aircraftSpeedKPH * 1000.0); int totalLFImages = (int)(totalPathDistance / (DRImageHeightKm * 1000.0)); writeKmlLinearFeatureMissonPlan(p, DRImageHeightKm, numParallelPaths, flightAlt, swathWidthKm, totalPathDistance, total2WayFerryDistance, aircraftSpeedKPH, totalPathTime, totalLFImages, linearFeatures); ////////////////////////////////////////////// //show the END button button2.Visible = true; button2.Enabled = true; ////////////////////////////////////////////// //wait here to see if the user clicks the "END" button while (true) { Application.DoEvents(); } //Environment.Exit(-1); //} //////////////////////// end of linear feature Planner ////////////////// }