/// <summary>
 /// Convert bounding box from one coordinate system to
 /// another coordinate system.
 /// Converted bounding box is returned as a polygon
 /// since it probably is not a rectangle any more.
 /// </summary>
 /// <param name="clientInformation">Information about the client that makes this web service call.</param>
 /// <param name="boundingBox">Bounding boxe that should be converted.</param>
 /// <param name="fromCoordinateSystem">From coordinate system.</param>
 /// <param name="toCoordinateSystem">To coordinate system.</param>
 /// <returns>Converted bounding box.</returns>
 public WebPolygon GetConvertedBoundingBox(
     WebClientInformation clientInformation,
     WebBoundingBox boundingBox,
     WebCoordinateSystem fromCoordinateSystem,
     WebCoordinateSystem toCoordinateSystem)
 {
     using (WebServiceContext context = GetWebServiceContext(clientInformation))
     {
         try
         {
             return(WebServiceData.CoordinateConversionManager.GetConvertedBoundingBox(
                        boundingBox,
                        fromCoordinateSystem,
                        toCoordinateSystem));
         }
         catch (Exception exception)
         {
             LogException(clientInformation, context, exception);
             LogParameter(context, "BoundingBox", boundingBox.WebToString());
             LogParameter(context, "FromCoordinateSystem", fromCoordinateSystem.WebToString());
             LogParameter(context, "ToCoordinateSystem", toCoordinateSystem.WebToString());
             throw;
         }
     }
 }
        /// <summary>
        /// Load data into the WebBoundingBox instance.
        /// </summary>
        /// <param name="boundingBox">This boundingbox.</param>
        /// <param name='dataReader'>An open data reader.</param>
        /// <param name='columnName'>Name of the column in result set to read data from.</param>
        public static void LoadData(this WebBoundingBox boundingBox,
                                    DataReader dataReader,
                                    String columnName)
        {
            char[] delimiter;
            String boundingBoxString;

            String[] coordinates;

            // Get coordinates.
            boundingBoxString = dataReader.GetString(columnName);
            delimiter         = new[] { Settings.Default.CoordinateDelimiter };
            coordinates       = boundingBoxString.Split(delimiter);
            if (coordinates.Length != 4)
            {
                throw new ApplicationException("Wrong format in bounding box: " + boundingBox);
            }

            // Create points.
            boundingBox.Min   = new WebPoint();
            boundingBox.Max   = new WebPoint();
            boundingBox.Min.X = Double.Parse(coordinates[0], NumberStyles.AllowDecimalPoint, CultureInfo.CreateSpecificCulture("en-GB"));
            boundingBox.Min.Y = Double.Parse(coordinates[1], NumberStyles.AllowDecimalPoint, CultureInfo.CreateSpecificCulture("en-GB"));
            boundingBox.Max.X = Double.Parse(coordinates[2], NumberStyles.AllowDecimalPoint, CultureInfo.CreateSpecificCulture("en-GB"));
            boundingBox.Max.Y = Double.Parse(coordinates[3], NumberStyles.AllowDecimalPoint, CultureInfo.CreateSpecificCulture("en-GB"));
        }
