public void Test_SubGridTreeBitmapSubGridBitsTests_CellXY_Indexer() { SubGridTreeBitmapSubGridBits bits = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled); Assert.True(bits.IsEmpty(), "Bits not empty after creation"); bits[0, 0] = true; Assert.False(bits.IsEmpty(), "Bits empty after setting bit"); bits[0, 0] = false; Assert.True(bits.IsEmpty(), "Bits not empty after clearing bit"); }
public void Test_SubGridTreeBitmapSubGridBitsTests_Fill() { SubGridTreeBitmapSubGridBits bits = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled); Assert.True(bits.IsEmpty(), "Bits not empty"); bits.Fill(); Assert.True(bits.IsFull(), "Bits not empty after performing a Fill()"); }
public void Test_SubGridTreeBitmapSubGridBitsTests_XORWith() { SubGridTreeBitmapSubGridBits bits1 = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Filled); SubGridTreeBitmapSubGridBits bits2 = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled); SubGridTreeBitmapSubGridBits result = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled); result.XorWith(bits2); Assert.True(result.IsEmpty()); result.XorWith(bits1); Assert.True(result.IsFull()); result.XorWith(bits2); Assert.True(result.IsFull()); result.XorWith(bits1); Assert.True(result.IsEmpty()); }
public void Test_SubGridTreeBitmapSubGridBitsTests_Creation() { // Test the constructor with filled false produces bitmask with all bits set to off SubGridTreeBitmapSubGridBits bits2 = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled); Assert.True(bits2.IsEmpty() && !bits2.IsFull(), "Bits is not empty as expected"); // Test the constructor with filled true produces bitmask with all bits set to on SubGridTreeBitmapSubGridBits bits3 = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Filled); Assert.True(!bits3.IsEmpty() && bits3.IsFull(), "Bits is not full as expected"); }
protected void ProcessVolumeInformationForSubGrid(ClientHeightLeafSubGrid baseScanSubGrid, ClientHeightLeafSubGrid topScanSubGrid) { _log.LogDebug("In ProcessVolumeInformationForSubGrid"); try { // DesignHeights represents all the valid spot elevations for the cells in the // sub grid being processed (IClientHeightLeafSubGrid designHeights, DesignProfilerRequestResult profilerRequestResult)getDesignHeightsResult = (null, DesignProfilerRequestResult.UnknownError); // FCellArea is a handy place to store the cell area, rather than calculate it all the time (value wont change); var cellArea = CellSize * CellSize; // Query the patch of elevations from the surface model for this sub grid if (ActiveDesign?.Design != null) { _log.LogDebug("About to call ActiveDesign.Design.GetDesignHeightsViaLocalCompute()"); getDesignHeightsResult = ActiveDesign.Design.GetDesignHeightsViaLocalCompute(SiteModel, ActiveDesign.Offset, baseScanSubGrid.OriginAsCellAddress(), CellSize); if (getDesignHeightsResult.profilerRequestResult != DesignProfilerRequestResult.OK && getDesignHeightsResult.profilerRequestResult != DesignProfilerRequestResult.NoElevationsInRequestedPatch) { _log.LogError($"Design profiler sub grid elevation request for {baseScanSubGrid.OriginAsCellAddress()} failed with error {getDesignHeightsResult.profilerRequestResult}"); return; } } var bits = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled); // ReSharper disable once CompareOfFloatsByEqualityOperator var standardVolumeProcessing = LiftParams.TargetLiftThickness == Consts.NullHeight || LiftParams.TargetLiftThickness <= 0; var localCellsScanned = 0; var localCellsUsed = 0; var localCellsDiscarded = 0; var localCellsUsedCut = 0; var localCellsUsedFill = 0; var localVolume = 0.0d; var localCutFillVolume = new CutFillVolume(0, 0); // If we are interested in standard volume processing use this cycle if (standardVolumeProcessing) { localCellsScanned += SubGridTreeConsts.SubGridTreeCellsPerSubGrid; for (var i = 0; i < SubGridTreeConsts.SubGridTreeDimension; i++) { for (var j = 0; j < SubGridTreeConsts.SubGridTreeDimension; j++) { float topZ; var baseZ = baseScanSubGrid.Cells[i, j]; // If the user has configured a first pass thickness, then we need to subtract this height // difference from the BaseZ retrieved from the current cell if this measured height was // the first pass made in the cell. if (LiftParams.FirstPassThickness > 0) { baseZ -= LiftParams.FirstPassThickness; } if (VolumeType == VolumeComputationType.BetweenFilterAndDesign || VolumeType == VolumeComputationType.BetweenDesignAndFilter) { topZ = getDesignHeightsResult.designHeights?.Cells[i, j] ?? Consts.NullHeight; if (VolumeType == VolumeComputationType.BetweenDesignAndFilter) { MinMax.Swap(ref baseZ, ref topZ); } } else { topZ = topScanSubGrid.Cells[i, j]; } switch (VolumeType) { case VolumeComputationType.None: break; case VolumeComputationType.AboveLevel: { // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight) { localCellsUsed++; if (baseZ > BaseLevel) { localVolume += cellArea * (baseZ - BaseLevel); } } else { localCellsDiscarded++; } break; } case VolumeComputationType.Between2Levels: { // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight) { localCellsUsed++; if (baseZ > BaseLevel) { localVolume += cellArea * (baseZ < TopLevel ? (baseZ - BaseLevel) : (TopLevel - BaseLevel)); } } else { localCellsDiscarded++; } break; } case VolumeComputationType.AboveFilter: case VolumeComputationType.Between2Filters: case VolumeComputationType.BetweenFilterAndDesign: case VolumeComputationType.BetweenDesignAndFilter: { // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight && // ReSharper disable once CompareOfFloatsByEqualityOperator topZ != Consts.NullHeight) { localCellsUsed++; // Note the fact we have processed this cell in the coverage map bits.SetBit(i, j); var cellUsedInVolumeCalc = (topZ - baseZ >= FillTolerance) || (baseZ - topZ >= CutTolerance); // Accumulate volumes if (cellUsedInVolumeCalc) { var volumeDifference = cellArea * (topZ - baseZ); // Accumulate the 'surplus' volume. Ie: the simple summation of // all cuts and fills. localVolume += volumeDifference; // Accumulate the cuts and fills into discrete cut and fill quantities if (topZ < baseZ) { localCellsUsedCut++; localCutFillVolume.AddCutVolume(Math.Abs(volumeDifference)); } else { localCellsUsedFill++; localCutFillVolume.AddFillVolume(Math.Abs(volumeDifference)); } } else { // Note the fact there was no volume change in this cell // NoChangeMap.Cells[BaseScanSubGrid.OriginX + I, BaseScanSubGrid.OriginY + J] := True; } } else { localCellsDiscarded++; } } break; default: _log.LogError($"Unknown volume type {VolumeType} in ProcessVolumeInformationForSubGrid()"); break; } } } } // ReSharper disable once CompareOfFloatsByEqualityOperator var targetLiftThicknessCalculationsRequired = LiftParams.TargetLiftThickness != Consts.NullHeight && LiftParams.TargetLiftThickness > 0; //If we are interested in thickness calculations do them if (targetLiftThicknessCalculationsRequired) { var belowToleranceToCheck = LiftParams.TargetLiftThickness - LiftParams.BelowToleranceLiftThickness; var aboveToleranceToCheck = LiftParams.TargetLiftThickness + LiftParams.AboveToleranceLiftThickness; SubGridUtilities.SubGridDimensionalIterator((i, j) => { var baseZ = baseScanSubGrid.Cells[i, j]; var topZ = topScanSubGrid.Cells[i, j]; // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight || // ReSharper disable once CompareOfFloatsByEqualityOperator topZ != Consts.NullHeight) { localCellsScanned++; } //Test if we don't have NULL values and carry on // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight && // ReSharper disable once CompareOfFloatsByEqualityOperator topZ != Consts.NullHeight) { bits.SetBit(i, j); double elevationDiff = topZ - baseZ; if (elevationDiff <= aboveToleranceToCheck && elevationDiff >= belowToleranceToCheck) { localCellsUsed++; } else if (elevationDiff > aboveToleranceToCheck) { localCellsUsedFill++; } else if (elevationDiff < belowToleranceToCheck) { localCellsUsedCut++; } } else { localCellsDiscarded++; } }); } // Update the quantities in the aggregator proper // Note: the lock is not asynchronous as this will be highly non-contended _lock.Wait(); try { CellsScanned += localCellsScanned; CellsUsed += localCellsUsed; CellsDiscarded += localCellsDiscarded; CellsUsedCut += localCellsUsedCut; CellsUsedFill += localCellsUsedFill; Volume += localVolume; CutFillVolume.AddCutFillVolume(localCutFillVolume.CutVolume, localCutFillVolume.FillVolume); // Record the bits for this sub grid in the coverage map by requesting the whole sub grid // of bits from the leaf level and setting it in one operation under an exclusive lock if (!bits.IsEmpty()) { var coverageMapSubGrid = CoverageMap.ConstructPathToCell(baseScanSubGrid.OriginX, baseScanSubGrid.OriginY, SubGridPathConstructionType.CreateLeaf); ((SubGridTreeLeafBitmapSubGrid)coverageMapSubGrid).Bits = bits; } } finally { _lock.Release(); } } catch (Exception e) { _log.LogError(e, "Exception thrown by ProcessVolumeInformationForSubGrid - rethrowing"); throw; } finally { _log.LogDebug("Out ProcessVolumeInformationForSubGrid"); } }
/// <summary> /// Performs the donkey work of the elevation patch calculation /// </summary> public IClientLeafSubGrid Execute(ISiteModel siteModel, int otgCellBottomLeftX, int otgCellBottomLeftY, double cellSize, SurveyedSurfacePatchType patchType, Guid[] includedSurveyedSurfaces, IDesignFiles designs, ISurveyedSurfaces surveyedSurfaces, SubGridTreeBitmapSubGridBits processingMap) { var calcResult = DesignProfilerRequestResult.UnknownError; try { if (!Enum.IsDefined(typeof(SurveyedSurfacePatchType), patchType)) { _log.LogError($"Unknown SurveyedSurfacePatchType: {patchType}, returning null"); return(null); } if (includedSurveyedSurfaces == null) { _log.LogError("Included surveyed surfaces list is null, returning null"); return(null); } if (processingMap == null) { _log.LogError("Supplied processing map is null, returning null"); return(null); } var patch = _clientLeafSubGridFactory.GetSubGridEx( patchType == SurveyedSurfacePatchType.CompositeElevations ? GridDataType.CompositeHeights : GridDataType.HeightAndTime, cellSize, SubGridTreeConsts.SubGridTreeLevels, otgCellBottomLeftX, otgCellBottomLeftY); // Assign var patchSingle = patchType != SurveyedSurfacePatchType.CompositeElevations ? patch as ClientHeightAndTimeLeafSubGrid : null; var patchComposite = patchType == SurveyedSurfacePatchType.CompositeElevations ? patch as ClientCompositeHeightsLeafSubgrid : null; patch.CalculateWorldOrigin(out var originX, out var originY); var halfCellSize = cellSize / 2; var originXPlusHalfCellSize = originX + halfCellSize; var originYPlusHalfCellSize = originY + halfCellSize; // Work down through the list of surfaces in the time ordering provided by the caller foreach (var surveyedSurfaceUid in includedSurveyedSurfaces) { if (processingMap.IsEmpty()) { break; } var thisSurveyedSurface = surveyedSurfaces.Locate(surveyedSurfaceUid); if (thisSurveyedSurface == null) { _log.LogError($"Surveyed surface {surveyedSurfaceUid} not found in site model, returning null"); calcResult = DesignProfilerRequestResult.FailedToLoadDesignFile; return(null); } // Lock & load the design var design = designs.Lock(thisSurveyedSurface.DesignDescriptor.DesignID, siteModel, cellSize, out _); if (design == null) { _log.LogError($"Failed to lock design file {thisSurveyedSurface.DesignDescriptor} in {nameof(CalculateSurfaceElevationPatch)}"); calcResult = DesignProfilerRequestResult.FailedToLoadDesignFile; return(null); } try { if (!design.HasElevationDataForSubGridPatch( otgCellBottomLeftX >> SubGridTreeConsts.SubGridIndexBitsPerLevel, otgCellBottomLeftY >> SubGridTreeConsts.SubGridIndexBitsPerLevel)) { continue; } var asAtDate = thisSurveyedSurface.AsAtDate.Ticks; var hint = -1; // Walk across the sub grid checking for a design elevation for each appropriate cell // based on the processing bit mask passed in processingMap.ForEachSetBit((x, y) => { // If we can interpolate a height for the requested cell, then update the cell height // and decrement the bit count so that we know when we've handled all the requested cells if (design.InterpolateHeight(ref hint, originXPlusHalfCellSize + cellSize * x, originYPlusHalfCellSize + cellSize * y, 0, out var z)) { // Check for composite elevation processing if (patchType == SurveyedSurfacePatchType.CompositeElevations) { // Set the first elevation if not already set if (patchComposite.Cells[x, y].FirstHeightTime == 0) { patchComposite.Cells[x, y].FirstHeightTime = asAtDate; patchComposite.Cells[x, y].FirstHeight = (float)z; } // Always set the latest elevation (surfaces ordered by increasing date) patchComposite.Cells[x, y].LastHeightTime = asAtDate; patchComposite.Cells[x, y].LastHeight = (float)z; // Update the lowest height if (patchComposite.Cells[x, y].LowestHeightTime == 0 || patchComposite.Cells[x, y].LowestHeight > z) { patchComposite.Cells[x, y].LowestHeightTime = asAtDate; patchComposite.Cells[x, y].LowestHeight = (float)z; } // Update the highest height if (patchComposite.Cells[x, y].HighestHeightTime == 0 || patchComposite.Cells[x, y].HighestHeight > z) { patchComposite.Cells[x, y].HighestHeightTime = asAtDate; patchComposite.Cells[x, y].HighestHeight = (float)z; } } else // earliest/latest singular value processing { patchSingle.Times[x, y] = asAtDate; patchSingle.Cells[x, y] = (float)z; } } // Only clear the processing bit if earliest or latest information is wanted from the surveyed surfaces if (patchType != SurveyedSurfacePatchType.CompositeElevations) { processingMap.ClearBit(x, y); } return(true); }); } finally { designs.UnLock(thisSurveyedSurface.DesignDescriptor.DesignID, design); } } calcResult = DesignProfilerRequestResult.OK; return(patch); } catch (Exception e) { _log.LogError(e, $"Exception occurred calculating surveyed surface patch, calcResult = {calcResult}"); return(null); } }
public void ProcessElevationInformationForSubGrid(int cellOriginX, int cellOriginY, float[,] baseSubGrid, float[,] topSubGrid) { // FCellArea is a handy place to store the cell area, rather than calculate it all the time (value wont change); var cellArea = CellSize * CellSize; var bits = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled); CellsScanned += SubGridTreeConsts.SubGridTreeCellsPerSubGrid; for (var i = 0; i < SubGridTreeConsts.SubGridTreeDimension; i++) { for (var j = 0; j < SubGridTreeConsts.SubGridTreeDimension; j++) { var topZ = topSubGrid[i, j]; var baseZ = baseSubGrid[i, j]; if (baseZ != Consts.NullHeight && topZ != Consts.NullHeight) { CellsUsed++; // Note the fact we have processed this cell in the coverage map bits.SetBit(i, j); var cellUsedInVolumeCalc = (topZ - baseZ >= FillTolerance) || (baseZ - topZ >= CutTolerance); // Accumulate volumes if (cellUsedInVolumeCalc) { var volumeDifference = cellArea * (topZ - baseZ); // Accumulate the 'surplus' volume. Ie: the simple summation of // all cuts and fills. Volume += volumeDifference; // Accumulate the cuts and fills into discrete cut and fill quantities if (topZ < baseZ) { CellsUsedCut++; CutFillVolume.AddCutVolume(Math.Abs(volumeDifference)); } else { CellsUsedFill++; CutFillVolume.AddFillVolume(Math.Abs(volumeDifference)); } } else { // Note the fact there was no volume change in this cell // NoChangeMap.Cells[BaseScanSubGrid.OriginX + I, BaseScanSubGrid.OriginY + J] := True; } } else { CellsDiscarded++; } } } // Record the bits for this sub grid in the coverage map by requesting the whole sub grid // of bits from the leaf level and setting it in one operation under an exclusive lock if (!bits.IsEmpty()) { var coverageMapSubGrid = CoverageMap.ConstructPathToCell(cellOriginX, cellOriginY, SubGridPathConstructionType.CreateLeaf); ((SubGridTreeLeafBitmapSubGrid)coverageMapSubGrid).Bits = bits; } }
/// <summary> /// Annotates height information with elevations from surveyed surfaces /// </summary> private ServerRequestResult PerformHeightAnnotation() { if (!_haveComputedSpatialFilterMaskAndClientProdDataMap) { // At this point, the prod data map will be empty. Fill it here so the filter has something to filter against... _clientGrid.ProdDataMap.Fill(); } if (!_haveComputedSpatialFilterMaskAndClientProdDataMap && (ComputeSpatialFilterMaskAndClientProdDataMap() != ServerRequestResult.NoError)) { ClientLeafSubGridFactory.ReturnClientSubGrid(ref _clientGrid); return(ServerRequestResult.FilterInitialisationFailure); } if ((_filteredSurveyedSurfaces?.Count ?? 0) == 0) { return(ServerRequestResult.NoError); } var result = ServerRequestResult.NoError; // TODO: Add Debug_SwitchOffCompositeSurfaceGenerationFromSurveyedSurfaces to configuration // if <config>.Debug_SwitchOffCompositeSurfaceGenerationFromSurveyedSurfaces then Exit; if (!_clientGrid.UpdateProcessingMapForSurveyedSurfaces(_processingMap, _filteredSurveyedSurfaces as IList, _returnEarliestFilteredCellPass)) { return(ServerRequestResult.NoError); } if (_processingMap.IsEmpty()) { return(result); } try { // Hand client grid details, a mask of cells we need surveyed surface elevations for, and a temp grid to the Design Profiler // Instantiate an argument object for the surface elevation patch request. We always want to request all surface elevations to // promote cacheability. var surfaceElevationPatchArg = new SurfaceElevationPatchArgument { SiteModelID = _siteModel.ID, OTGCellBottomLeftX = _clientGrid.OriginX, OTGCellBottomLeftY = _clientGrid.OriginY, CellSize = _siteModel.CellSize, IncludedSurveyedSurfaces = _filteredSurveyedSurfacesAsGuidArray, SurveyedSurfacePatchType = _surveyedSurfacePatchType, ProcessingMap = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Filled) }; if (!(_surfaceElevationPatchRequest.Execute(surfaceElevationPatchArg) is ClientHeightAndTimeLeafSubGrid surfaceElevations)) { return(result); } // Construct the elevation range filter lambda Func <int, int, float, bool> elevationRangeFilterLambda = null; if (_filter.AttributeFilter.HasElevationRangeFilter) { elevationRangeFilterLambda = ApplyElevationRangeFilter; } if (!_clientGrid.PerformHeightAnnotation(_processingMap, _filteredSurveyedSurfaces as IList, _returnEarliestFilteredCellPass, surfaceElevations, elevationRangeFilterLambda)) { return(ServerRequestResult.SubGridHeightAnnotationFailed); } result = ServerRequestResult.NoError; } finally { // TODO: Use client sub grid pool... // PSNodeImplInstance.RequestProcessor.RepatriateClientGrid(TICSubGridTreeLeafSubGridBase(SurfaceElevations)); } return(result); }