////////////////////////////////////////////////////////////
        //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();
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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;
        }
Beispiel #4
0
        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
                    }
                }
            }
        }
Beispiel #5
0
        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;
        }
Beispiel #6
0
        //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>");
        }
Beispiel #7
0
        //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>");
        }
Beispiel #8
0
        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 //////////////////
        }