Пример #1
0
        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");
        }
Пример #2
0
        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()");
        }
Пример #3
0
        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());
        }
Пример #4
0
        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");
        }
Пример #5
0
        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");
            }
        }
Пример #6
0
        /// <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);
            }
        }
Пример #7
0
        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;
            }
        }
Пример #8
0
        /// <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);
        }