Example #3
0
        /// <summary>
        /// Creates the centre coordinate from bounding box.
        /// </summary>
        /// <param name="boundingBox">The bounding box.</param>
        private static WebPoint CreateCentreCoordinateFromBoundingBox(WebBoundingBox boundingBox)
        {
            WebPoint point = new WebPoint(
                boundingBox.Min.X + ((boundingBox.Max.X - boundingBox.Min.X) / 2.0),
                boundingBox.Min.Y + ((boundingBox.Max.Y - boundingBox.Min.Y) / 2.0));

            return(point);
        }
 /// <summary>
 /// Test if point is located inside bounding box.
 /// Currently only two dimensions are handled.
 /// </summary>
 /// <param name="boundingBox">Bounding box.</param>
 /// <param name='point'>Point.</param>
 /// <returns>True if point is located inside bounding box.</returns>
 public static Boolean IsPointInside(this WebBoundingBox boundingBox,
                                     WebPoint point)
 {
     return(boundingBox.IsNotNull() &&
            point.IsNotNull() &&
            (boundingBox.Max.X >= point.X) &&
            (boundingBox.Min.X <= point.X) &&
            (boundingBox.Max.Y >= point.Y) &&
            (boundingBox.Min.Y <= point.Y));
 }
 /// <summary>
 /// Cast web linearring to linearring
 /// </summary>
 /// <param name="webBoundingBox"></param>
 /// <returns></returns>
 public static IPolygon ToPolygon(this WebBoundingBox webBoundingBox)
 {
     return(new Polygon(new LinearRing(new[]
     {
         new Coordinate(webBoundingBox.Min.X, webBoundingBox.Min.Y),
         new Coordinate(webBoundingBox.Min.X, webBoundingBox.Max.Y),
         new Coordinate(webBoundingBox.Max.X, webBoundingBox.Max.Y),
         new Coordinate(webBoundingBox.Max.X, webBoundingBox.Min.Y),
         new Coordinate(webBoundingBox.Min.X, webBoundingBox.Min.Y)
     })));
 }
        /// <summary>
        /// Gets the sweden extent bounding box in SWEREF99_TM coordinates.
        /// </summary>
        /// <returns>
        /// The sweden extent bounding box in SWEREF99_TM coordinates.
        /// </returns>
        public static WebBoundingBox GetSwedenExtentWebBoundingBoxSweref99()
        {
            WebBoundingBox boundingBox;

            boundingBox       = new WebBoundingBox();
            boundingBox.Max   = new WebPoint();
            boundingBox.Min   = new WebPoint();
            boundingBox.Max.X = 969927.0414948005;
            boundingBox.Max.Y = 7737973.607602615;
            boundingBox.Min.X = 195816.17626999575;
            boundingBox.Min.Y = 6029444.002008331;
            return(boundingBox);
        }
