예제 #1
0
        public COVERAGE_TYPE polygonGeometry()
        {
            ///////////////////////////////////////////////////////////////////////
            // get the mean polygon location and the polygon area
            // convert all vertices to UTM
            // get the UTM zone that will be used
            // get recangular bounding box
            // determine the orientation of the point-scatter covariance ellipse
            ///////////////////////////////////////////////////////////////////////
            utm = new UTM2Geodetic();  //class for LL to UTM conversion

            for (int i = 0; i < polygons.Count; i++)
            {
                //local copy of the polygon -- re-inserted into the polygon list at the end of this procedure
                Polygon p = polygons[i];

                int numLatLonPoints = p.latitudeDeg.Count;

                //mean Northing and Easting from mean lat/lon -- also use this to get the UTMZone that will be held fixed for this polygon
                p.meanNorthing = 0.0;
                p.meanEasting = 0.0;

                //NOTE:  get the UTMZone here (for the polygon mean location) that will be used for the whole project
                utm.LLtoUTM(p.meanLatDeg * Deg2Rad, p.meanLonDeg * Deg2Rad, ref p.meanNorthing, ref p.meanEasting, ref p.UTMZone, false);
                //the UTM zone will be held constant across the polygon points -- deals with polys that cross UTM zones

                //get the Northing and Easting from the lat/lon poly points
                double northing = 0.0, easting = 0.0;
                p.maxNorthing = -9999999.0; p.maxEasting = -999999999.0; p.minNorthing = 999999999.0; p.minEasting = 99999999999.0;
                p.maxLatDeg = -99999.0; p.maxLonDeg = -9999999.0; p.minLatDeg = 99999.0; p.minLonDeg = 999999.0;
                double CovXX = 0.0, CovYY = 0.0, CovXY = 0.0;  //covariance of the UTM poly points
                for (int ipnts = 0; ipnts < numLatLonPoints; ipnts++)
                {
                    utm.LLtoUTM(p.latitudeDeg[ipnts] * Deg2Rad, p.longitudeDeg[ipnts] * Deg2Rad, ref northing, ref easting, ref p.UTMZone, true);
                    p.Northing.Add(northing); p.Easting.Add(easting);

                    if (northing > p.maxNorthing) p.maxNorthing = northing;
                    if (northing < p.minNorthing) p.minNorthing = northing;
                    if (easting > p.maxEasting) p.maxEasting = easting;
                    if (easting < p.minEasting) p.minEasting = easting;

                    if (p.latitudeDeg[ipnts]  > p.maxLatDeg) p.maxLatDeg = p.latitudeDeg[ipnts];
                    if (p.latitudeDeg[ipnts]  < p.minLatDeg) p.minLatDeg = p.latitudeDeg[ipnts];
                    if (p.longitudeDeg[ipnts] > p.maxLonDeg) p.maxLonDeg = p.longitudeDeg[ipnts];
                    if (p.longitudeDeg[ipnts] < p.minLonDeg) p.minLonDeg = p.longitudeDeg[ipnts];

                    //covariance elements used to compute the principal axes of the polygon
                    //here we are assuming a north (X), east (Y), down (Z) corrdinatate system
                    CovYY += (easting  - p.meanEasting)  * (easting - p.meanEasting);
                    CovXX += (northing - p.meanNorthing) * (northing - p.meanNorthing);
                    CovXY += (easting  - p.meanEasting)  * (northing - p.meanNorthing);
                }

                CovYY /= 1000*numLatLonPoints;
                CovXX /= 1000*numLatLonPoints;
                CovXY /= 1000*numLatLonPoints;

                //Compute the orientation of the principal axis of the covariance ellipse
                //this is used to determine the best flying direction

                //eigenvalues of covariance matrix -- see below site for equations for eigenvalues/vectors for 2D matrix
                //    people.csail.mit.edu/bkph/articles/Eigenvectors.pdf
                double det = Math.Sqrt( (CovXX - CovYY)*(CovXX - CovYY) + 4.0 * CovXY * CovXY );
                double lambda1 = ( CovXX + CovYY +  det )/ 2.0;
                double lambda2 = ( CovXX + CovYY -  det )/ 2.0;
                //the larger of these is the length of the major axis
                //smaller is length of the minor axis

                //the angle associated with the larger eigenvalue is the angle of the major axis
                //eigenvectors should be 90 deg apart
                if (lambda1 > lambda2)
                {
                    //eigenvectors of covariance matrix
                    double eigen1X = 2.0 * CovXY;
                    double eigen1Y = CovYY - CovXX + det;
                    double ang1 = Math.Atan(eigen1Y / eigen1X) * Constants.Rad2Deg;
                    p.rotationAngleDeg = ang1;
                }
                else
                {
                    //eigenvectors of covariance matrix
                    double eigen2X = 2.0 * CovXY;
                    double eigen2Y = CovYY - CovXX - det;
                    double ang2 = Math.Atan(eigen2Y / eigen2X) * Constants.Rad2Deg;
                    p.rotationAngleDeg = ang2;
                }
                //reflect a negative major axis direction by 180 deg (major axis can go either direction)
                if (p.rotationAngleDeg < 0) p.rotationAngleDeg += 180.0;

                //this must be done after creating the UTM Northing & Easting from the geodetic coordinates
                polygonMath polyMath = new polygonMath(p.Easting, p.Northing, datasetFolder);

                //compute rectangle bounds using the principal axes
                double maxAlongPA = -9999999999, minAlongPA = 9999999999, distanceA = 0.0;
                double maxOrthoPA = -9999999999, minOrthoPA = 9999999999, distanceO = 0.0;
                for (int ipnts = 0; ipnts < numLatLonPoints; ipnts++)
                {
                    //note below that the X components is Northing and the Y components are Easting
                    //The "line" is defined by its origin and rotation angle (positive east of north)
                    //the "point" is the ipnts vertex of the polygon
                    distanceO = polyMath.distanceFromPoint2Line(  //distance orthogonal to the pricipal axis
                        new PointD(p.meanNorthing, p.meanEasting),
                        p.rotationAngleDeg,
                        new PointD(p.Northing[ipnts], p.Easting[ipnts]));
                    distanceA = polyMath.distanceFromPoint2Line(  //distance orthogonal to the pricipal axis
                        new PointD(p.meanNorthing, p.meanEasting),
                        p.rotationAngleDeg + 90,
                        new PointD(p.Northing[ipnts], p.Easting[ipnts]));

                    if (distanceA > maxAlongPA) maxAlongPA = distanceA;
                    if (distanceA < minAlongPA) minAlongPA = distanceA;
                    if (distanceO > maxOrthoPA) maxOrthoPA = distanceO;
                    if (distanceO < minOrthoPA) minOrthoPA = distanceO;
                }

                //length and width of a bounding box with long-side along principal axis
                //length should always be larger than the width
                double lengthKm = (maxAlongPA - minAlongPA) / 1000.0;
                double widthKm = (maxOrthoPA - minOrthoPA)  / 1000.0;
                //Constants.Log("Principal Axis bounding box: " + lengthKm.ToString("F1") + " X " + widthKm.ToString("F1"));
                //if (lengthKm < widthKm)
                //    MessageBox.Show("Principal axes of polygon not longer than minor axis");

                //principal axis direction is defined by p.rotationAngleDeg
                //set the corner of the principal axes bounding box to be negative-going along the major and minor axis
                //this corner will also become the origion of the flight line reference coordinate system
                //this will correspond to a south-east corner for a north-going princpal axis

                p.boundingBoxCornerNorthing =
                    Math.Cos(p.rotationAngleDeg * Constants.Deg2Rad) * (p.meanNorthing + minAlongPA) +
                    Math.Sin(p.rotationAngleDeg * Constants.Deg2Rad) * (p.meanEasting + minOrthoPA);
                p.boundingBoxCornerEasting =
                   -Math.Sin(p.rotationAngleDeg * Constants.Deg2Rad) * (p.meanNorthing + minAlongPA) +
                    Math.Cos(p.rotationAngleDeg * Constants.Deg2Rad) * (p.meanEasting + minOrthoPA);

                //TODO:  should just return the polygon points and fill the p structure after the return
                p.areasqkm = polyMath.areaOfPolygon();
                p.areasqmi = p.areasqkm * 1000000.0 / ((0.3048 * 5280.0) * (0.3048 * 5280.0));

                Constants.Log("");
                if (p.polyCoverageType == COVERAGE_TYPE.polygon)
                {
                    Constants.Log("Polygon area: " + p.areasqmi.ToString("F3") + "  (sqmi)   " + p.areasqkm.ToString("F3") + "  (sqkm)");
                }
                else if (p.polyCoverageType == COVERAGE_TYPE.linearFeature)
                {
                    double pathRange = polyMath.pathLength();
                    Constants.Log("Path length: " + (pathRange/0.3048/5280.0).ToString("F2") +
                        " (mi)  " + (pathRange/1000.0).ToString("F2") + " (km)");
                }
                Constants.Log("");

                ///////////////////////////////////////////////////////////////////////////////////////////////
                //write the summary information for this polygon
                //outfile.WriteLine();
                outfile.WriteLine("PolygonName:  {0}   PolyPoints= {1,4}  Area= {2,9:####.00} (kmsq) {3,9:####.00} (sqmi)",
                        p.PolygonName, numLatLonPoints.ToString(), p.areasqkm, p.areasqmi);
                //foreach (airport apt in apts)
                //       outfile.WriteLine("   {0,50}  distance (mi) {1,8:####.00}", apt.name, apt.distanceToPolyCenterMi);
                /////////////////////////////////////////////////////////////////////////////////////////////////

                //we have modified a copy of the polygon object -- re-insert it into the polygon list
                polygons[i] = p;
            }

            //NOTE: here we are assuming that all the coverage types are identical
            //generally we only use a single coverage for a mission plan session
            return polygons[0].polyCoverageType;
        }
