/// <summary>Calculate Field-of-View from a specified TargetMode, assuming a spherical earth
        /// and height measured in feet if <code>hexesPerMile</code> is not equal 0.</summary>
        /// <param name="board">A reference to an IFovBoard {IHex} instance.</param>
        /// <param name="coordsObserver">Cordinates of observer;s hex.</param>
        /// <param name="fovRadius">Radius of Field-of-view desired.</param>
        /// <param name="targetMode">TargetMode value for determining target visibility.</param>
        /// <param name="setFieldOfView">Sets a hex as visible in the Field-of-View.</param>
        /// <param name="defaultHeight">Height used for observer and target when targetMode = EqualHeights/</param>
        /// <param name="hexesPerMile">Number of hexes per mile (ie 1/4000 of planet radius).</param>
        /// <remarks>Adjusts visibility for curvature of the Earth. This is the only version of
        /// ComputeFieldOfView that is <b>not</b> scale invariant for height, and assumes that height
        /// is measured in feet.
        /// </remarks>
        /// <a href="http://mathcentral.uregina.ca/qq/database/QQ.09.02/shirley3.html">Hidden by the Curvature of the Earth</a>
        public static void ComputeFieldOfView(
            IFovBoard board,
            HexCoords coordsObserver,
            int fovRadius,
            FovTargetMode targetMode,
            Action <HexCoords> setFieldOfView,
            int defaultHeight,
            int hexesPerMile
            )
        {
            if (fovRadius > int.MinValue)
            {
                fovRadius = fovRadius - 1;
            }
            int CalculationHeightUnits = UseMetric ?   5  // convert metres to  cm  for greater precision
                                                   :  12; // convert feet to inches for greater precision

            Func <HexCoords, int> elevationTarget;
            int elevationObserver;

            switch (targetMode)
            {
            case FovTargetMode.TargetHeightEqualZero:
                elevationTarget   = coords => board.ElevationGroundASL(coords);
                elevationObserver = board.ElevationObserverASL(coordsObserver);
                break;

            default:
            case FovTargetMode.TargetHeightEqualActual:
                elevationTarget   = coords => board.ElevationTargetASL(coords);
                elevationObserver = board.ElevationObserverASL(coordsObserver);
                break;

            case FovTargetMode.EqualHeights:
                elevationTarget   = coords => board.ElevationGroundASL(coords) + defaultHeight;
                elevationObserver = elevationTarget(coordsObserver);
                break;
            }

            var deltaHeight = GetDeltaHeight(coordsObserver, hexesPerMile, CalculationHeightUnits);

            ComputeFieldOfView(
                coordsObserver,
                fovRadius,
                elevationObserver * CalculationHeightUnits,
                hc => board.MapSizeHexes.IsOnboard(hc),
                coords => elevationTarget(coords) * CalculationHeightUnits - deltaHeight(coords),
                (coords, hexside) => board.ElevationHexsideASL(coords, hexside) * CalculationHeightUnits - deltaHeight(coords),
                setFieldOfView
                );
        }
        /// <summary>Calculate Field-of-View from a specified TargetMode, assuming a spherical earth
        /// and height measured in feet if <code>hexesPerMile</code> is not equal 0.</summary>
        /// <param name="coordsObserver">Cordinates of observer;s hex.</param>
        /// <param name="board">A reference to an IFovBoard {IHex} instance.</param>
        /// <param name="targetMode">TargetMode value for determining target visibility.</param>
        /// <param name="setFieldOfView">Sets a hex as visible in the Field-of-View.</param>
        /// <param name="defaultHeight">Height used for observer and target when targetMode = EqualHeights/</param>
        /// <param name="hexesPerMile">Number of hexes per mile (ie 1/4000 of planet radius).</param>
        /// <remarks>Adjusts visibility for curvature of the Earth. This is the only version of
        /// ComputeFieldOfView that is <b>not</b> scale invariant for height, and assumes that height
        /// is measured in feet.
        /// </remarks>
        /// <a href="http://mathcentral.uregina.ca/qq/database/QQ.09.02/shirley3.html">Hidden by the Curvature of the Earth</a>
        public static void ComputeFieldOfView(
            HexCoords coordsObserver,
            IFovBoard <IHex> board,
            FovTargetMode targetMode,
            Action <HexCoords> setFieldOfView,
            int defaultHeight,
            int hexesPerMile
            )
        {
            int CalculationHeightUnits = UseMetric ?   5 // convert metres to  cm  for greater precision
                                             :  12;      // convert feet to inches for greater precision

            Func <HexCoords, int> elevationTarget;
            int elevationObserver;

            switch (targetMode)
            {
            case FovTargetMode.TargetHeightEqualZero:
                elevationTarget   = coords => board.ElevationGroundASL(coords);
                elevationObserver = board.ElevationObserverASL(coordsObserver);
                break;

            default:
            case FovTargetMode.TargetHeightEqualActual:
                elevationTarget   = coords => board.ElevationTargetASL(coords);
                elevationObserver = board.ElevationObserverASL(coordsObserver);
                break;

            case FovTargetMode.EqualHeights:
                elevationTarget   = coords => board.ElevationGroundASL(coords) + defaultHeight;
                elevationObserver = elevationTarget(coordsObserver);
                break;
            }

            Func <HexCoords, int> deltaHeight = GetDeltaHeight(coordsObserver, hexesPerMile, CalculationHeightUnits);

            ShadowCasting.ComputeFieldOfView(
                coordsObserver,
                board.FovRadius - 1,
                elevationObserver * CalculationHeightUnits,
                board.IsOnboard,
                coords => elevationTarget(coords) * CalculationHeightUnits - deltaHeight(coords),
                (coords, hexside) => board.ElevationHexsideASL(coords, hexside) * CalculationHeightUnits - deltaHeight(coords),
                setFieldOfView
                );
        }