public IntervisibilityReport(List <GeoPoint> pointsWithElevation, IntervisibilityMetrics visibilityMetrics, bool includeAllPoints = false, double originVerticalOffset = 0d) { this.Origin = pointsWithElevation.First(); this.Target = pointsWithElevation.Last(); this.Metrics = visibilityMetrics; this.GeoPoints = pointsWithElevation; this.OriginVerticalOffset = originVerticalOffset; }
/// <summary> /// Return visibility report from first point to last point. We assume that all points are aligned. /// WARNING: those calculations are not spherical (yet) and are not accurate for long distances. /// <see cref="IntervisibilityMetrics"/> /// </summary> /// <param name="points">Input list of points, visibility is calculated for first and last points (ie: are they visible or is there a relief standing in between)</param> /// <returns><see cref="IntervisibilityMetrics"/> object</returns> internal static IntervisibilityMetrics ComputeVisibilityMetrics(IList <GeoPoint> points, bool visibilityCheck = true, double sourceVerticalOffset = 0d, double targetVerticalOffset = 0, double?noDataValue = null) { IntervisibilityMetrics metrics = new IntervisibilityMetrics(); if (points.Count == 0) { return(metrics); } GeoPoint A = points.First(), B = points.Last(); double hA = A.Elevation ?? 0d, hB = B.Elevation ?? 0d; hA += sourceVerticalOffset; hB += targetVerticalOffset; double AB = A.DistanceTo(B); visibilityCheck = visibilityCheck && (AB > double.Epsilon); if (hA < hB) { MathHelper.Swap(ref A, ref B); MathHelper.Swap(ref hA, ref hB); } double total = 0, minElevation = double.MaxValue, maxElevation = double.MinValue, totalClimb = 0, totalDescent = 0; GeoPoint firstPoint = points[0]; firstPoint.DistanceFromOriginMeters = 0; // force at 0. If null, ignored in json responses double lastElevation = firstPoint.Elevation ?? 0; IntervisibilityObstacle obstacle = null; double lastPeakElevation = 0; int numNoDataPoints = 0; for (int i = 1; i < points.Count; i++) { #region metrics GeoPoint curPoint = points[i]; double v_dist = DistanceTo(curPoint, points[i - 1]); total += v_dist; curPoint.DistanceFromOriginMeters = total; minElevation = Math.Min(minElevation, curPoint.Elevation ?? double.MaxValue); maxElevation = Math.Max(maxElevation, curPoint.Elevation ?? double.MinValue); numNoDataPoints += curPoint.Elevation == noDataValue ? 1 : 0; double currentElevation = curPoint.Elevation ?? lastElevation; double diff = currentElevation - lastElevation; if (diff > 0) { totalClimb += diff; } else { totalDescent += diff; } #endregion #region visibility checks // Visibility check // If obstacle hit, add it and if (visibilityCheck) { double distToLowestPoint = curPoint.DistanceTo(B); double visibilityElevationThreshold = (distToLowestPoint * (hA - hB)) / AB + hB; if (currentElevation >= visibilityElevationThreshold) { if (obstacle == null) { obstacle = new IntervisibilityObstacle(curPoint, visibilityElevationThreshold); lastPeakElevation = currentElevation; obstacle.PeakPoint = curPoint; } else { // still inside obstacle, find peak if (currentElevation > lastPeakElevation) { lastPeakElevation = currentElevation; obstacle.PeakPoint = curPoint; } } } else { if (obstacle != null) // out of obstacle, register it { obstacle.ExitPoint = curPoint; metrics.AddObstacle(obstacle); obstacle = null; } } } if (i == points.Count - 1 && obstacle != null) { // Edge case: last point is exit point. We still have an active obstacle instance // If obstacle entry is curPoint, this is the same point and this is not an obstacle if (!obstacle.EntryPoint.Equals(curPoint)) { obstacle.ExitPoint = curPoint; metrics.AddObstacle(obstacle); obstacle = null; } } #endregion lastElevation = currentElevation; } metrics.Climb = totalClimb; metrics.Descent = totalDescent; metrics.NumPoints = points.Count; metrics.Distance = total; metrics.MinElevation = minElevation; metrics.MaxElevation = maxElevation; metrics.HasVoids = numNoDataPoints > 0; metrics.NumVoidPoints = numNoDataPoints; return(metrics); }