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;
        }
Beispiel #2
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;
        }