Example #7
0
        /// <summary>
        /// Creates the grid cell bounding polygon from an SQL geometry.
        /// </summary>
        /// <param name="gridCell">The grid cell.</param>
        private static WebBoundingBox CreateGridCellBoundingBoxFromSqlGeometry(SqlGeometry gridCell)
        {
            WebBoundingBox gridCellBoundingBox;

            //Map the gridCell coordinates to the BoundingBox polygon property in the returning object
            gridCellBoundingBox       = new WebBoundingBox();
            gridCellBoundingBox.Min   = new WebPoint();
            gridCellBoundingBox.Max   = new WebPoint();
            gridCellBoundingBox.Min.X = Convert.ToDouble(gridCell.STStartPoint().STX.Value);
            gridCellBoundingBox.Min.Y = Convert.ToDouble(gridCell.STStartPoint().STY.Value);
            gridCellBoundingBox.Max.X = Convert.ToDouble(gridCell.STPointN(3).STX.Value);
            gridCellBoundingBox.Max.Y = Convert.ToDouble(gridCell.STPointN(3).STY.Value);
            return(gridCellBoundingBox);
        }
        /// <summary>
        /// Convert a WebBoundingBox instance into a WebPolygon
        /// instance with same geographic area.
        /// </summary>
        /// <param name="boundingBox">Bounding box.</param>
        /// <returns>A WebPolygon instance.</returns>
        public static WebPolygon GetPolygon(this WebBoundingBox boundingBox)
        {
            WebLinearRing linearRing;
            WebPolygon    polygon;

            linearRing        = new WebLinearRing();
            linearRing.Points = new List <WebPoint>();
            linearRing.Points.Add(new WebPoint(boundingBox.Min.X, boundingBox.Min.Y));
            linearRing.Points.Add(new WebPoint(boundingBox.Max.X, boundingBox.Min.Y));
            linearRing.Points.Add(new WebPoint(boundingBox.Max.X, boundingBox.Max.Y));
            linearRing.Points.Add(new WebPoint(boundingBox.Min.X, boundingBox.Max.Y));
            linearRing.Points.Add(new WebPoint(boundingBox.Min.X, boundingBox.Min.Y));
            polygon             = new WebPolygon();
            polygon.LinearRings = new List <WebLinearRing>();
            polygon.LinearRings.Add(linearRing);
            return(polygon);
        }
        /// <summary>
        /// Check that data is valid.
        /// </summary>
        /// <param name="boundingBox">Bounding box.</param>
        public static void CheckData(this WebBoundingBox boundingBox)
        {
            if (boundingBox.IsNotNull())
            {
                // Check points.
                boundingBox.Max.CheckNotNull("Max");
                boundingBox.Max.CheckData();
                boundingBox.Min.CheckNotNull("Min");
                boundingBox.Min.CheckData();

                // Check that points are of the same dimension.
                if (boundingBox.Max.IsMSpecified !=
                    boundingBox.Min.IsMSpecified)
                {
                    throw new ArgumentException("WebBoundingBox: Min and max has different M dimensions");
                }
                if (boundingBox.Max.IsZSpecified !=
                    boundingBox.Min.IsZSpecified)
                {
                    throw new ArgumentException("WebBoundingBox: Min and max has different Z dimensions");
                }

                // Check that all min values are lower than all max values.
                if (boundingBox.Max.X < boundingBox.Min.X)
                {
                    throw new ArgumentException("WebBoundingBox: Max.X is smaller than Min.X");
                }
                if (boundingBox.Max.Y < boundingBox.Min.Y)
                {
                    throw new ArgumentException("WebBoundingBox: Max.Y is smaller than Min.Y");
                }
                if (boundingBox.Max.IsZSpecified &&
                    (boundingBox.Max.Z < boundingBox.Min.Z))
                {
                    throw new ArgumentException("WebBoundingBox: Max.Z is smaller than Min.Z");
                }
                if (boundingBox.Max.IsMSpecified &&
                    (boundingBox.Max.M < boundingBox.Min.M))
                {
                    throw new ArgumentException("WebBoundingBox: Max.M is smaller than Min.M");
                }
            }
        }
        /// <summary>
        /// Add bounding box 2 to bounding box 1.
        /// The union of bounding box 1 and bounding box 2
        /// are stored in bounding box 1.
        /// </summary>
        /// <param name="boundingBox1">Bounding box 1.</param>
        /// <param name="boundingBox2">Bounding box 2.</param>
        public static void Add(this WebBoundingBox boundingBox1,
                               WebBoundingBox boundingBox2)
        {
            if (boundingBox1.Max.IsNull())
            {
                boundingBox1.Max = boundingBox2.Max.Clone();
            }
            else
            {
                boundingBox1.Max.X = Math.Max(boundingBox1.Max.X, boundingBox2.Max.X);
                boundingBox1.Max.Y = Math.Max(boundingBox1.Max.Y, boundingBox2.Max.Y);
                if (boundingBox1.Max.IsZSpecified != boundingBox2.Max.IsZSpecified)
                {
                    throw new ArgumentException("Different dimensions in points!");
                }
                boundingBox1.Max.Z = Math.Max(boundingBox1.Max.Z, boundingBox2.Max.Z);
                if (boundingBox1.Max.IsMSpecified != boundingBox2.Max.IsMSpecified)
                {
                    throw new ArgumentException("Different dimensions in points!");
                }
                boundingBox1.Max.M = Math.Max(boundingBox1.Max.M, boundingBox2.Max.M);
            }

            if (boundingBox1.Min.IsNull())
            {
                boundingBox1.Min = boundingBox2.Min.Clone();
            }
            else
            {
                boundingBox1.Min.X = Math.Min(boundingBox1.Min.X, boundingBox2.Min.X);
                boundingBox1.Min.Y = Math.Min(boundingBox1.Min.Y, boundingBox2.Min.Y);
                if (boundingBox1.Min.IsZSpecified != boundingBox2.Min.IsZSpecified)
                {
                    throw new ArgumentException("Different dimensions in points!");
                }
                boundingBox1.Min.Z = Math.Min(boundingBox1.Min.Z, boundingBox2.Min.Z);
                if (boundingBox1.Min.IsMSpecified != boundingBox2.Min.IsMSpecified)
                {
                    throw new ArgumentException("Different dimensions in points!");
                }
                boundingBox1.Min.M = Math.Min(boundingBox1.Min.M, boundingBox2.Min.M);
            }
        }