예제 #2
0
        public void createBackgroundMapsForLinearFeature(List<LINEAR_FEATURE> linearFeatures, String UTMZone, bool downloadMap)
        {
            /////////////////TODO -- put in a input file
            ////////////////////////////////////////////////////////////////////////////
            double maxIncrementAlonPathMeters = 5.0 * 5280.0 * 0.3048; // 5.0 miles
            ////////////////////////////////////////////////////////////////////////////

            for (int iFeat = 0; iFeat < linearFeatures.Count; iFeat++)
            {
                LINEAR_FEATURE  linearFeature = linearFeatures[iFeat];

                polygonMath polyMath = new polygonMath(linearFeature.EastingProNavS, linearFeature.NorthingProNavS, datasetFolder);
                UTM2Geodetic utm = new UTM2Geodetic();

                linearFeature.NWMapCorner = new List<PointD>();
                linearFeature.SEMapCorner = new List<PointD>();
                linearFeature.mapNames = new List<string>();

                //find a deltaRange along the path < maxIncrementAlonPathMeters such that deltaRange * N = totalPathLength
                int nMaps = (int)(linearFeature.pathLength / maxIncrementAlonPathMeters);
                //above gives a number of increments such that one additional increment will fall outside the endpoint.
                double incrementAlongPath = linearFeature.pathLength / (nMaps + 1);
                //this gives an incerment that is < maxIncrementAlonPathMeters such that
                //there will be nMaps+1 increments that end exactly on the end of the path
                //We will need nMaps + 2 maps with one map at the beginning and one map at the end
                int totalMaps = nMaps + 2;

                //this lets us put N equispaced 10mix10mi maps along the path.
                for (int i = 0; i < totalMaps; i++)
                {

                    /////////////////////
                    //get a map
                    /////////////////////
                    //build up the URL to retrieve the Google Map -- see the above model for the syntax

                    //this zoom level gives ~ 10mi X 10mi
                    int zoom = 12;

                    //increment the path
                    PointD pap = polyMath.pointAlongPathFromStart(i * incrementAlongPath);

                    double latitude = 0.0, longitude = 0.0;
                    utm.UTMtoLL(pap.Y, pap.X, UTMZone, ref latitude, ref longitude);

                    setMap(zoom, new PointD(longitude, latitude));

                    /*  below used only to get the width & height of the returned map with zoom = 12
                    double NWNorthing = 0.0, NWEasting = 0.0;
                    double SENorthing = 0.0, SEEasting = 0.0;
                    utm.LLtoUTM(mapNWCornerCordinates.Y * Constants.Deg2Rad, mapNWCornerCordinates.X * Constants.Deg2Rad,
                        ref NWNorthing, ref NWEasting, ref UTMZone, true);
                    utm.LLtoUTM(mapSECornerCordinates.Y * Constants.Deg2Rad, mapSECornerCordinates.X * Constants.Deg2Rad,
                        ref SENorthing, ref SEEasting, ref UTMZone, true);

                    double mapNSDimensionMi = (NWNorthing - SENorthing) / 0.3048 / 5280.0;
                    double mapEWDimensionMi = (SEEasting  - NWEasting)  / 0.3048 / 5280.0;
                     * */

                    linearFeature.NWMapCorner.Add(mapNWCornerCordinates);
                    linearFeature.SEMapCorner.Add(mapSECornerCordinates);

                    String GoogleMapsURL = "http://maps.googleapis.com/maps/api/staticmap?center=" +
                                latitude.ToString() + "," +
                                longitude.ToString() + "&zoom=" + zoom.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 + "\\linearMap_" + iFeat.ToString("D2") +"_" + i.ToString("D3") + ".png";

                    if (downloadMap)
                    {
                        //get the file from the web using a webClient
                        getFileFromWeb(GoogleMapsURL, SaveToFile);
                    }

                    linearFeature.mapNames.Add(SaveToFile);

                }
            }
        }
예제 #3
0
        public void expandPolygonVertices(double border)
        {
            for (int ipoly = 0; ipoly < polygons.Count; ipoly++)
            {
                Polygon p = polygons[ipoly];

                polygonMath polyMath = new polygonMath(p.Easting, p.Northing, datasetFolder);

                ////////////////////////////////////////////////////////////////////////////////////////////////////////
                //expand the client polygon with a border
                //these will become the expanded polygon points
                //note the origonal points are destroyed -- should save them
                List<double> EastingExp = new List<double>();
                List<double> NorthingExp = new List<double>();
                p.OriginalLatitudeDeg = new List<double>();
                p.OriginalLongitudeDeg = new List<double>();
                //double border = 500.0;  //added border in meters
                polyMath.expandedPoly(ref EastingExp, ref NorthingExp, border);

                //preserve the original polygon latitude and longitude
                for (int i = 0; i < EastingExp.Count; i++)
                {
                    p.OriginalLatitudeDeg.Add(p.latitudeDeg[i]);
                    p.OriginalLongitudeDeg.Add(p.longitudeDeg[i]);
                }

                //destroy the old lat-lon points and replace with the new expanded points
                p.latitudeDeg.Clear();
                p.longitudeDeg.Clear();
                p.Easting.Clear();
                p.Northing.Clear();
                for (int i = 0; i < EastingExp.Count; i++)
                {
                    if (NorthingExp[i] > p.maxNorthing) p.maxNorthing = NorthingExp[i];
                    if (NorthingExp[i] < p.minNorthing) p.minNorthing = NorthingExp[i];
                    if (EastingExp[i] > p.maxEasting) p.maxEasting = EastingExp[i];
                    if (EastingExp[i] < p.minEasting) p.minEasting = EastingExp[i];

                    p.Easting.Add(EastingExp[i]);  //replace original with expanded
                    p.Northing.Add(NorthingExp[i]);
                    double lat = 0.0;
                    double lon = 0.0;
                    utm.UTMtoLL(NorthingExp[i], EastingExp[i], p.UTMZone, ref lat, ref lon);
                    p.latitudeDeg.Add(lat);
                    p.longitudeDeg.Add(lon);

                    if (lat > p.maxLatDeg) p.maxLatDeg = lat;
                    if (lat < p.minLatDeg) p.minLatDeg = lat;
                    if (lon > p.maxLonDeg) p.maxLonDeg = lon;
                    if (lon < p.minLonDeg) p.minLonDeg = lon;
                }
                ////////////////////////////////////////////////////////////////////////////////////////////////////
                Constants.Log("Expanded the client polygon by 500m " + p.PolygonName);

                //we have modified a copy of the polygon -- new re-insert it into the list
                polygons[ipoly] = p;
            }
        }
