/// <summary> /// Interpolate elevation for the specified point on the line segment from startPoint to endPoint /// </summary> /// <param name="point">The point to interpolate</param> /// <param name="startPoint">The start of the line segment</param> /// <param name="endPoint">The end of the line segment</param> /// <param name="useY2">True if interpolating the second elevation</param> private void InterpolateElevation(CompactionDataPoint point, CompactionDataPoint startPoint, CompactionDataPoint endPoint, bool useY2) { var proportion = (point.x - startPoint.x) / (endPoint.x - startPoint.x); if (useY2) { point.y2 = InterpolateElevation(proportion, startPoint.y2 ?? float.NaN, endPoint.y2 ?? float.NaN); } else { point.y = InterpolateElevation(proportion, startPoint.y, endPoint.y); } log.LogDebug($"Interpolated station {point.x} of cell type {point.cellType} for type {point.type}"); }
/// <summary> /// Determine if the current point is a midpoint and has an elevation. /// </summary> /// <param name="point">The point to check</param> /// <param name="useY2">True if checking the second elevation</param> /// <returns>True if the cell has a non-NaN elevation value for the specified height type</returns> private bool MidPointCellHasHeightValue(CompactionDataPoint point, bool useY2) { if (point.cellType == ProfileCellType.MidPoint) { if (useY2) { if (point.y2.HasValue) { return(!float.IsNaN(point.y2.Value)); } } else { return(!float.IsNaN(point.y)); } } return(false); }
/// <summary> /// Add mid points between the cell edge intersections. This is because the profile line is plotted using these points. /// The cell edges are retained as this is where the color changes on the graph or chart. /// </summary> /// <param name="profileResult">The profile results from Raptor, one list of cell points for each profile type</param> /// <returns>The complete list of interspersed edges and mid points for each profile type.</returns> public void AddMidPoints(CompactionProfileResult <CompactionProfileDataResult> profileResult) { log.LogDebug("Adding midpoints"); foreach (var result in profileResult.results) { log.LogDebug($"Adding midpoints for {result.type}"); if (result.data.Count >= 4) { //No mid point for first and last segments since only partial across the cell. //We have already added them as mid points. var points = new List <CompactionDataPoint> { result.data[0] }; for (int i = 1; i < result.data.Count - 2; i++) { points.Add(result.data[i]); if (result.data[i].cellType != ProfileCellType.Gap) { var midPoint = new CompactionDataPoint(result.data[i]) { cellType = ProfileCellType.MidPoint, x = result.data[i].x + (result.data[i + 1].x - result.data[i].x) / 2 }; points.Add(midPoint); } } points.Add(result.data[result.data.Count - 2]); points.Add(result.data[result.data.Count - 1]); result.data = points; } StringBuilder sb = new StringBuilder(); sb.Append($"After adding midpoints for {result.type}: {result.data.Count}"); foreach (var point in result.data) { sb.Append($",{point.cellType}"); } log.LogDebug(sb.ToString()); } }
/// <summary> /// The profiles for various types (CMV, temperature, pass count etc.) may have several points /// in sequence which have no data which are effectively a single gap. Remove these repeated /// points and just keep the start of the gap and the next data point. /// </summary> /// <param name="result">The profile result to remove the repeated gaps from</param> /// <param name="calcType">The type of summary volumes calculation</param> public void RemoveRepeatedNoData(CompactionProfileResult <CompactionProfileDataResult> result, VolumeCalcType?calcType) { log.LogDebug("RemoveRepeatedNoData: Production data profile"); bool isDesignToGround = calcType.HasValue && calcType == VolumeCalcType.DesignToGround; foreach (var profileResult in result.results) { if (profileResult.data.Count > 0) { //Identify all the gaps. All data with NaN elevation or value is effectively a gap. //The exception is a summary volumes profile that is design to ground where y is NaN as it will be set later using the design. In this case use y2. foreach (var point in profileResult.data) { bool noValue = point.type.StartsWith("passCount") ? point.value == -1 : float.IsNaN(point.value); bool noY = point.type == CompactionDataPoint.SUMMARY_VOLUMES && isDesignToGround ? point.y2.HasValue && float.IsNaN(point.y2.Value) : float.IsNaN(point.y); if (noY || noValue) { point.cellType = ProfileCellType.Gap; } } //Now remove repeated gaps. //Always keep first and last points as they are the slicer end points CompactionDataPoint prevData = profileResult.data[0]; bool haveGap = prevData.cellType == ProfileCellType.Gap; List <CompactionDataPoint> newList = new List <CompactionDataPoint> { prevData }; for (int i = 1; i < profileResult.data.Count - 1; i++) { if (profileResult.data[i].cellType == ProfileCellType.Gap) { if (!haveGap) { //This is the start of a gap - keep it haveGap = true; newList.Add(profileResult.data[i]); } //else ignore it - repeated gap } else { //A data point - keep it haveGap = false; newList.Add(profileResult.data[i]); } } newList.Add(profileResult.data[profileResult.data.Count - 1]); //If the only 2 points are the slicer end points and they have no data then //remove them and return an empty list to indicate no profile data at all. if (newList.Count == 2 && newList[0].cellType == ProfileCellType.Gap && newList[1].cellType == ProfileCellType.Gap) { newList.RemoveRange(0, 2); } profileResult.data = newList; } } }