Example #11
0
        /// <summary>
        /// Gets the bounding box of all geometries in a list of SqlGeometry objects.
        /// </summary>
        /// <param name="featureList">The feature list.</param>
        private static WebBoundingBox GetBoundingBoxFromSqlGeometryList(List <SqlGeometry> featureList)
        {
            SqlGeometry feature;

            if (featureList.IsEmpty())
            {
                return(null);
            }

            feature = featureList[0];
            feature = feature.STEnvelope();
            WebBoundingBox boundingBox = new WebBoundingBox();

            boundingBox.Min = new WebPoint(feature.STStartPoint().STX.Value, feature.STStartPoint().STY.Value);
            boundingBox.Max = new WebPoint(feature.STPointN(3).STX.Value, feature.STPointN(3).STY.Value);

            for (int i = 1; i < featureList.Count; i++)
            {
                feature = featureList[i];
                feature = feature.STEnvelope();
                if (feature.STStartPoint().STX.Value < boundingBox.Min.X)
                {
                    boundingBox.Min.X = feature.STStartPoint().STX.Value;
                }
                if (feature.STStartPoint().STY.Value < boundingBox.Min.Y)
                {
                    boundingBox.Min.Y = feature.STStartPoint().STY.Value;
                }

                if (feature.STPointN(3).STX.Value > boundingBox.Max.X)
                {
                    boundingBox.Max.X = feature.STPointN(3).STX.Value;
                }
                if (feature.STPointN(3).STY.Value > boundingBox.Max.Y)
                {
                    boundingBox.Max.Y = feature.STPointN(3).STY.Value;
                }
            }

            return(boundingBox);
        }
Example #12
0
        /// <summary>
        /// Gets the sweden extent bounding box polygon.
        /// </summary>
        /// <param name="coordinateSystem">The coordinate system.</param>
        public static WebPolygon GetSwedenExtentBoundingBoxPolygon(WebCoordinateSystem coordinateSystem)
        {
            WebPolygon boundingBox = new WebPolygon();

            // Sweden extent in SWEREF99_TM
            WebBoundingBox swedenBoundingBox = SwedenExtentCoordinates.GetSwedenExtentWebBoundingBoxSweref99();

            if (coordinateSystem.IsNotNull() && coordinateSystem.Id.IsNotNull() &&
                (coordinateSystem.Id != CoordinateSystemId.SWEREF99_TM))
            {
                WebCoordinateSystem sweref99CoordinateSystem = new WebCoordinateSystem();
                sweref99CoordinateSystem.Id = CoordinateSystemId.SWEREF99_TM;
                boundingBox = WebServiceData.CoordinateConversionManager.GetConvertedBoundingBox(
                    swedenBoundingBox, sweref99CoordinateSystem, coordinateSystem);
            }
            else
            {
                boundingBox = swedenBoundingBox.GetPolygon();
            }

            return(boundingBox);
        }
        /// <summary>
        /// Get bounding box for provided polygon.
        /// Currently only 2 dimensions are handled.
        /// </summary>
        /// <param name="polygon">This polygon.</param>
        /// <returns>Bounding box for provided polygon.</returns>
        public static WebBoundingBox GetBoundingBox(this WebPolygon polygon)
        {
            WebBoundingBox boundingBox;

            boundingBox = null;
            if (polygon.LinearRings.IsNotEmpty() &&
                polygon.LinearRings[0].Points.IsNotEmpty())
            {
                foreach (WebPoint point in polygon.LinearRings[0].Points)
                {
                    if (boundingBox.IsNull())
                    {
                        boundingBox     = new WebBoundingBox();
                        boundingBox.Max = point.Clone();
                        boundingBox.Min = point.Clone();
                    }
                    else
                    {
                        if (boundingBox.Max.X < point.X)
                        {
                            boundingBox.Max.X = point.X;
                        }
                        if (boundingBox.Max.Y < point.Y)
                        {
                            boundingBox.Max.Y = point.Y;
                        }
                        if (boundingBox.Min.X > point.X)
                        {
                            boundingBox.Min.X = point.X;
                        }
                        if (boundingBox.Min.Y > point.Y)
                        {
                            boundingBox.Min.Y = point.Y;
                        }
                    }
                }
            }
            return(boundingBox);
        }
 /// <summary>
 /// Check if grid specifications is correct.
 /// </summary>
 /// <param name="gridSpecification"> Information on grid specifications.</param>
 /// <param name="speciesObservationBoundingBox">Bounding box for species observation.</param>
 public static void CheckGridSpecifications(this WebGridSpecification gridSpecification, WebBoundingBox speciesObservationBoundingBox)
 {
     if (gridSpecification.IsNotNull() && gridSpecification.BoundingBox.IsNotNull())
     {
         if (speciesObservationBoundingBox.IsNotNull())
         {
             throw new ArgumentException("WebGridSpecifications: Properties WebGridSpecifications.BoundingBox and WebSpeciesObservatioSearchCriteria.BoundingBox have value set, only one BoundigBox can be set.");
         }
     }
 }