예제 #4
0
        public void generateSmoothTrajectoryWithProNav(String DataSetFolder, String UTMZone, bool computeProjections, ref double maxBankDeg, 
            double ProNavGain, double deltaTime, double DistanceToTarget, double vehicleVelocity, double altitude, double swathWidth,
            List<double> NorthingIn, List<double> EastingIn, List<double> NorthingOut, List<double> EastingOut,
            List<double> NorthingProjected, List<double> EastingProjected, 
            List<double> NorthingIP1, List<double> EastingIP1, List<double> NorthingIP2, List<double> EastingIP2)
        {
            //////////////////////////////////////////////////////////////////////////////////////////
            //using an input set of path points generated by Google earth, create a smoothed
            //trajectory flyable by an aircraft that passes near the points.
            //////////////////////////////////////////////////////////////////////////////////////////

            //initialize the state at first vertex with heading along first segment
            double X = NorthingIn[0];
            double Y = EastingIn[0];
            //heading from 0th point towards the 1th point -- heading is positive CCW from north
            double heading = Math.Atan2(EastingIn[1] - EastingIn[0],
                                         NorthingIn[1] - NorthingIn[0]);

            //add the initial position to the output trajectory
            NorthingOut.Add(X);
            EastingOut.Add(Y);

            //stop a segment when the ProNav acceleration command results in a bank greater than some max bank threshold
            bool stopThisSegment = false;

            polygonMath polyMath = new polygonMath(EastingIn, NorthingIn, datasetFolder);
            UTM2Geodetic utm = new UTM2Geodetic();

            int lastXYVertex = 0;
            double distanceToNextVertex = 0;
            int iTime = 0;
            double bankCommand = 0.0;
            double maxBank = -999999.0;

            //integrate the state in the while loop
            while (!stopThisSegment)
            {
                //////////////////////////////
                //state:  X   Y   heading
                //////////////////////////////

                //get a point on the path that is orthognal to the velocity vector
                PointD pca = polyMath.pointOnPathOrthogonalToVelocityVector(heading, new PointD(X, Y),
                    ref lastXYVertex, ref distanceToNextVertex,
                    EastingIn, NorthingIn);

                //if (lastXYVertex == 1) break;

                //stopping condition -- have gone beyond last vertex in the path
                if (-distanceToNextVertex > 0)
                {
                    Console.WriteLine(" stopping on distanceToNextVertex " + distanceToNextVertex.ToString());
                    stopThisSegment = true;
                }

                //locate a point ahead of (X,Y) along the path by a prescribed distance
                //this becomes the target for pro nav guidance
                //placing the target farther ahead reduces the max bank but smooths the points
                PointD pAhead = polyMath.FuturePointOnLineFeature(
                    pca, lastXYVertex, distanceToNextVertex, DistanceToTarget,
                    EastingIn, NorthingIn);

                //X is north and Y is east
                double velX = vehicleVelocity * Math.Cos(heading);
                double velY = vehicleVelocity * Math.Sin(heading);

                //compute the Line-Of-Sight rate between the platform and the target point
                //the heading_dot will be commanded to -3*LOSRate per the ProNav
                // X -- North      Y -- East -- causes heading to be positive CCW rotation for Right Hand coordinate system
                double delX = pAhead.X - X;
                double delY = pAhead.Y - Y;
                double LOSAngle = Math.Atan2(delY, delX);
                double LOSRate = Math.Cos(LOSAngle) * Math.Cos(LOSAngle) * (-velY / delX + delY * velX / (delX * delX));

                X += velX * deltaTime;
                Y += velY * deltaTime;

                ////////////////////////////////////////////////////
                //generate the output file for the trajectory
                ////////////////////////////////////////////////////
                NorthingOut.Add(X);
                EastingOut.Add(Y);

                double accelCommand = ProNavGain * LOSRate * vehicleVelocity;  //units:  m/s/s

                //pursuit navigation
                //double accelCommand = -0.10 * (heading - LOSAngle) * vehicleVelocity;

                //bank command necessary to maintain level flight and achieve command lateral acceleration
                bankCommand = Math.Atan(accelCommand / Constants.Gravity);  // degrees
                if (Math.Abs(bankCommand) > maxBank) maxBank = Math.Abs(bankCommand);

                heading += (accelCommand / vehicleVelocity) * deltaTime;

                if (accelCommand / Constants.Gravity > 100.0)
                {
                    stopThisSegment = true;
                    Console.WriteLine(" stopping on the bank command threshold ");
                }

                if (iTime%3 == 0 && computeProjections)
                {
                    double Xproj = X + altitude * Math.Tan(bankCommand) * velY / vehicleVelocity;
                    double Yproj = Y - altitude * Math.Tan(bankCommand) * velX / vehicleVelocity;
                    NorthingProjected.Add(Xproj);
                    EastingProjected.Add(Yproj);

                    //creat endpoints for an image taken at this location
                    NorthingIP1.Add(Xproj +  swathWidth * velY / vehicleVelocity / 2.0 );
                    EastingIP1.Add( Yproj -  swathWidth * velX / vehicleVelocity / 2.0 );
                    NorthingIP2.Add(Xproj -  swathWidth * velY / vehicleVelocity / 2.0 );
                    EastingIP2.Add( Yproj +  swathWidth * velX / vehicleVelocity / 2.0 );

                    //predicted miss distance
                    //if we assume the future point doesnt move, we can compute the miss distance
                    //that would occur if the continue with pronav until the point-of-closest-approach
                    double tgo = DistanceToTarget / vehicleVelocity;
                    double Xmiss = 3.0 * (X + velX * tgo) / tgo * tgo;
                    double Ymiss = 3.0 * (Y + velY * tgo) / tgo * tgo;
                    //miss vector orthogonal to flight direction
                }

                iTime++;
                if (iTime > 5000)
                {
                    stopThisSegment = true;
                    Console.WriteLine("stopping on count");
                }

            } //end of trajectory generator
            maxBankDeg = maxBank*Constants.Rad2Deg;

            Console.WriteLine(" Max bank command = " + maxBankDeg.ToString("F2") + " deg");
        }
예제 #5
0
        List<LINEAR_FEATURE> linearFeatureCoverage(int numberParallelPaths, LINEAR_PATH_CENTERING centering,
            double altAGLMeters, double swathWidth, double vehicleVelocityMperSec)
        {
            /////////////////////////////////////////////////////////////////////////////////////////////////////
            //This procedure creates a mission plan from a kml linear path created by the Google Earth Path tool
            //The geodetic path points (vertices) must have been generated using "InspectKMLforMissions()"
            //In this procedure, the input path is "flown" using a 2D simulation and proportional navigation
            //Capability is provided to allow multiple side-by-side parallel paths
            //either centered over the inoput path of to the right of this path. Separate flight lines
            //may be required for a single path if the path turns are too sharp.
            /////////////////////////////////////////////////////////////////////////////////////////////////////

            //TODO:  get these from the input file
            //pronav numerical integration parameters
            double proNavGain       = 3.0;      //proportional Navigation Gain -- usually assumed to be 3.0
            double deltaTime        = 2.0;      //integration step size along the trajectory for the pro nav simulation
            double distanceToTarget = 2500.0;   //diatance ahead along the path the "rabbit" is placed

            double altitudeAGL = altAGLMeters;
            double vehicleVelocity  = vehicleVelocityMperSec;

            List<LINEAR_FEATURE> linearFeatures = new List<LINEAR_FEATURE>();

            //the following trajectories are constant for all parallel paths
            //these represent the smoothed trajectory for the input path
            //The parallel paths are parallel t the smoothed input path
            List<double>  EastingTrajectoryF = new List<double>();
            List<double>  NorthingTrajectoryF = new List<double>();
            List<double>  EastingTrajectoryB = new List<double>();
            List<double>  NorthingTrajectoryB = new List<double>();
            List<double>  EastingTrajectoryS = new List<double>();
            List<double>  NorthingTrajectoryS = new List<double>();

            List<double> placeHolder = new List<double>();

            //polyMat provides polygon and line path math tools
            polygonMath polyMath = new polygonMath(polys[0].Easting, polys[0].Northing, datasetFolder);

            String UTMZone = polys[0].UTMZone;

            double maxBankDeg = 0.0;

            //generate the pro nav smoothed forward trajectory
            polyMath.generateSmoothTrajectoryWithProNav(datasetFolder, UTMZone, false, ref maxBankDeg,
                proNavGain, deltaTime, distanceToTarget, vehicleVelocity, altitudeAGL, swathWidth,
                polys[0].Northing, polys[0].Easting, NorthingTrajectoryF, EastingTrajectoryF,
                placeHolder, placeHolder,
                placeHolder, placeHolder, placeHolder, placeHolder);

            //create a reversed set of path points so we can fly the path backwards
            List<double> NorthingB = new List<double>();
            List<double> EastingB = new List<double>();
            for (int i = polys[0].Northing.Count - 1; i >= 0; i--)
            {
                NorthingB.Add(polys[0].Northing[i]);
                EastingB.Add(polys[0].Easting[i]);
            }

            //generate the pro nav smoothed backward trajectory
            polyMath.generateSmoothTrajectoryWithProNav(datasetFolder, UTMZone, false, ref maxBankDeg,
                proNavGain, deltaTime, distanceToTarget, vehicleVelocity, altitudeAGL, swathWidth,
                NorthingB, EastingB, NorthingTrajectoryB, EastingTrajectoryB,
                placeHolder, placeHolder,
                placeHolder, placeHolder, placeHolder, placeHolder);

            List<double> NorthingOutBrev = new List<double>();
            List<double> EastingOutBrev = new List<double>();
            //reverse this trajectory so we can match it to the forward trajectory
            for (int i = NorthingTrajectoryB.Count - 1; i >= 0; i--)
            {
                NorthingOutBrev.Add(NorthingTrajectoryB[i]);
                EastingOutBrev.Add(EastingTrajectoryB[i]);
            }

            //
            //average the two paths to get a smoothed path that can be flown both ways
            int numRecs = NorthingTrajectoryF.Count;
            double distanceToNextVertex = 0;
            int index = 0;
            if (NorthingTrajectoryF.Count > NorthingTrajectoryB.Count)
                numRecs = NorthingTrajectoryB.Count;
            //go through all points and do the averaging
            for (int i = 0; i < numRecs; i++)
            {
                //increment through points in forward trajectory
                //locate point-of-closest-approach on the backward trajectorty
                //average these two points to get the smoothed trajectory

                PointD pca = polyMath.distanceFromPoint2Linefeature(
                    new PointD(NorthingTrajectoryF[i], EastingTrajectoryF[i]),
                    ref index, ref distanceToNextVertex, NorthingOutBrev, EastingOutBrev);

                NorthingTrajectoryS.Add((NorthingTrajectoryF[i] + pca.X) / 2.0);
                EastingTrajectoryS.Add((EastingTrajectoryF[i] + pca.Y) / 2.0);

            }

            /*
            //the following diagnostic kml files are constant for each path
            prepareKmlForUTMPointList(datasetFolder, "TrajectoryF",
                NorthingTrajectoryF, EastingTrajectoryF, UTMZone);
            prepareKmlForUTMPointList(datasetFolder, "TrajectoryB",
                NorthingTrajectoryB, EastingTrajectoryB, UTMZone);
            prepareKmlForUTMPointList(datasetFolder, "TrajectoryS ",
                NorthingTrajectoryS, EastingTrajectoryS, UTMZone);
             * */

            //prepare the trajectory information for the offset parallel paths
            //NOTE: path 0 is always assumed to be the initial path flown from start-to-end as defined by the input data
            //      successive even paths are flown end-to-start (reversed direction)
            //      successive odd paths are flown start-to-end
            for (int iPar = 0; iPar < numberParallelPaths; iPar++)
            {
                LINEAR_FEATURE linearFeature = new LINEAR_FEATURE();

                linearFeature.proNavGain = proNavGain;
                linearFeature.rabbitAheadDistance = distanceToTarget;

                linearFeature.EastingProNavS        = new List<double>();
                linearFeature.NorthingProNavS       = new List<double>();
                linearFeature.EastingProjection     = new List<double>();
                linearFeature.NorthingProjection    = new List<double>();
                linearFeature.EastingImage1         = new List<double>();
                linearFeature.NorthingImage1        = new List<double>();
                linearFeature.EastingImage2         = new List<double>();
                linearFeature.NorthingImage2        = new List<double>();
                linearFeature.alongPathAltitude     = new List<double>();

                //this where we offset the input path depending on  numberParallelPaths & LINEAR_PATH_CENTERING
                //generate an offset trajectory to the smoothed trajectory
                polygonMath polyMath2 = new polygonMath(EastingTrajectoryS, NorthingTrajectoryS, datasetFolder);
                double offset = 0.0;
                if (     centering == LINEAR_PATH_CENTERING.centeredOnInputPath) offset = -(numberParallelPaths - 1) * swathWidth / 2.0 + iPar * swathWidth;
                else if (centering == LINEAR_PATH_CENTERING.offsetToLeftOfInputPath) offset = iPar * swathWidth;
                else if (centering == LINEAR_PATH_CENTERING.offsetToRightOfInputPath) offset = -iPar * swathWidth;
                offset *= 0.70; ///this accounts for a 30% sidelap between paths

                //create the current path when multiple paths are requested
                List<double> EastingParallel = new List<double>();
                List<double> NorthingParallel = new List<double>();
                //return a parallel path from the input smoothed path
                polyMath2.parallelPath(ref EastingParallel, ref NorthingParallel, offset);

                //should this be polyMath2 ???
                linearFeature.pathLength = polyMath.pathLength();

                //create a reversed set of path points so we can fly the path backwards
                List<double> NorthingRev = new List<double>();
                List<double> EastingRev = new List<double>();
                if (iPar % 2 != 0)  //locate the odd paths (1, 3, 5, 7 ...
                {
                    for (int i = EastingTrajectoryS.Count - 1; i >= 0; i--)
                    {
                        NorthingRev.Add(NorthingParallel[i]);
                        EastingRev.Add(EastingParallel[i]);
                    }
                    for (int i = 0; i < EastingTrajectoryS.Count; i++)
                    {
                        NorthingParallel[i] = NorthingRev[i];
                        EastingParallel[i] = EastingRev[i];
                    }
                }

                //generate the pro nav solution for the smoothed forward/backward trajectory
                //this is the trajectory that is expected from the actual flight mission
                polyMath.generateSmoothTrajectoryWithProNav(datasetFolder, UTMZone, true, ref maxBankDeg,
                    proNavGain, deltaTime, distanceToTarget/2.0, vehicleVelocity, altitudeAGL, swathWidth,
                    NorthingParallel, EastingParallel,
                    linearFeature.NorthingProNavS,     linearFeature.EastingProNavS,

                    linearFeature.NorthingProjection, linearFeature.EastingProjection,
                    linearFeature.NorthingImage1, linearFeature.EastingImage1,
                    linearFeature.NorthingImage2, linearFeature.EastingImage2);

                String parallelPathNotation= "";
                if (numberParallelPaths > 0) parallelPathNotation = iPar.ToString("D2");

                /*
                //the following kml output files vary with each parallel path
                prepareKmlForUTMPointList(datasetFolder, "ParallelPath" + parallelPathNotation,
                    NorthingParallel, EastingParallel, UTMZone);
                prepareKmlForUTMPointList(datasetFolder, "ProNavS" + parallelPathNotation,
                    linearFeature.NorthingProNavS, linearFeature.EastingProNavS, UTMZone);
                prepareKmlForUTMPointList(datasetFolder, "Projection" + parallelPathNotation,
                    linearFeature.NorthingProjection, linearFeature.EastingProjection, UTMZone);
                prepareKmlForUTMPointPairs(datasetFolder, "ImageEndpoints" + parallelPathNotation, UTMZone,
                        linearFeature.NorthingImage1, linearFeature.EastingImage1,
                        linearFeature.NorthingImage2, linearFeature.EastingImage2);
                 * */

                linearFeature.maximumMeasuredBank = maxBankDeg;

                //add the linear feature just prepared fo the list of linear features
                //there will be one prepared linear feature for each of the parallel paths
                linearFeatures.Add(linearFeature);
            }

            //return the list of linear features prepared in this routine.
            return linearFeatures;
        }