Example #15
0
        public static List <WebGridCellFeatureStatistics> GetFeaturesBasedOnGridCells(List <SqlGeometry> featureList, WebGridSpecification gridSpecification, int srid)
        {
            List <WebGridCellFeatureStatistics> webGridCellFeatureStatisticsList;
            List <SqlGeometry> gridCellList;
            List <WebPoint>    centrePointList;
            SqlGeometry        newFeature;

            if (featureList.IsNotNull() && featureList.Count.Equals(0))
            {
                throw new Exception("There are no features in the featureList.");
            }

            webGridCellFeatureStatisticsList = new List <WebGridCellFeatureStatistics>();
            if (gridSpecification.IsNull())
            {
                return(webGridCellFeatureStatisticsList);
            }


            WebBoundingBox gridBoundingBox     = gridSpecification.BoundingBox;
            WebBoundingBox featuresBoundingBox = GetBoundingBoxFromSqlGeometryList(featureList);

            var mergedBoundingBox = GetFeatureStatisticsMergedBoundingBox(featuresBoundingBox, gridBoundingBox);

            if (mergedBoundingBox != null)
            {
                gridSpecification.BoundingBox = mergedBoundingBox; // change bounding box
            }
            gridCellList = CreateSqlGeometryGrid(gridSpecification, srid, out centrePointList);
            gridSpecification.BoundingBox = gridBoundingBox; // reset bounding box
            WebCoordinateSystem gridCoordinateSystem = gridSpecification.GridCoordinateSystem.ToWebCoordinateSystem();
            int maxGridCellArea = gridSpecification.GridCellSize * gridSpecification.GridCellSize;

            // Pick one grid cell at a time and step through all the features.
            // If they overlap, add the grid cell and the data to the resulting list.
            for (int i = 0; i < gridCellList.Count; i++)
            {
                SqlGeometry gridCell = gridCellList[i];
                WebGridCellFeatureStatistics webGridCellFeatureStatistics = null;

                foreach (SqlGeometry feature in featureList)
                {
                    if (feature == null)
                    {
                        continue;
                    }



                    // *     ____                --------                            ____
                    //  ____/     |             |       |                         __/    |
                    // < ___      |             |       |           =>(intersect) |_     |
                    //      |_   _|   feature   |_______| gridcell                  |____|
                    //        |_|
                    newFeature = feature.STIntersection(gridCell).MakeValid(); //Create a new feature from overlay with the geometry feature and the gridcell
                    if (!(newFeature.STNumGeometries() == 0))                  //If there is intersection
                    {
                        if (webGridCellFeatureStatistics == null)
                        {
                            webGridCellFeatureStatistics = new WebGridCellFeatureStatistics();

                            webGridCellFeatureStatistics.CoordinateSystem        = gridCoordinateSystem;
                            webGridCellFeatureStatistics.BoundingBox             = CreateGridCellBoundingPolygonFromSqlGeometry(gridCell);
                            webGridCellFeatureStatistics.CentreCoordinate        = centrePointList[i];
                            webGridCellFeatureStatistics.Size                    = gridSpecification.GridCellSize;
                            webGridCellFeatureStatistics.GridCoordinateSystem    = gridSpecification.GridCoordinateSystem;
                            webGridCellFeatureStatistics.OrginalBoundingBox      = CreateGridCellBoundingBoxFromSqlGeometry(gridCell);
                            webGridCellFeatureStatistics.OrginalCentreCoordinate = CreateCentreCoordinateFromBoundingBox(webGridCellFeatureStatistics.OrginalBoundingBox);
                            if (feature.GetGeometryType().ToString().Equals("MultiPolygon"))
                            {
                                webGridCellFeatureStatistics.FeatureType = FeatureType.Multipolygon;
                            }
                            if (feature.GetGeometryType().ToString().Equals("Polygon"))
                            {
                                webGridCellFeatureStatistics.FeatureType = FeatureType.Polygon;
                            }
                            if (feature.GetGeometryType().ToString().Equals("MultiLineString"))
                            {
                                webGridCellFeatureStatistics.FeatureType = FeatureType.Multiline;
                            }
                            if (feature.GetGeometryType().ToString().Equals("LineString"))
                            {
                                webGridCellFeatureStatistics.FeatureType = FeatureType.Line;
                            }
                            if (feature.GetGeometryType().ToString().Equals("MultiPoint"))
                            {
                                webGridCellFeatureStatistics.FeatureType = FeatureType.Point;
                            }
                            if (feature.GetGeometryType().ToString().Equals("Point"))
                            {
                                webGridCellFeatureStatistics.FeatureType = FeatureType.Point;
                            }

                            webGridCellFeatureStatisticsList.Add(webGridCellFeatureStatistics);

                            // Create line for lines and Polygones
                            if (feature.GetGeometryType().ToString().Equals("MultiPolygon") || feature.GetGeometryType().ToString().Equals("Polygon") ||
                                feature.GetGeometryType().ToString().Equals("MultiLineString") || feature.GetGeometryType().ToString().Equals("LineString"))
                            {
                                // Old incorrect value
                                // webGridCellFeatureStatistics.FeatureLength += newFeature.STLength().Value;
                                // For debug only
                                // double test = newFeature.STLength().Value;

                                // Here we get the length of the newFeaure without intersection length.
                                // First we get boundaries for feature, newFeature and gridcell
                                SqlGeometry featureBoundary    = feature.STBoundary();
                                SqlGeometry newfeatureBoundary = newFeature.STBoundary();
                                // SqlGeometry gridBoundary = gridCell.STBoundary();

                                // *     ____                       ____
                                //    __/    |                 ___/     |
                                //    |_     |                <___      |            => (difference)   |
                                //      |____|   newfeature       |_   _| feature                          __
                                //                                   |_|
                                // Get the difference between newfeature and feature ie intersection with the gridcell
                                SqlGeometry intersectionObjectsWithGridCell = newfeatureBoundary.STDifference(featureBoundary);

                                // *           ____                                                               ____
                                //          __/    |                                                           __/    |
                                //          |_     |                    |               => (difference)         _     |
                                //            |____| newfeature            __                                    |_  _|
                                //
                                // Then we remove intersectionObjects from newFeature and we get the resulting objects.
                                SqlGeometry newFeatueWithoutIntersection = newfeatureBoundary.STDifference(intersectionObjectsWithGridCell);

                                // And now we have the correct lenght of newFeature.
                                double newFeatueWithoutIntersectionLine = newFeatueWithoutIntersection.STLength().Value;
                                webGridCellFeatureStatistics.FeatureLength += newFeatueWithoutIntersectionLine;
                            }
                        }
                        else
                        {
                            // Create add line length to existing line used for lines and polygones
                            if (feature.GetGeometryType().ToString().Equals("MultiPolygon") || feature.GetGeometryType().ToString().Equals("Polygon") ||
                                feature.GetGeometryType().ToString().Equals("MultiLineString") || feature.GetGeometryType().ToString().Equals("LineString"))
                            {
                                webGridCellFeatureStatistics.FeatureLength += newFeature.STLength().Value;
                            }
                        }
                        // Calculate area only for polygons
                        if (feature.GetGeometryType().ToString().Equals("MultiPolygon") || feature.GetGeometryType().ToString().Equals("Polygon"))
                        {
                            webGridCellFeatureStatistics.FeatureArea += newFeature.STArea().Value;
                            if (webGridCellFeatureStatistics.FeatureArea > maxGridCellArea)
                            {
                                webGridCellFeatureStatistics.FeatureArea = maxGridCellArea;
                            }
                        }

                        if (feature.GetGeometryType().ToString().Equals("MultiPoint") || feature.GetGeometryType().ToString().Equals("Point"))
                        {
                            webGridCellFeatureStatistics.FeatureCount += (int)newFeature.STNumGeometries();
                        }
                        else
                        {
                            webGridCellFeatureStatistics.FeatureCount += 1;
                        }

                        // webGridCellFeatureStatistics.FeatureCount += newFeature.STNumGeometries().Value;
                    }
                }
            }
            return(webGridCellFeatureStatisticsList);
        }
Example #16
0
        /// <summary>
        /// Merges the features bounding box with the grid bounding box.
        /// </summary>
        /// <param name="featuresBoundingBox">The features bounding box.</param>
        /// <param name="gridBoundingBox">The grid bounding box.</param>
        /// <returns>A minimum bounding box.</returns>
        public static WebBoundingBox GetFeatureStatisticsMergedBoundingBox(WebBoundingBox featuresBoundingBox,
                                                                           WebBoundingBox gridBoundingBox)
        {
            WebBoundingBox boundingBox = null;

            if (featuresBoundingBox == null & gridBoundingBox == null)
            {
                return(null);
            }
            if (featuresBoundingBox == null)
            {
                boundingBox     = new WebBoundingBox();
                boundingBox.Min = new WebPoint(gridBoundingBox.Min.X, gridBoundingBox.Min.Y);
                boundingBox.Max = new WebPoint(gridBoundingBox.Max.X, gridBoundingBox.Max.Y);
                return(boundingBox);
            }
            if (gridBoundingBox == null)
            {
                boundingBox     = new WebBoundingBox();
                boundingBox.Min = new WebPoint(featuresBoundingBox.Min.X, featuresBoundingBox.Min.Y);
                boundingBox.Max = new WebPoint(featuresBoundingBox.Max.X, featuresBoundingBox.Max.Y);
                return(boundingBox);
            }

            boundingBox     = new WebBoundingBox();
            boundingBox.Min = new WebPoint();
            boundingBox.Max = new WebPoint();

            if (featuresBoundingBox.Min.X < gridBoundingBox.Min.X)
            {
                boundingBox.Min.X = gridBoundingBox.Min.X;
            }
            else
            {
                boundingBox.Min.X = featuresBoundingBox.Min.X;
            }

            if (featuresBoundingBox.Min.Y < gridBoundingBox.Min.Y)
            {
                boundingBox.Min.Y = gridBoundingBox.Min.Y;
            }
            else
            {
                boundingBox.Min.Y = featuresBoundingBox.Min.Y;
            }

            if (gridBoundingBox.Max.X < featuresBoundingBox.Max.X)
            {
                boundingBox.Max.X = gridBoundingBox.Max.X;
            }
            else
            {
                boundingBox.Max.X = featuresBoundingBox.Max.X;
            }

            if (gridBoundingBox.Max.Y < featuresBoundingBox.Max.Y)
            {
                boundingBox.Max.Y = gridBoundingBox.Max.Y;
            }
            else
            {
                boundingBox.Max.Y = featuresBoundingBox.Max.Y;
            }

            return(boundingBox);
        }