예제 #6
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;
        }
예제 #7
0
        //////////////////////////////////////
        //all the action occurs here
        //////////////////////////////////////
        private void prepareKML()
        {
            /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // this procedure is called immediately after we have clicked the BROWSE button and selected the input file
            /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            //filestream to create the files where we will write some summary output (debug: KMLDebugFile)
            FileStream fs1;
            try
            {
                //debug output data
               fs1 = new FileStream(datasetFolder + "\\KMLDebugFile.txt", FileMode.Create, FileAccess.Write, FileShare.None);
               outfile = new StreamWriter(fs1);
               outfile.AutoFlush = true;
            }
            catch { MessageBox.Show("Cant open the KMLDebugFile file "); }

            //Constants.Log -- writes the the RTF on the main page of the GUI
            Constants.Log("File opened for writing log information:  \n" + datasetFolder + "\\KMLDebugFile.txt");

            //First determine whether the mission is a linear or area mission
            //In Google Earth -- use the Polygon tool for the polygon and the Path tool for the linear feature
            //can detect these by inspecting the input KML
            //Path has only a LineString tag and coordonate tag
            //Polygon has a Polygon tag, OuterBoundaryIs tag, linearRing tag, coordinates tag

            //get the lat/lon client data and other info from the input kml file
            InspectKMLforMissions kml = new InspectKMLforMissions(kmlInputFilename, datasetFolder, outfile);

            //generate polygon or linearFeature geometry information and further populate the job definition structure
            //convert geodetic to UTM coordinates, compute area, bounding box, etc ...
            COVERAGE_TYPE polyCoverageType_polygonOrLinear = kml.polygonGeometry();

            //expand the poly boundary for insurance -- destroying the original polygon
            if (polyCoverageType_polygonOrLinear == COVERAGE_TYPE.polygon)
            {
                //original lat-lon poonts saved in originalLatitudeDeg, originalLongitudeDeg within poly description
                double borderWidth = 10;  //probably should be adaptive to 1/2 the swath width !!
                kml.expandPolygonVertices(borderWidth);  //expand the client poly vertices with a border for insurance
            }

            //get the airports that are close to the Job polygons
            kml.getAirports();

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////
            //We plan to 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
            //we will use the polygon.name for the output kml file --- that becomes the flight plan for the flight computer
            /////////////////////////////////////////////////////////////////////////////////////////////////////////////

            //retrieve a local copy of the Project Polygons from the client-input kml
            //shared for the polygon and linear feature coverage
            polys = new List<Polygon>();  //do we need this statement ???
            polys = kml.getPolygonsFromKML();

            //must see if the input kml file and the polygon.name are the same.
            //if polygon name and inut polygon name are the same, the input file will be deastroyed (over-written)
            //if names are the same:  add "_input" to the original name
            DirectoryInfo kmlInfo = new DirectoryInfo(kmlInputFilename);
            String inputFilenameWOextension = Path.GetFileNameWithoutExtension(kmlInputFilename);
            if (inputFilenameWOextension == polys[0].PolygonName)
            {
                MessageBox.Show("Google Earth placemark name same as input kml filename. \n" +
                         "Input kml name changed by appending  _input  at the end");
                String newName = Path.GetDirectoryName(kmlInputFilename) + "\\" + inputFilenameWOextension + "_input.kml";
                System.IO.File.Move(kmlInputFilename, newName);
                Constants.Log("");
                Constants.Log("The input kml fileName and the Google Earth Placemark name are the same");
                Constants.Log("The input kml filename has been changed by adding _input to its fileName");
                Constants.Log("The prepared mission plan will have the name of the original kml file");
                Constants.Log("Use the new kml filename (with _input) on any new flight planning exercise");
            }

            ////////////////////////////////////////////////////////////////////////////////////
            //at this point, we have the polygon selected as well as the nearby airports
            ////////////////////////////////////////////////////////////////////////////////////

            /////////////////////////////////////////////////////////////////
            //Cycle through all the Project Polygons
            // for now -- we only allow a single poly at a time
            //however, the kml read program allows mpre than one at a time
            //this would be useful for clients with a lot of polygon areas
            ////////////////////////////////////////////////////////////////
            //for (int totIt=0; totIt<4; totIt++)
            {
                //disable the browse button after the input kml selection has been completed
                button1.Visible = false;
                button1.Enabled = false;

                Polygon p = polys[0];

                //we need to display the airports and allow the user to select a project airport.
                //show the airport list in a datGridView and wait for the user to select the correct airport.
                foreach (airport apt in p.airports) this.dataGridView1.Rows.Add(apt.name, apt.distanceToPolyCenterMi.ToString("F1"));
                Constants.Log(" ");
                Constants.Log("Please select a takeoff airport from the dropdown list ");

                this.dataGridView1.Enabled = true;
                this.dataGridView1.Visible = true;
                this.dataGridView1.CurrentCell.Selected = false;

                this.label2.Enabled = true;
                this.label2.Visible = true;
                //wait here while the user selects the takeoff airport
                //the selected airport is given:  selectedAirportIndex
                while (!airportSelected)
                {
                    Application.DoEvents();
                }
                Constants.Log("Selected airport:  " + p.airports[selectedAirportIndex].name);

                Constants.Log("   ");
                Constants.Log("Please  select a camera .... ");

                this.listBox2.Enabled = true;
                this.listBox2.Visible = true;
                this.label1.Enabled = true;
                this.label1.Visible = true;

                //wait here for the user to select a camera
                while (!cameraSelected)
                {
                    Application.DoEvents();  //wait for user inputs
                }
                String cameraType = this.listBox2.SelectedItem.ToString();

                Constants.Log("   ");
                Constants.Log("Please  select a resolution (cm) .... ");

                this.listBox3.Enabled = true;
                this.listBox3.Visible = true;
                this.label3.Enabled = true;
                this.label3.Visible = true;

                //wait here for the user to select a resolution
                while (!ResolutionSelected)
                {
                    Application.DoEvents();  //wait for user inputs
                }

                //no need to select max flight line for a linear feature path coverage
                if (p.polyCoverageType == COVERAGE_TYPE.polygon)
                {
                    Constants.Log("   ");
                    Constants.Log("Please  select a max Flightline Length (nm) .... ");

                    this.listBox4.Enabled = true;
                    this.listBox4.Visible = true;
                    this.label4.Enabled = true;
                    this.label4.Visible = true;

                    //wait here for the user to select a max flight line length
                    while (!maxFLSelected)
                    {
                        Application.DoEvents();  //wait for user inputs
                    }

                    //no need to select max mission time for a linear feature path coverage
                    //assume the user will make the linear features of flyable lengths
                    Constants.Log("   ");
                    Constants.Log("Large coverages may require multiple flight missions. ");
                    Constants.Log("Please  select a maximum per-mission ferry+overtarget flight time (hrs) .... ");

                    this.listBox6.Enabled = true;
                    this.listBox6.Visible = true;
                    this.label6.Enabled = true;
                    this.label6.Visible = true;

                    //wait here for the user to select a max mission time
                    while (!maxMissionTimeSelected)
                    {
                        Application.DoEvents();  //wait for user inputs
                    }
                }
                else
                {
                    this.panel1.Enabled = true;
                    this.panel1.Visible = true;
                    radioButton1.Checked = false;
                    radioButton2.Checked = false;
                    radioButton3.Checked = false;
                    //wait here for the user to select the number of prlallel paths
                    while (!linearFeaturePathsSelected ||
                        (radioButton1.Checked == false &&
                        radioButton2.Checked == false &&
                        radioButton3.Checked == false)  )

                    {
                        Application.DoEvents();  //wait for user inputs
                    }
                    if      (radioButton1.Checked == true) {linearPathCentering = LINEAR_PATH_CENTERING.centeredOnInputPath; }
                    else if (radioButton2.Checked == true) {linearPathCentering = LINEAR_PATH_CENTERING.offsetToLeftOfInputPath; }
                    else if (radioButton3.Checked == true) {linearPathCentering = LINEAR_PATH_CENTERING.offsetToRightOfInputPath; }
                }

                Constants.Log("   ");
                Constants.Log("Please  select a nominal aircraft speed (knots) .... ");

                this.listBox5.Enabled = true;
                this.listBox5.Visible = true;
                this.label5.Enabled = true;
                this.label5.Visible = true;

                //wait here for the user to select a aircraft speed
                while (!nominalSpeedSelected)
                {
                    Application.DoEvents();  //wait for user inputs
                }

                p.selectedAirportIndex = selectedAirportIndex;

                //////////////////////////////////////////////////////////////////////////
                //at this point, we have completed all the user's initial inputs.
                //next we compute the performance for the three coverage types

                ////////////////SETUP PARAMETERS///////////////////////////////////////////////////////////////////
                ///  specific setup for the Canon 5D rotated 90 deg (long side downrange)
                //double flightAlt = 3500.0; //5,000 provides about 6" resolution  -- !!!!!!!!!!this is assumed to be agl!!!!!!!!!!!
                //compute the flight line spacing from the camera FOV and altitude
                double CRFOV = 27.5;    //degrees crossrange FOV (rotated 5D with 50m lenses -- GeoPod)
                double DRFOV = 39.0;      //degrees downrange FOV (rotated 5D with 50mm lens)
                double downlap = 60;    //percent downlap X 100 -- 60
                double sideLap = 30.0;  //percent sidelap X 100
                int cameraPixels = 3744;  //pixels in crossrange  (rotated canon 5D)

                //max FL length is in nautical miles
                double maxFLLengthMi = maxFLLengthNM  * 1.15078;  //convert nautical miles to statute miles

                double timeToTurn = 1.0 / 60.0; //hours   -- about 2 min -- this is conservative 1.5 is typical
                double aircraftSpeedKnots = nominalSpeed; // 100.0;

                double takeoffTime = 5.0 / 60.0;    //time to roll/run-up on the runway turing the takeoff event -- added to the ferry time
                double landingTime = 5.0 / 60.0;    //time in pattern & taxi roll on the runway turing the landing event -- added to the ferry time

                //we attempt to merge small lines (below minLineLength) with prior lines
                double minLineLength = 8.0;   //minimum length of a flight line segment (units km)

                ///////////////////////////////////////////////////////////////////////////////////////////////////////////

                //derived setup parameters/////////////////////////////////////////////////////////////////////////////////////////////////
                //double swathWidth = 2.0 * Math.Tan(CRFOV * Constants.Deg2Rad / 2.0) * flightAlt;  //in feet
                //double GSD = swathWidth / cameraPixels * 0.3048;  //GSD im meters

                double swathWidthMeters = resolutionCm * cameraPixels / 100.0;
                double flightAlt = swathWidthMeters / (2.0 * Math.Tan(CRFOV * Constants.Deg2Rad / 2.0)) / 0.3048;

                double swathWidthWithSidelap = (1.0 - sideLap / 100.0) * swathWidthMeters; //in meters
                double swathWidthKm = swathWidthWithSidelap / 1000.0;  //Km

                //set the max flight line length
                double maxFLLengthKm = maxFLLengthMi * 5280.0 * 0.3048 / 1000.0;
                outfile.WriteLine();
                outfile.WriteLine("Input flight line length is: " + maxFLLengthMi.ToString("F1") + " (mi)  " + maxFLLengthKm.ToString("F1") + "( km)" );

                //downrange image spacing including downlap
                //below used to compute the number of images per flight line
                double DRImageHeightWithDownlap = (1.0 - downlap / 100.0) * 2.0 * Math.Tan(DRFOV * Constants.Deg2Rad / 2.0) * flightAlt; //in feet

                //same as above but in km -- distance downrange between each image trigger
                double DRImageHeightKm = DRImageHeightWithDownlap * 0.3048 / 1000.0;  //Km

                double aircraftSpeedKPH = aircraftSpeedKnots * 0.514 / 1000 * 3600.0;  //0.514 m/sec per knot -- 100 knots = 51.4 m/s

                double forwardSpeedInClimb = 0.70 * aircraftSpeedKPH;  //limit the forward speed when the aircraft is climbing to altitude
                double avgClimbRate = 500.0;   //ft per minute -- climb rate
                double minimumFerryTime = (flightAlt / avgClimbRate) / 60.0;     //time to climb to altitude
                double distanceDuringClimb = forwardSpeedInClimb * minimumFerryTime;            //horizontal distance traveled during the climb to altitude
                //note -- this can make a flight line exceed the max length -- but not by too much.
                //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                ///////////////////////////////////////////////////////////////////////////////
                //at this point, we have the camera parameters
                //and all the coordinates of the polygon or linear path
                ///////////////////////////////////////////////////////////////////////////////

                //compute the LinearFeature coverage --- The below procedure will terminate the Planner
                if (p.polyCoverageType == COVERAGE_TYPE.linearFeature)
                {
                    LinearFeaturePlanner(p, flightAlt, swathWidthKm, aircraftSpeedKnots, aircraftSpeedKPH, DRImageHeightKm);
                }

                //////////////////////////////////////////////////////////////////////////
                //we will get to the below functionality only for the polygon planner
                //////////////////////////////////////////////////////////////////////////

                //now determine if we prefere a NS or EW mission plan
                //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;

                btnNS_EW.Visible = true;
                btnNS_EW.Enabled = true;

                int numSets = 0;
                int numFlightLines = 0;
                int maxSets = 20;
                int maxFlightLines = 200;
                double[, ,] XAllSets = new double[2, maxSets, maxFlightLines];
                double[, ,] YAllSets = new double[2, maxSets, maxFlightLines];

                ///////////////////////////////////////////////////////////////////////////////////////////
                //get the polygon coverage for three types of coverage and present the results to the user
                ///////////////////////////////////////////////////////////////////////////////////////////
                COVERAGE_SUMMARY covSummary1 =
                    getPolygonFlightPathCoverage(0.0,                               //North-South case
                            p, swathWidthKm, maxFLLengthKm, minLineLength, DRImageHeightKm,
                            ref numSets, ref numFlightLines, ref XAllSets, ref YAllSets);

                COVERAGE_SUMMARY covSummary2 =
                    getPolygonFlightPathCoverage(90.0 * Constants.Deg2Rad,          //East-West case
                            p, swathWidthKm, maxFLLengthKm, minLineLength, DRImageHeightKm,
                            ref numSets, ref numFlightLines, ref XAllSets, ref YAllSets);

                COVERAGE_SUMMARY covSummary3 =                                      //principal axis case
                    getPolygonFlightPathCoverage((180.0-p.rotationAngleDeg) * Constants.Deg2Rad,
                            p, swathWidthKm, maxFLLengthKm, minLineLength, DRImageHeightKm,
                            ref numSets, ref numFlightLines, ref XAllSets, ref YAllSets);
                double flightTime1 = covSummary1.totalFlightLineLength / aircraftSpeedKPH + (covSummary1.numTotalFlightLines - 1) * timeToTurn;
                double flightTime2 = covSummary2.totalFlightLineLength / aircraftSpeedKPH + (covSummary2.numTotalFlightLines - 1) * timeToTurn;
                double flightTime3 = covSummary3.totalFlightLineLength / aircraftSpeedKPH + (covSummary3.numTotalFlightLines - 1) * timeToTurn;

                //display the results to the user and allow user to select from among the types of coverage
                Constants.Log("");
                Constants.Log(" Coverage Options: ");
                Constants.Log("      Type                  #FL      AvgFL (km)        TotalLength (km)    Time (hrs)  ");
                String fmt1 = String.Format("NorthSouth       \t{0,5}      \t{1,8:##.00}       \t{2,10:###.00}        \t{3,6:#.00}",
                    covSummary1.numTotalFlightLines, covSummary1.averageFlightLineLength, covSummary1.totalFlightLineLength, flightTime1);
                Constants.Log(fmt1);
                String fmt2 = String.Format("EastWest         \t{0,5}       \t{1,8:##.00}       \t{2,10:###.00}        \t{3,6:#.00}",
                    covSummary2.numTotalFlightLines, covSummary2.averageFlightLineLength, covSummary2.totalFlightLineLength, flightTime2);
                Constants.Log(fmt2);
                String fmt3 = String.Format("PrincipalAxis    \t{0,5}       \t{1,8:##.00}       \t{2,10:###.00}         \t{3,6:#.00}",
                    covSummary3.numTotalFlightLines, covSummary3.averageFlightLineLength, covSummary3.totalFlightLineLength, flightTime3);
                Constants.Log(fmt3);
                outfile.WriteLine();
                outfile.WriteLine("Results of three types of coverage ");
                outfile.WriteLine(fmt1);
                outfile.WriteLine(fmt2);
                outfile.WriteLine(fmt3);

                //select the recommended coverage option from the three candidates
                if (flightTime1 < flightTime2 && flightTime1 < flightTime3)
                    { btnNS_EW.Text = "North-South";    polyCoverageType = PolygonCoverageType.South2North; };
                if (flightTime2 < flightTime1 && flightTime2 < flightTime3)
                    { btnNS_EW.Text = "East-West";      polyCoverageType = PolygonCoverageType.East2West; };
                if (flightTime3 < flightTime1 && flightTime3 < flightTime2)
                    { btnNS_EW.Text = "PrincipleAxis";  polyCoverageType = PolygonCoverageType.PrincipalAxis; };

                //enable the missionPlan startup 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
                }

                //redo  the polygon coverage based on the user selection  --
                //need to save the old plans so we dont have to redo!!
                if ( polyCoverageType == PolygonCoverageType.South2North)
                    getPolygonFlightPathCoverage(0.0,                               //North-South case
                            p, swathWidthKm, maxFLLengthKm, minLineLength, DRImageHeightKm,
                            ref numSets, ref numFlightLines, ref XAllSets, ref YAllSets);
                else if (polyCoverageType == PolygonCoverageType.East2West)
                    getPolygonFlightPathCoverage(90.0 * Constants.Deg2Rad,          //East-West case
                            p, swathWidthKm, maxFLLengthKm, minLineLength, DRImageHeightKm,
                            ref numSets, ref numFlightLines, ref XAllSets, ref YAllSets);
                else if (polyCoverageType == PolygonCoverageType.PrincipalAxis)
                    getPolygonFlightPathCoverage((180.0-p.rotationAngleDeg) * Constants.Deg2Rad,
                            p, swathWidthKm, maxFLLengthKm, minLineLength, DRImageHeightKm,
                            ref numSets, ref numFlightLines, ref XAllSets, ref YAllSets);

                //this is placed here just in case the user has changed his mind and re-clicked a different airport
                p.selectedAirportIndex = selectedAirportIndex;

                GoogleMaps gglmaps = new GoogleMaps(datasetFolder, p.PolygonName);
                gglmaps.getGoogleMapToCoverProject(ref p, downloadMap);  //insert the Google map from the web into this polygon

                /////////////////////////////////////////////////////////////////////////////////
                bool replannerTool = false;     //set true for a replanner tool
                /////////////////////////////////////////////////////////////////////////////////

                //this is for a replanner tool
                // what does this do ?????
                //supposed to allow visual editing of the Quick-Look and replanning the mission to cover clouds & shadows
                List<endPoints> reflyLines = null;
                ReflyPlanner refly;

                if (replannerTool)
                {
                    refly = new ReflyPlanner(@"E:\Melbourne\Melbourne_NEW_005\QLData\refly_005.kml", p);
                    MessageBox.Show("refly tool is in effect");
                    //should have a way to indicate which photocenters are to be reflown
                    reflyLines = refly.getReflySegments();
                }
                /////////////////////////////////////////////////////////////////////////////////

                //open the file to write the kml Project results
                kmlOutputFilename = datasetFolder + "\\" + p.PolygonName + ".kml";

                //NOTE: this will fail if the input client polygon (.kml) is the same as the desired output 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;

                //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                //at this point, we have a 2D table YAllSets[numsets][numFlightLines] that describes the intended flightlines and lengths
                //however, some of these have zero length because of the irregular shape of the polygon
                //Moreover, we have not considered the max endurance -- so we must collect sequentual flightlines into flyable missions
                //Below we convert the 2D structure to a 1D structure containing only flightlines to be flown
                //we also assign the flightlines to individual missions
                //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                outfile.WriteLine();
                outfile.WriteLine("Now have the set of flight lines by set");
                outfile.WriteLine("Next break up these flight lines into time-constrained groupings to get a mission");
                outfile.WriteLine();

                Constants.Log("Accumulate flight lines into missions based on max mission length");

                //go through the flightlines for a set and determine the total time-over-the-covered area for the complete Set
                //If this is > 2 hrs, then break the set into ~equal Area Of Interest (AOI) flight sessions "AOITimeMax".
                //find the start-end flightline for AOIs so the accumulated AOITime < AOITimeMax  -- except the last AOI may be larger

                List<MissionSummary> MissionSumA = new List<MissionSummary>();
                //set up for the polygon math
                polygonMath polyMath = new polygonMath(p.Easting, p.Northing, datasetFolder);

                double totalFlightlineDistance = 0;
                double flightlineDistanceThisMission = 0;
                int missionNumber = 0;
                int totalImages = 0;
                int totalImagesThisMission = 0;
                int startFlightlineThisMission = 0;
                double sumMissionLat = 0.0;  //used to compute the average mission center (average lat/lon)
                double sumMissionLon = 0.0;
                double totalFerryTime = 0.0;
                double totalTimeToTurn = 0.0;
                double totalTimeOverTarget = 0.0;

                double latStart = 0, lonStart = 0;
                double latEnd = 0, lonEnd = 0;
                int jApt = 0;  //airport counter
                double ferryTime = 0.0;
                double oldFerryTime = 0.0;
                int numberFlightlines = 0;

                outfile.Flush();

                double avgMissionLat=0.0; double avgMissionLon=0.0;

                int numFL = 0;  //numFL becomes the sequential flight line counter for the 1D array that will persist across the project
                for (int i = 0; i < numSets; i++)
                {

                    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                    //compute the max allowable AOITime for this Set
                    double maxAOITime = maxMissionTimeHrs; //maxAOITimeGlobal;
                    outfile.WriteLine("Input Maximum Mission time is set to " + maxMissionTimeHrs.ToString());

                    double totalFlightlineDistancePerSet = 0; int numFLthisSet=0;
                    int lastNonZeroFLThisSet = 0;
                    //precompute the total time to cover the flight lines in this set
                    for (int j = 0; j < numFlightLines; j++)
                    {
                        double FLLength = 0;
                        double delEasting  = XAllSets[1, i, j] - XAllSets[0, i, j];
                        double delNorthing = YAllSets[1, i, j] - YAllSets[0, i, j];
                        FLLength = Math.Sqrt(delEasting*delEasting + delNorthing * delNorthing);

                        if (FLLength > 0)
                        {
                            numFLthisSet++;
                            totalFlightlineDistancePerSet += FLLength;

                            lastNonZeroFLThisSet = j;
                        }

                    }
                    //total time for this set
                    double totalAOITime = (totalFlightlineDistancePerSet / 1000.0) / aircraftSpeedKPH + (numFLthisSet - 1) * timeToTurn;

                    double numberOfAOIsThisSet = Math.Floor(totalAOITime / maxAOITime) + 1.0;
                    //adjust the maxAOITime so that it is <= to the input maxAOITime but equal for all missions within this set
                    maxAOITime = totalAOITime / numberOfAOIsThisSet;
                    outfile.WriteLine();
                    outfile.WriteLine("Number of missions this set = " + numberOfAOIsThisSet.ToString());
                    outfile.WriteLine("Adjusted Maximum Mission time to enforce equal-size missions this set " + maxAOITime.ToString("F1"));
                    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                    //max allowable is now <= the initial max allowable -- reduced to have neary equal sized AOIs within the set.
                    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                    int AOIsThisSet = 0;

                    ////////////////////////////////////////////////////////////////////////////////////
                    //now go back through the flight lines and break into groupings of flight lines
                    //this will define the flyable missions with a prescribed time-over-target threshold
                    ////////////////////////////////////////////////////////////////////////////////////
                    for (int j = 0; j < numFlightLines; j++)
                    {
                        //compute the flight line length using the north and south intersection extents
                        //YAllSets[1, i, j] is the flight line "end" -- the larger Northing value (i.e., northmost end)
                        double flightLineDistanceKm = 0;
                        double delEasting = XAllSets[1, i, j] - XAllSets[0, i, j];
                        double delNorthing = YAllSets[1, i, j] - YAllSets[0, i, j];
                        flightLineDistanceKm = Math.Sqrt(delEasting * delEasting + delNorthing * delNorthing) / 1000.0;

                        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                        if (flightLineDistanceKm == 0 ) continue;  //skip the below computations id the terminated flight line has zero length
                        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                        //number of flightlines this mission is current flight line (numFL) minus start flight line
                        numberFlightlines = numFL - startFlightlineThisMission + 1;  //num of flight lines this mission

                        //sum the total flight line distances
                        //images this flight line based on the downrange image extent
                        int imagesThisFlightline = Convert.ToInt32(Math.Floor(flightLineDistanceKm / DRImageHeightKm)  + 1.0);

                        //per-mission and total travel distances and num of images
                        totalFlightlineDistance += flightLineDistanceKm;
                        flightlineDistanceThisMission += flightLineDistanceKm;
                        totalImages += imagesThisFlightline;
                        totalImagesThisMission += imagesThisFlightline;

                        //transfer the start/end UTM coordinates back to the LatLon for use in the kml file
                        //Y is northing and X is easting
                        utm.UTMtoLL(YAllSets[0, i, j], XAllSets[0, i, j], p.UTMZone, ref latStart, ref lonStart);
                        utm.UTMtoLL(YAllSets[1, i, j], XAllSets[1, i, j], p.UTMZone, ref latEnd, ref lonEnd);

                        FLSum[numFL].planned.startUTMX = XAllSets[0, i, j];
                        FLSum[numFL].planned.startUTMY = YAllSets[0, i, j];
                        FLSum[numFL].planned.endUTMX = XAllSets[1, i, j];
                        FLSum[numFL].planned.endUTMY = YAllSets[1, i, j];

                        //fill the flightline summary structure
                        FLSum[numFL].planned.startLat = latStart;
                        FLSum[numFL].planned.startLon = lonStart;
                        FLSum[numFL].planned.endLat = latEnd;
                        FLSum[numFL].planned.endLon = lonEnd;

                        FLSum[numFL].numImages = imagesThisFlightline;

                        FLSum[numFL].lineNumberInPolygon = numFL;
                        FLSum[numFL].flightlineLengthMi = flightLineDistanceKm * 1000.0 / 0.3048 / 5280.0;
                        FLSum[numFL].FLstat = fightlineStatus.plan;

                        //sums to be used to compute the average mission center
                        sumMissionLat += (FLSum[numFL].planned.startLat + FLSum[numFL].planned.endLat) / 2.0;
                        sumMissionLon += (FLSum[numFL].planned.startLon + FLSum[numFL].planned.endLon) / 2.0;

                        ///////////////////////////////Closest Airport///////////////////////////////////////////////////////////////////
                        //average mission lat/lon for use in getting the ferry distance
                        //we keep a running sum to get the avg as we add flight lines to the mission
                        avgMissionLat = sumMissionLat / numberFlightlines;
                        avgMissionLon = sumMissionLon / numberFlightlines;

                        /*
                        //closest airport and ferry time to average mission location
                        //test all candidate airports and use the closest to the mission center
                        double minAptDistance = 99999999999999.0;
                        for (int jA = 0; jA < p.airports.Count; jA++)
                        {
                            double delLat = p.airports[jA].lat - avgMissionLat;
                            double delLon = p.airports[jA].lon - avgMissionLon;
                            double dist = Math.Sqrt(delLat * delLat + delLon * delLon);
                            if (dist < minAptDistance) { minAptDistance = dist; jApt = jA; }  //store the index of the closest airport
                        }
                        */

                        double aptNorthing = 0.0, aptEasting = 000, avgNorthing = 0.0, avgEasting = 0.0;
                        //convert the airport lat/Long (LL) coordinates to UTM for distance measurement
                        utm.LLtoUTM(p.airports[p.selectedAirportIndex].lat * Constants.Deg2Rad, p.airports[p.selectedAirportIndex].lon * Constants.Deg2Rad,
                            ref aptNorthing, ref aptEasting, ref p.UTMZone, true);
                        utm.LLtoUTM(avgMissionLat * Constants.Deg2Rad, avgMissionLon * Constants.Deg2Rad, ref avgNorthing, ref avgEasting, ref p.UTMZone, true);
                        //delta N and E distances in Miles
                        double dn = (aptNorthing - avgNorthing) / 1000.0;
                        double de = (aptEasting - avgEasting) / 1000.0;

                        //ferry distance one-way -- approximation cause the ferry time really to the start of the first line of this mission
                        double ferryDistanceKm = Math.Sqrt(dn * dn + de * de);

                        oldFerryTime = ferryTime; // save the last ferry time --- new ferry time assuming we keep the current mission is below
                        if (ferryDistanceKm > distanceDuringClimb) //account for the travel portion while climbing to altitude
                            ferryTime = minimumFerryTime + (ferryDistanceKm - distanceDuringClimb) / aircraftSpeedKPH +
                                ferryDistanceKm / aircraftSpeedKPH + takeoffTime + landingTime;
                        else  //closest airport is inside the mission area -- climb time is purely addidive to the flight time
                            ferryTime = minimumFerryTime + ferryDistanceKm / aircraftSpeedKPH + takeoffTime + landingTime;
                        /////////////////////////////////Closest AIRPORT////////////////////////////////////////////////////////////////

                        numFL++;  //number of flight lines that have been investigated

                        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                        //separate the flight lines into multiple missions by asking: If we add this flight line do we exceed the aircraft endurance?
                        //also do this for the last flightline in each Set -- and the very last flight line.
                        //note -- on the first entry, we havent computed a ferry time (its zero) but its computed below

                        double expectedAOITime = (flightlineDistanceThisMission) / aircraftSpeedKPH + (numberFlightlines - 1) * timeToTurn;

                        outfile.WriteLine(String.Format(" New flightline: {0,2} length {1,10:#.00} (mi) accumulated Time = {2,10:#.00}",
                            numFL, flightLineDistanceKm * 1000.0 / 0.3048 / 5280.0, expectedAOITime));
                        outfile.Flush();

                        //NOTE: expectedAOITime includes the current line being tested
                        //will we allow each AOI to be slightly larger than maxAOITime (by one flight line) or force each AOI to be smaller by one flight line?
                        //for the first case, we will end up with a short AOI at the end -- for the second case, we end up with a long AOI at the end.
                        //at the very end, we always want to set the prior lines in this Set as an AOI

                        //first part of the "if" test tests to see if adding this flight line exceeds the maxAOITime.
                        //note that this test is removed if we are at the last AOI within a set. This allows the last AOI to be established
                        //even though the AOITime is below the maxAOITime. The second part of the "if" test triggers the AOI at the very last nonzero flightline.

                        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                        if ((expectedAOITime > maxAOITime && AOIsThisSet != (numberOfAOIsThisSet-1) ) || (j == lastNonZeroFLThisSet))
                        {

                            AOIsThisSet++;

                            //numFL is the total number of flight lines that have been processed below
                            //numFL-1 is the flightline ID of the last fight line that was processed
                            //numFL will be the next flight line process so is the start line of the next mission

                            //below logic accounts for the potential staggered start and ends of flight lines
                            //these computations assume you start at the west headed north and turn back south after each north-going line, alternating north/south directions
                            double extendedTravelToNextLine = 0; // accounts for the distance to travel to the start of the next flight line
                            for (int k = startFlightlineThisMission; k < (numFL-2) ; k += 2)
                            {
                                //account for extended line going north and turning south  (end is at the north)
                                extendedTravelToNextLine += Math.Abs(FLSum[k].planned.endUTMX - FLSum[k + 1].planned.endUTMX) / 1000.0;
                                //account for extended line going south and turning north (start is at the south)
                                extendedTravelToNextLine += Math.Abs(FLSum[k + 1].planned.startUTMX - FLSum[k + 2].planned.startUTMX) / 1000.0;
                            }

                            flightlineDistanceThisMission += extendedTravelToNextLine;

                            totalTimeToTurn += (numberFlightlines - 1) * timeToTurn;

                            //Store the mission summary data into a structure and add to the ArrayList Collection
                            MissionSummary missionSum = new MissionSummary();
                            missionSum.missionPolygon = new List<PointD>();  // this is a geodetic polygon formed from the flight line endpoints
                            missionSum.missionNumber = missionNumber;   //incremental index of the missions
                            missionSum.startFlightLine = startFlightlineThisMission; //starting flight line for this mission
                            missionSum.FerryTime = ferryTime;   //two-way ferry --- computed below
                            missionSum.LengthMi = flightlineDistanceThisMission * 1000.0 / 0.3048 / 5280.0;
                            missionSum.numberFlightlines = numberFlightlines;  //
                            missionSum.TimeOverTarget = flightlineDistanceThisMission / aircraftSpeedKPH + (numberFlightlines - 1) * timeToTurn;
                            totalTimeOverTarget += missionSum.TimeOverTarget;
                            missionSum.MissionStat = MissionStatus.plan;
                            missionSum.takeoffAirport = p.airports[jApt];
                            missionSum.totalImages = totalImagesThisMission;
                            missionSum.backgroundImageNWCorner = new PointD(0.0, 0.0);
                            missionSum.backgroundImageSECorner = new PointD(0.0, 0.0);
                            outfile.WriteLine(String.Format(" new misson:  {0,2} startFlightLine {1,3} numFlightLines {2,3} flightlineDistance {3,10:#.00} (Mi) ferryTime {4,5:#.00} (hr) ",
                                 missionSum.missionNumber, missionSum.startFlightLine, missionSum.numberFlightlines, missionSum.LengthMi, missionSum.FerryTime));

                            missionSum.backgroundImageFilename = "NA";
                            missionSum.setNumber = i;

                            if (!replannerTool)
                            {
                                //form a polygon of the mission envelope based upon the flight line endpoints.
                                //I dont see where these UTM endpoints are used!!!
                                List<double> EastingMsn = new List<double>(); List<double> NorthingMsn = new List<double>();
                                //go around the flight line endpoints beginning at the south ends and moving counterclockwise
                                for (int ifl = startFlightlineThisMission; ifl < (startFlightlineThisMission + numberFlightlines); ifl++)
                                {
                                    EastingMsn.Add(FLSum[ifl].planned.startUTMX);
                                    NorthingMsn.Add(FLSum[ifl].planned.startUTMY);
                                    missionSum.missionPolygon.Add(new PointD(FLSum[ifl].planned.startLon, FLSum[ifl].planned.startLat));
                                }
                                //add the north ends starting at the westmost lines so the poly forms a sequential set of points counterclockwise
                                for (int ifl = (startFlightlineThisMission + numberFlightlines - 1); ifl > startFlightlineThisMission - 1; ifl--)
                                {
                                    EastingMsn.Add(FLSum[ifl].planned.endUTMX);
                                    NorthingMsn.Add(FLSum[ifl].planned.endUTMY);
                                    missionSum.missionPolygon.Add(new PointD(FLSum[ifl].planned.endLon, FLSum[ifl].planned.endLat));
                                }
                                //add one more point back at the start to create a linesegment to close the polygon
                                missionSum.missionPolygon.Add(new PointD(FLSum[startFlightlineThisMission].planned.startLon, FLSum[startFlightlineThisMission].planned.startLat));
                            }
                            else
                            {
                                //form the mission polygon from the flight line bounding boxes
                                double maxLat = -99999.0, maxLon = -99999.0;
                                double minLat =  99999.0, minLon =  99999.0;
                                foreach (endPoints ep in reflyLines)
                                {
                                    if (ep.startLat > maxLat) maxLat = ep.startLat;
                                    if (ep.endLat > maxLat) maxLat = ep.endLat;
                                    if (ep.startLon > maxLon) maxLon = ep.startLon;
                                    if (ep.endLon > maxLon) maxLon = ep.endLon;
                                    if (ep.startLat < minLat) minLat = ep.startLat;
                                    if (ep.endLat < minLat) minLat = ep.endLat;
                                    if (ep.startLon < minLon) minLon = ep.startLon;
                                    if (ep.endLon < minLon) minLon = ep.endLon;
                                }
                                missionSum.missionPolygon.Add(new PointD(minLon-0.01, maxLat+0.01));
                                missionSum.missionPolygon.Add(new PointD(maxLon+0.01, maxLat+0.01));
                                missionSum.missionPolygon.Add(new PointD(maxLon+0.01, minLat-0.01));
                                missionSum.missionPolygon.Add(new PointD(minLon-0.01, minLat-0.01));
                                missionSum.missionPolygon.Add(new PointD(minLon-0.01, maxLat+0.01));
                            }

                            //mission center/average lat/lon is computed below this statement
                            missionSum.missionCenter = new PointD(avgMissionLat, avgMissionLon);

                            outfile.Flush();

                            //////////// add mission summary  to the ArrayList collection of missions///////////////////////////////
                            MissionSumA.Add(missionSum);

                            totalFerryTime += ferryTime;    //total ferry times across the complete project -- used for the average ferry per mission

                            flightlineDistanceThisMission = 0;  //reset the mission distance summation
                            totalImagesThisMission = 0;         //reset the total images this mission
                            missionNumber++;                    //increment the mission couinter
                            startFlightlineThisMission = numFL; //set the start flight line for the next mission
                            sumMissionLat = 0.0;                //reset the sums used for the current mission lat/lon average
                            sumMissionLon = 0.0;
                        }

                    }
                }

                ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                //cycle back through the missions and get the maps and the average altitude
                //do this in a batch rather than in the per-mission segment above
                //in order to better batch-submit requests to the Google Maps API
                //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                List<PointD> totalPointsInPoly = new List<PointD>();  //a list of Npts points randomly spaced withi each polygon;

                int Npts = 100;  //number of random-spaced points from each mission polygon used to form the average elevation in the mission polygon
                // MissionSumA is the collection of all missions -- this includes the mission polygon that is determined above
                for (int i = 0; i < MissionSumA.Count; i++)
                {
                    ////////////////////////////////////////////////////////////
                    // not quite sure why we do the intermediate storage !!!
                    ////////////////////////////////////////////////////////////
                    MissionSummary missionSum = MissionSumA[i];

                    //get the map of the mission area
                    gglmaps.createBackgroundMaps(ref missionSum, FLSum, p.UTMZone, downloadMap);
                    MissionSumA[i] = missionSum;

                    //get a list of all the accumulated Npts points used for all the averages
                    //this is done so we can request the elevation using Google Maps API with a minimum of requests
                    //  getPointsInPoly returns a list of Npts PointD points -- so the below call adds Npts on each call ???
                    for (int ips = 0; ips < Npts; ips++)
                    {
                        totalPointsInPoly.Add(polyMath.getPointsInPoly(Npts, missionSum.missionPolygon)[ips]);
                    }
                }

                //this procedure gets the elevations for the totalPointsInPoly using the Google Elevation API
                //the points are requested using the polyline request method to minimize the number of requests.
                //we pack as many elevation points into each request as possible within the constraints of the URL length
                //The elevation API limits requests to 2500/day and a total of 25000 elevation points per day.
                //so 2500 requests with 100 points per request will hit the limit.
                List<double> heights = polyMath.getTerrainHeightsFromPointList(totalPointsInPoly);

                //go back through the total list of elevations and break the list into missions and get the elevation stats per mission
                for (int i = 0; i < MissionSumA.Count; i++)
                {
                    MissionSummary missionSum = MissionSumA[i];
                    List<double> heightsThisMission = new List<double>();
                    for (int ips = i * Npts; ips < (i + 1) * Npts; ips++) heightsThisMission.Add(heights[ips]);

                    heightsThisMission.Sort();  //used to get the 90 percentile height

                    missionSum.ts.avgTerrainHeight = heightsThisMission.Average();
                    missionSum.ts.terrain90percentileHeight = heightsThisMission[Convert.ToInt32(0.90 * Npts)];
                    missionSum.ts.maxTerrainHeight = heightsThisMission.Max();
                    missionSum.ts.minTerrainHeight = heightsThisMission.Min();
                    ////flight altitude in feet for the pilot display
                    missionSum.FlightAltMSLft = flightAlt + missionSum.ts.terrain90percentileHeight / 0.3048;  //flightAlt is AGL in ft;
                    ////round to the nearest 100 ft
                    missionSum.FlightAltMSLft = (int)(missionSum.FlightAltMSLft / 100.0 + 0.50) * 100.00;

                    MissionSumA[i] = missionSum;
                }

                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                //now we have all the data for the missions.
                //we need to write out the xml text lines that will be inserted into the original kml file.
                //we insert beneath the original Placemark element (after the <Placemark> and form a <Folder> kml structure
                //each element in the folders will contain one mission
                //also each mission folder will contain multiple Placemark elements that represent the flightlines
                //note the <description> <!CDATA[   ..... ]]> .... this is inserted HTML code to format the presentation
                //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                Constants.Log(" Write the mission plan .kml");

                ////////////////////////////////
                //write the kml file
                ////////////////////////////////

                writeKmlPolygonMissonPlan(p,
                        DRImageHeightKm, missionNumber, flightAlt, swathWidthKm,
                        totalFlightlineDistance, totalFerryTime, aircraftSpeedKPH,
                        totalTimeOverTarget, numFL, totalImages, MissionSumA);

                ///////////////////////////////////////////////////////////////
                //the above comprises a complete closed set of .kml elements
                ///////////////////////////////////////////////////////////////

                kmlFlightLines.Flush();

                Constants.Log("");
                Constants.Log("");
                Constants.Log("Normal Termination ");
                Constants.Log("");

                //////////////////////////////////////////////
                //show the END button
                button2.Visible = true;
                button2.Enabled = true;
                //////////////////////////////////////////////

            }

            //Application.Exit();
        }
예제 #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 //////////////////
        }