Beispiel #1
0
        public async Task <CellPassesResponse> ExecuteAsync(CellPassesRequestArgument_ApplicationService arg)
        {
            var result = new CellPassesResponse()
            {
                ReturnCode = CellPassesReturnCode.Error
            };

            if (arg.Filters?.Filters != null && arg.Filters.Filters.Length > 0)
            {
                // Prepare the filters for use in cell passes operations. Failure to prepare any filter results in this request terminating
                if (!arg.Filters.Filters.Select(x => FilterUtilities.PrepareFilterForUse(x, arg.ProjectID)).All(x => x == RequestErrorStatus.OK))
                {
                    return(new CellPassesResponse {
                        ReturnCode = CellPassesReturnCode.FailedToPrepareFilter
                    });
                }
            }

            var siteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(arg.ProjectID);

            if (siteModel == null)
            {
                _log.LogError($"Failed to locate site model {arg.ProjectID}");
                return(result);
            }

            if (!arg.CoordsAreGrid)
            {
                //WGS84 coords need to be converted to NEE
                var pointToConvert = new XYZ(arg.Point.X, arg.Point.Y, 0);
                arg.Point       = DIContext.Obtain <ICoreXWrapper>().LLHToNEE(siteModel.CSIB(), pointToConvert.ToCoreX_XYZ(), CoreX.Types.InputAs.Radians).ToTRex_XYZ();
                result.Northing = arg.Point.Y;
                result.Easting  = arg.Point.X;
            }

            var existenceMap = siteModel.ExistenceMap;

            // Determine the on-the-ground cell
            siteModel.Grid.CalculateIndexOfCellContainingPosition(arg.Point.X,
                                                                  arg.Point.Y,
                                                                  out var otgCellX,
                                                                  out var otgCellY);

            if (!existenceMap[otgCellX >> SubGridTreeConsts.SubGridIndexBitsPerLevel, otgCellY >> SubGridTreeConsts.SubGridIndexBitsPerLevel])
            {
                result.ReturnCode = CellPassesReturnCode.NoDataFound;
                return(result);
            }

            var computeArg      = new CellPassesRequestArgument_ClusterCompute(arg.ProjectID, arg.Point, otgCellX, otgCellY, arg.Filters);
            var requestCompute  = new CellPassesRequest_ClusterCompute();
            var affinityKey     = new SubGridSpatialAffinityKey(SubGridSpatialAffinityKey.DEFAULT_SPATIAL_AFFINITY_VERSION_NUMBER_TICKS, arg.ProjectID, otgCellX, otgCellY);
            var responseCompute = await requestCompute.ExecuteAsync(computeArg, affinityKey);

            result.ReturnCode = responseCompute.ReturnCode;
            result.CellPasses = responseCompute.CellPasses;

            return(result);
        }
Beispiel #2
0
        /// <summary>
        /// Processes the request for type T.
        /// </summary>
        protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item)
        {
            try
            {
                var request = CastRequestObjectTo <TileRequest>(item);

                var filter1 = request.Filter1;
                var filter2 = request.Filter2;
                await PairUpAssetIdentifiers(request.ProjectUid.Value, filter1, filter2);
                await PairUpImportedFileIdentifiers(request.ProjectUid.Value, request.DesignDescriptor, filter1, filter2);

                if (request.ComputeVolumesType == VolumesType.Between2Filters)
                {
                    if (!request.ExplicitFilters)
                    {
                        (filter1, filter2) = FilterUtilities.AdjustFilterToFilter(request.Filter1, request.Filter2);
                    }
                }
                else
                {
                    (filter1, filter2) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(filter1, filter2, request.Mode, request.ComputeVolumesType);
                }

                var trexRequest = new TRexTileRequest(
                    request.ProjectUid.Value,
                    request.Mode,
                    request.Palettes,
                    request.DesignDescriptor,
                    filter1,
                    filter2,
                    request.BoundBoxLatLon,
                    request.BoundBoxGrid,
                    request.Width,
                    request.Height,
                    AutoMapperUtility.Automapper.Map <OverridingTargets>(request.LiftBuildSettings),
                    AutoMapperUtility.Automapper.Map <LiftSettings>(request.LiftBuildSettings),
                    request.ComputeVolumesType
                    );
                log.LogDebug($"{nameof(TilesExecutor)} trexRequest {JsonConvert.SerializeObject(trexRequest)}");

                var fileResult = await trexCompactionDataProxy.SendDataPostRequestWithStreamResponse(trexRequest, "/tile", customHeaders);

                using (var ms = new MemoryStream())
                {
                    fileResult.CopyTo(ms);
                    return(new TileResult(ms.ToArray()));
                }
            }
            finally
            {
                ContractExecutionStates.ClearDynamic();
            }
        }
Beispiel #3
0
        protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item)
        {
            try
            {
                var request = CastRequestObjectTo <SummaryVolumesRequest>(item);

                var baseFilter = request.BaseFilter;
                var topFilter  = request.TopFilter;
                await PairUpAssetIdentifiers(request.ProjectUid.Value, baseFilter, topFilter);
                await PairUpImportedFileIdentifiers(request.ProjectUid.Value, filter1 : baseFilter, filter2 : topFilter);

                var designDescriptors = new List <DesignDescriptor>();
                designDescriptors.Add(request.BaseDesignDescriptor);
                designDescriptors.Add(request.TopDesignDescriptor);
                await PairUpImportedFileIdentifiers(request.ProjectUid.Value, designDescriptors);

                if (request.VolumeCalcType == VolumesType.Between2Filters)
                {
                    if (!request.ExplicitFilters)
                    {
                        (baseFilter, topFilter) = FilterUtilities.AdjustFilterToFilter(request.BaseFilter, request.TopFilter);
                    }
                }
                else
                {
                    (baseFilter, topFilter) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(baseFilter, topFilter, request.VolumeCalcType);
                }

                var summaryVolumesRequest = new SummaryVolumesDataRequest(
                    request.ProjectUid,
                    baseFilter,
                    topFilter,
                    request.BaseDesignDescriptor.FileUid,
                    request.BaseDesignDescriptor.Offset,
                    request.TopDesignDescriptor.FileUid,
                    request.TopDesignDescriptor.Offset,
                    request.VolumeCalcType);
                log.LogDebug($"{nameof(SummaryVolumesExecutor)} trexRequest {JsonConvert.SerializeObject(summaryVolumesRequest)}");
                return(await trexCompactionDataProxy.SendDataPostRequest <SummaryVolumesResult, SummaryVolumesDataRequest>(summaryVolumesRequest, "/volumes/summary", customHeaders));
            }
            finally
            {
                ContractExecutionStates.ClearDynamic();
            }
        }
Beispiel #4
0
        private async Task <CompactionProfileResult <CompactionSummaryVolumesProfileCell> > ProcessSummaryVolumesWithTRexGateway(CompactionProfileProductionDataRequest request)
        {
            if (request.IsAlignmentDesign)
            {
                throw new ServiceException(HttpStatusCode.BadRequest,
                                           new ContractExecutionResult(ContractExecutionStatesEnum.ValidationError, "TRex unsupported request"));
            }

            var volumeCalcType = request.VolumeCalcType ?? VolumeCalcType.None;

            if (volumeCalcType != VolumeCalcType.None)
            {
                var baseFilter = request.BaseFilter;
                var topFilter  = request.TopFilter;
                if (volumeCalcType == VolumeCalcType.GroundToGround && !request.ExplicitFilters)
                {
                    (baseFilter, topFilter) = FilterUtilities.AdjustFilterToFilter(request.BaseFilter, request.TopFilter);
                }

                var liftBuildSettings = request.LiftBuildSettings;
                var summaryVolumesProfileDataRequest = new SummaryVolumesProfileDataRequest(
                    request.ProjectUid ?? Guid.Empty,
                    baseFilter,
                    topFilter,
                    request.VolumeDesignDescriptor?.FileUid,
                    request.VolumeDesignDescriptor?.Offset,
                    ConvertVolumeCalcType(volumeCalcType),
                    request.GridPoints != null,
                    request.GridPoints?.x1 ?? request.WGS84Points.lon1,
                    request.GridPoints?.y1 ?? request.WGS84Points.lat1,
                    request.GridPoints?.x2 ?? request.WGS84Points.lon2,
                    request.GridPoints?.y2 ?? request.WGS84Points.lat2,
                    AutoMapperUtility.Automapper.Map <OverridingTargets>(liftBuildSettings),
                    AutoMapperUtility.Automapper.Map <LiftSettings>(liftBuildSettings)
                    );

                var trexResult = await trexCompactionDataProxy.SendDataPostRequest <ProfileDataResult <SummaryVolumeProfileCell>, SummaryVolumesProfileDataRequest>(summaryVolumesProfileDataRequest, "/volumes/summary/profile", customHeaders);

                return(trexResult != null?ConvertTRexSummaryVolumesProfileResult(trexResult, volumeCalcType) : null);
            }

            return(null);
        }
Beispiel #5
0
        protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item)
        {
            try
            {
                var request = CastRequestObjectTo <PatchRequest>(item);

                var filter1 = request.Filter1;
                var filter2 = request.Filter2;
                if (request.ComputeVolType == VolumesType.Between2Filters)
                {
                    (filter1, filter2) = FilterUtilities.AdjustFilterToFilter(request.Filter1, request.Filter2);
                }
                else
                {
                    (filter1, filter2) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(filter1, filter2, request.Mode, request.ComputeVolType);
                }

                await PairUpAssetIdentifiers(request.ProjectUid.Value, filter1, filter2);
                await PairUpImportedFileIdentifiers(request.ProjectUid.Value, request.DesignDescriptor, filter1, filter2);

                var patchDataRequest = new PatchDataRequest(
                    request.ProjectUid.Value,
                    filter1,
                    filter2,
                    request.Mode,
                    request.PatchNumber,
                    request.PatchSize,
                    AutoMapperUtility.Automapper.Map <OverridingTargets>(request.LiftBuildSettings),
                    AutoMapperUtility.Automapper.Map <LiftSettings>(request.LiftBuildSettings));
                log.LogDebug($"{nameof(PatchExecutor)} patchDataRequest {JsonConvert.SerializeObject(patchDataRequest)}");

                var fileResult = await trexCompactionDataProxy.SendDataPostRequestWithStreamResponse(patchDataRequest, "/patches", customHeaders);

                return(fileResult.Length > 0
          ? ConvertPatchResult(fileResult, request)
          : new ContractExecutionResult(ContractExecutionStatesEnum.InternalProcessingError, "Null patch returned"));
            }
            finally
            {
                ContractExecutionStates.ClearDynamic();
            }
        }
Beispiel #6
0
        /// <summary>
        /// Processes the request for type of T.
        /// </summary>
        protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item)
        {
            try
            {
                var request = CastRequestObjectTo <TileRequest>(item);

                var filter1 = request.Filter1;
                var filter2 = request.Filter2;
                if (request.ComputeVolumesType == VolumesType.Between2Filters && !request.ExplicitFilters)
                {
                    (filter1, filter2) = FilterUtilities.AdjustFilterToFilter(request.Filter1, request.Filter2);
                }

                var trexRequest = new TRexTileRequest(
                    request.ProjectUid.Value,
                    request.Mode,
                    request.Palettes,
                    request.DesignDescriptor,
                    filter1,
                    filter2,
                    request.BoundBoxLatLon,
                    request.BoundBoxGrid,
                    request.Width,
                    request.Height,
                    AutoMapperUtility.Automapper.Map <OverridingTargets>(request.LiftBuildSettings),
                    AutoMapperUtility.Automapper.Map <LiftSettings>(request.LiftBuildSettings),
                    request.ComputeVolumesType
                    );
                var fileResult = await trexCompactionDataProxy.SendDataPostRequestWithStreamResponse(trexRequest, "/tile", customHeaders);

                using (var ms = new MemoryStream())
                {
                    fileResult.CopyTo(ms);
                    return(new TileResult(ms.ToArray()));
                }
            }
            finally
            {
                ContractExecutionStates.ClearDynamic();
            }
        }
Beispiel #7
0
        protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item)
        {
            // Note: The numPatches out parameter is ignored in favor of the same value returned in the PatchResult proper. This will be removed
            // in due course once the breaking modifications process is agreed with BC.
            try
            {
                var request = CastRequestObjectTo <PatchRequest>(item);

                var filter1 = request.Filter1;
                var filter2 = request.Filter2;
                if (request.ComputeVolType == VolumesType.Between2Filters)
                {
                    (filter1, filter2) = FilterUtilities.AdjustFilterToFilter(request.Filter1, request.Filter2);
                }
                else
                {
                    (filter1, filter2) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(filter1, filter2, request.Mode, request.ComputeVolType);
                }

                var patchDataRequest = new PatchDataRequest(
                    request.ProjectUid.Value,
                    filter1,
                    filter2,
                    request.Mode,
                    request.PatchNumber,
                    request.PatchSize,
                    AutoMapperUtility.Automapper.Map <OverridingTargets>(request.LiftBuildSettings),
                    AutoMapperUtility.Automapper.Map <LiftSettings>(request.LiftBuildSettings));

                var fileResult = await trexCompactionDataProxy.SendDataPostRequestWithStreamResponse(patchDataRequest, "/patches", customHeaders);

                return(fileResult.Length > 0
            ? ConvertPatchResult(fileResult, true)
            : CreateNullPatchReturnedResult());
            }
            finally
            {
                ContractExecutionStates.ClearDynamic();
            }
        }
Beispiel #8
0
        protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item)
        {
            try
            {
                var request = CastRequestObjectTo <SummaryVolumesRequest>(item);

                var baseFilter = request.BaseFilter;
                var topFilter  = request.TopFilter;
                if (request.VolumeCalcType == VolumesType.Between2Filters)
                {
                    if (!request.ExplicitFilters)
                    {
                        (baseFilter, topFilter) = FilterUtilities.AdjustFilterToFilter(request.BaseFilter, request.TopFilter);
                    }
                }
                else
                {
                    // Note: The use of the ReconcileTopFilterAndVolumeComputationMode() here breaks with the pattern of all the other V2
                    // end points which explicitly do not perform this step. It has been copied from the Raptor implementation of this end point
                    (baseFilter, topFilter) = FilterUtilities.ReconcileTopFilterAndVolumeComputationMode(baseFilter, topFilter, request.VolumeCalcType);
                }

                var summaryVolumesRequest = new SummaryVolumesDataRequest(
                    request.ProjectUid,
                    baseFilter,
                    topFilter,
                    request.BaseDesignDescriptor?.FileUid,
                    request.BaseDesignDescriptor?.Offset,
                    request.TopDesignDescriptor?.FileUid,
                    request.TopDesignDescriptor?.Offset,
                    request.VolumeCalcType);

                return(await trexCompactionDataProxy.SendDataPostRequest <SummaryVolumesResult, SummaryVolumesDataRequest>(summaryVolumesRequest, "/volumes/summary", customHeaders));
            }
            finally
            {
                ContractExecutionStates.ClearDynamic();
            }
        }
Beispiel #9
0
        private void ConfigurePipeline(SubGridPipelineAggregative <SubGridsRequestArgument, SimpleVolumesResponse> PipeLine)
        {
            //PipeLine.TimeToLiveSeconds := VLPDSvcLocations.VLPDPSNode_VolumePipelineTTLSeconds;
            PipeLine.RequestDescriptor = RequestDescriptor;
            //PipeLine.ExternalDescriptor := FExternalDescriptor;

            PipeLine.DataModelID = SiteModel.ID;

            Log.LogDebug($"Volume calculation extents for DM={SiteModel.ID}, Request={RequestDescriptor}: {Extents}");

            PipeLine.OverallExistenceMap     = OverallExistenceMap;
            PipeLine.ProdDataExistenceMap    = ProdDataExistenceMap;
            PipeLine.DesignSubGridOverlayMap = DesignSubgridOverlayMap;

            // Initialise a request analyzer to provide to the pipeline
            PipeLine.RequestAnalyser          = DIContext.Obtain <IRequestAnalyser>();
            PipeLine.RequestAnalyser.Pipeline = PipeLine;
            PipeLine.RequestAnalyser.WorldExtents.Assign(Extents);

            PipeLine.LiftParams = LiftParams;

            // Construct and assign the filter set into the pipeline
            IFilterSet FilterSet = FilterUtilities.ConstructFilters(new FilterSet(BaseFilter, TopFilter), VolumeType);

            if (FilterSet.Filters.Length == 3)
            {
                PipeLine.SubGridsRequestComputeStyle = SubGridsRequestComputeStyle.SimpleVolumeThreeWayCoalescing;
            }
            PipeLine.FilterSet    = FilterSet;
            PipeLine.GridDataType = GridDataType.Height;

            if (FilteredTopSurveyedSurfaces.Count > 0 || FilteredBaseSurveyedSurfaces.Count > 0)
            {
                PipeLine.IncludeSurveyedSurfaceInformation = true;
            }
        }
Beispiel #10
0
        /// <summary>
        /// Executes the progressive volumes computation returning a ProgressiveVolumesResponse with the results
        /// </summary>
        public async Task <ProgressiveVolumesResponse> ExecuteAsync()
        {
            var volumesResult         = new ProgressiveVolumesResponse();
            var resultBoundingExtents = BoundingWorldExtent3D.Null();
            var requestDescriptor     = Guid.NewGuid(); // TODO ASNodeImplInstance.NextDescriptor;

            _log.LogInformation($"#In# Performing {nameof(ComputeProgressiveVolumes_Coordinator)}.Execute for DataModel:{SiteModelID}");

            try
            {
                try
                {
                    ApplicationServiceRequestStatistics.Instance.NumProgressiveVolumeRequests.Increment();

                    // Prepare filter for use in the request
                    var resultStatus = FilterUtilities.PrepareFiltersForUse(new[] { Filter, AdditionalSpatialFilter }, SiteModelID);
                    if (resultStatus != RequestErrorStatus.OK)
                    {
                        return(volumesResult);
                    }

                    // Obtain the site model context for the request
                    _siteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(SiteModelID);

                    if (_siteModel == null)
                    {
                        return(volumesResult);
                    }

                    // Determine the number of progressions that are required and establish the required aggregation states in the aggregator
                    var numProgressions = (int)((EndDate.Ticks - StartDate.Ticks) / Interval.Ticks);
                    if ((EndDate.Ticks - StartDate.Ticks) % Interval.Ticks == 0)
                    {
                        numProgressions++;
                    }

                    if (VolumeType == VolumeComputationType.Between2Filters)
                    {
                        // One fewer progressions will be calculated as each volume in the progression is computed across the interval of two
                        // surfaces derived from production data.
                        numProgressions--;
                    }

                    if (numProgressions > ClientProgressiveHeightsLeafSubGrid.MaxNumberOfHeightLayers)
                    {
                        throw new ArgumentException($"No more than {ClientProgressiveHeightsLeafSubGrid.MaxNumberOfHeightLayers} height layers may be requested at one time");
                    }

                    // Create and configure the aggregator that contains the business logic for the underlying volume calculation
                    Aggregator = new ProgressiveVolumesCalculationsAggregator
                    {
                        SiteModel         = _siteModel,
                        LiftParams        = _liftParams,
                        CellSize          = _siteModel.CellSize,
                        VolumeType        = VolumeType,
                        CutTolerance      = CutTolerance,
                        FillTolerance     = FillTolerance,
                        AggregationStates = Enumerable
                                            .Range(VolumeType == VolumeComputationType.Between2Filters ? 1 : 0, numProgressions)
                                            .Select(x => StartDate + x * Interval)
                                            .Select(d => new ProgressiveVolumeAggregationState(_siteModel.CellSize)
                        {
                            Date          = d,
                            CutTolerance  = CutTolerance,
                            FillTolerance = FillTolerance
                        }).ToArray()
                    };

                    // Create and configure the volumes calculation engine
                    var computeVolumes = new ProgressiveVolumesCalculator
                    {
                        RequestDescriptor = requestDescriptor,
                        SiteModel         = _siteModel,
                        Aggregator        = Aggregator,
                        Filter            = Filter,
                        VolumeType        = VolumeType,
                        LiftParams        = _liftParams,
                        StartDate         = StartDate,
                        EndDate           = EndDate,
                        Interval          = Interval
                    };

                    InitialiseVolumesCalculator(computeVolumes);

                    // Perform the volume computation
                    if (computeVolumes.ComputeVolumeInformation())
                    {
                        resultStatus = RequestErrorStatus.OK;
                    }
                    else
                    {
                        resultStatus = computeVolumes.AbortedDueToTimeout ? RequestErrorStatus.AbortedDueToPipelineTimeout : RequestErrorStatus.Unknown;
                    }

                    if (resultStatus != RequestErrorStatus.OK)
                    {
                        _log.LogInformation($"Progressive volume result: Failure, error = {resultStatus}");

                        // Send the (empty) results back to the caller
                        return(volumesResult);
                    }

                    // Instruct the Aggregator to perform any finalization logic before reading out the results
                    Aggregator.Finalise();

                    var noProductionDataCount   = 0;
                    var invalidPlanExtentsCount = 0;

                    foreach (var state in Aggregator.AggregationStates)
                    {
                        _log.LogInformation($"#Result# Progressive volume result: Cut={state.CutFillVolume.CutVolume:F3}, Fill={state.CutFillVolume.FillVolume:F3}, Area={state.CoverageArea:F3}");

                        if (!state.BoundingExtents.IsValidPlanExtent)
                        {
                            if (state.CoverageArea == 0 && state.CutFillVolume.CutVolume == 0 && state.CutFillVolume.FillVolume == 0)
                            {
                                noProductionDataCount++;
                            }
                            else
                            {
                                invalidPlanExtentsCount++;
                            }
                        }
                    }

                    if (noProductionDataCount == Aggregator.AggregationStates.Length)
                    {
                        resultStatus = RequestErrorStatus.NoProductionDataFound;
                    }
                    else if (invalidPlanExtentsCount == Aggregator.AggregationStates.Length)
                    {
                        resultStatus = RequestErrorStatus.InvalidPlanExtents;
                    }

                    if (resultStatus == RequestErrorStatus.NoProductionDataFound || resultStatus == RequestErrorStatus.InvalidPlanExtents)
                    {
                        _log.LogInformation($"Progressive volume invalid plan extents or no data found: {resultStatus}");
                    }

                    volumesResult.ResultStatus = resultStatus;

                    if (resultStatus == RequestErrorStatus.OK)
                    {
                        volumesResult.Volumes = Aggregator.AggregationStates.Select(aggregator => new ProgressiveVolumeResponseItem
                        {
                            Date   = aggregator.Date,
                            Volume = new SimpleVolumesResponse
                            {
                                Cut  = aggregator.CutFillVolume.CutVolume,
                                Fill = aggregator.CutFillVolume.FillVolume,
                                TotalCoverageArea  = aggregator.CoverageArea,
                                CutArea            = aggregator.CutArea,
                                FillArea           = aggregator.FillArea,
                                BoundingExtentGrid = aggregator.BoundingExtents,
                                BoundingExtentLLH  = resultBoundingExtents
                            }
                        }).ToArray();
                    }
                }
                finally
                {
                    ApplicationServiceRequestStatistics.Instance.NumProgressiveVolumeRequestsCompleted.Increment();
                    if (volumesResult.ResultStatus != RequestErrorStatus.OK)
                    {
                        ApplicationServiceRequestStatistics.Instance.NumProgressiveVolumeRequestsFailed.Increment();
                    }
                }
            }
            catch (Exception e)
            {
                _log.LogError(e, $"Failed to compute the progressive volumes. Site Model ID: {SiteModelID}");
            }

            return(volumesResult);
        }
Beispiel #11
0
        /// <summary>
        /// Executes the profiler
        /// </summary>
        public async Task <ProfileRequestResponse <T> > ExecuteAsync(ProfileRequestArgument_ApplicationService arg)
        {
            _log.LogInformation("Start execution");

            try
            {
                if (arg.Filters?.Filters != null && arg.Filters.Filters.Length > 0)
                {
                    // Prepare the filters for use in profiling operations. Failure to prepare any filter results in this request terminating
                    if (!arg.Filters.Filters.Select(x => FilterUtilities.PrepareFilterForUse(x, arg.ProjectID)).All(x => x == RequestErrorStatus.OK))
                    {
                        return(new ProfileRequestResponse <T> {
                            ResultStatus = RequestErrorStatus.FailedToPrepareFilter
                        });
                    }
                }

                var arg2 = new ProfileRequestArgument_ClusterCompute
                {
                    ProfileTypeRequired      = arg.ProfileTypeRequired,
                    ProfileStyle             = arg.ProfileStyle,
                    ProjectID                = arg.ProjectID,
                    Filters                  = arg.Filters,
                    ReferenceDesign          = arg.ReferenceDesign,
                    ReturnAllPassesAndLayers = arg.ReturnAllPassesAndLayers,
                    TRexNodeID               = arg.TRexNodeID,
                    VolumeType               = arg.VolumeType,
                    Overrides                = arg.Overrides,
                    LiftParams               = arg.LiftParams
                };

                // Perform coordinate conversion on the argument before broadcasting it:
                if (arg.PositionsAreGrid)
                {
                    arg2.NEECoords = new[] { arg.StartPoint, arg.EndPoint }.Select(x => new XYZ(x.Lon, x.Lat)).ToArray();
                }
                else
                {
                    var siteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(arg.ProjectID);

                    if (siteModel != null)
                    {
                        arg2.NEECoords = DIContext.Obtain <ICoreXWrapper>().WGS84ToCalibration(
                            siteModel.CSIB(),
                            new[] { arg.StartPoint, arg.EndPoint }
                            .ToCoreX_WGS84Point(),
                            CoreX.Types.InputAs.Radians)
                                         .ToTRex_XYZ();
                    }
                }

                var request         = new ProfileRequest_ClusterCompute <T>();
                var profileResponse = await request.ExecuteAsync(arg2);

                profileResponse.GridDistanceBetweenProfilePoints = MathUtilities.Hypot(arg2.NEECoords[1].X - arg2.NEECoords[0].X, arg2.NEECoords[1].Y - arg2.NEECoords[0].Y);

                //... and then sort them to get the final result, as well as removing initial and duplicate null values
                // Remove null cells in the profiles list. Null cells are defined by cells with null CellLastHeight.
                // All duplicate null cells will be replaced by a by single null cell entry
                int firstNonNullIndex = 0;
                var _profileCells     = profileResponse?.ProfileCells?.OrderBy(x => x.Station).ToList();
                if (_profileCells != null)
                {
                    profileResponse.ProfileCells = _profileCells.Where((x, i) =>
                    {
                        // Remove all leading nulls
                        if (_profileCells[i].IsNull() && i == firstNonNullIndex)
                        {
                            firstNonNullIndex++;
                            return(false);
                        }

                        // Collapse all interior nulls to single nulls, unless the null is at the end. Leave any single terminating null
                        return(i == 0 || !_profileCells[i].IsNull() || (_profileCells[i].IsNull() && !_profileCells[i - 1].IsNull()));
                    }).ToList();
                }

                // Return the care package to the caller
                return(profileResponse);
            }
            finally
            {
                _log.LogInformation("End execution");
            }
        }
Beispiel #12
0
        /// <summary>
        /// Builds the pipeline configured per the supplied state ready to execute the request
        /// </summary>
        public bool Build()
        {
            // Todo: This method is left as async as a reminder that the GetExistenveMap workflows could either be async (as they
            // potentially read from the persistent store), and/or they couild be cached in the site model designs/surveyed surfaces contexts
            // See Jira CCSSSCON-1481
            try
            {
                var stopWatch = Stopwatch.StartNew();

                // Ensure the task is initialised with the request descriptor
                Task.RequestDescriptor = RequestDescriptor;

                // Ensure the Task grid data type matches the pipeline processor
                Task.GridDataType = GridDataType;

                // Introduce the task and the pipeline to each other
                Pipeline.PipelineTask = Task;
                Task.PipeLine         = Pipeline;

                (Pipeline as ISubGridPipelineBase <TSubGridsRequestArgument>).CustomArgumentInitializer = CustomArgumentInitializer;

                if ((Filters?.Filters?.Length ?? 0) == 0)
                {
                    _log.LogError($"Filters supplied to pipeline processor is null or empty, replacing with single default filter");
                    Filters = new FilterSet(new CombinedFilter());
                }

                // Construct an aggregated set of excluded surveyed surfaces for the filters used in the query
                foreach (var filter in Filters.Filters)
                {
                    if (filter != null && SurveyedSurfaceExclusionList.Length > 0)
                    {
                        SurveyedSurfaceExclusionList = new Guid[filter.AttributeFilter.SurveyedSurfaceExclusionList.Length];
                        Array.Copy(filter.AttributeFilter.SurveyedSurfaceExclusionList, SurveyedSurfaceExclusionList,
                                   SurveyedSurfaceExclusionList.Length);
                    }
                }

                // Get the SiteModel for the request
                SiteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(DataModelID);
                if (SiteModel == null)
                {
                    Response.ResultStatus = RequestErrorStatus.NoSuchDataModel;
                    return(false);
                }

                SpatialExtents = SiteModel.GetAdjustedDataModelSpatialExtents(SurveyedSurfaceExclusionList);

                if (!SpatialExtents.IsValidPlanExtent)
                {
                    Response.ResultStatus = RequestErrorStatus.FailedToRequestDatamodelStatistics; // Or there was no data in the model
                    return(false);
                }

                // Get the current production data existence map from the site model
                ProdDataExistenceMap = SiteModel.ExistenceMap;

                // Obtain the sub grid existence map for the project
                // Retrieve the existence map for the datamodel
                OverallExistenceMap = new SubGridTreeSubGridExistenceBitMask {
                    CellSize = SubGridTreeConsts.SubGridTreeDimension * SiteModel.CellSize
                };

                if (RequireSurveyedSurfaceInformation)
                {
                    // Obtain local reference to surveyed surfaces (lock free access)
                    var localSurveyedSurfaces = SiteModel.SurveyedSurfaces;

                    if (localSurveyedSurfaces != null)
                    {
                        // Construct two filtered surveyed surface lists to act as a rolling pair used as arguments
                        // to the ProcessSurveyedSurfacesForFilter method
                        var filterSurveyedSurfaces   = DIContext.Obtain <ISurveyedSurfaces>();
                        var filteredSurveyedSurfaces = DIContext.Obtain <ISurveyedSurfaces>();

                        SurveyedSurfacesExcludedViaTimeFiltering = Filters.Filters.Length > 0;

                        foreach (var filter in Filters.Filters)
                        {
                            if (!localSurveyedSurfaces.ProcessSurveyedSurfacesForFilter(DataModelID, filter,
                                                                                        filteredSurveyedSurfaces, filterSurveyedSurfaces, OverallExistenceMap))
                            {
                                Response.ResultStatus = RequestErrorStatus.FailedToRequestSubgridExistenceMap;
                                return(false);
                            }

                            SurveyedSurfacesExcludedViaTimeFiltering &= filterSurveyedSurfaces.Count == 0;
                        }
                    }
                }

                OverallExistenceMap.SetOp_OR(ProdDataExistenceMap);

                foreach (var filter in Filters.Filters)
                {
                    if (filter != null)
                    {
                        if (!DesignFilterUtilities.ProcessDesignElevationsForFilter(SiteModel, filter, OverallExistenceMap))
                        {
                            Response.ResultStatus = RequestErrorStatus.NoDesignProvided;
                            return(false);
                        }

                        Response.ResultStatus = FilterUtilities.PrepareFilterForUse(filter, DataModelID);
                        if (Response.ResultStatus != RequestErrorStatus.OK)
                        {
                            _log.LogInformation($"PrepareFilterForUse failed: Datamodel={DataModelID}");
                            return(false);
                        }
                    }
                }

                // Adjust the extents we have been given to encompass the spatial extent of the supplied filters (if any)
                Filters.ApplyFilterAndSubsetBoundariesToExtents(SpatialExtents);

                // If this request involves a relationship with a design then ensure the existence map
                // for the design is loaded in to memory to allow the request pipeline to confine
                // sub grid requests that overlay the actual design
                if (RequestRequiresAccessToDesignFileExistenceMap)
                {
                    if (CutFillDesign == null || CutFillDesign.DesignID == Guid.Empty)
                    {
                        _log.LogError($"No design provided to cut fill, summary volume or thickness overlay render request for datamodel {DataModelID}");
                        Response.ResultStatus = RequestErrorStatus.NoDesignProvided;
                        return(false);
                    }

                    DesignSubGridOverlayMap = GetExistenceMaps().GetSingleExistenceMap(DataModelID, Consts.EXISTENCE_MAP_DESIGN_DESCRIPTOR, CutFillDesign.DesignID);

                    if (DesignSubGridOverlayMap == null)
                    {
                        _log.LogError($"Failed to request sub grid overlay index for design {CutFillDesign.DesignID} in datamodel {DataModelID}");
                        Response.ResultStatus = RequestErrorStatus.NoDesignProvided;
                        return(false);
                    }

                    DesignSubGridOverlayMap.CellSize = SubGridTreeConsts.SubGridTreeDimension * SiteModel.CellSize;
                }

                // Impose the final restriction on the spatial extents from the client context
                SpatialExtents.Intersect(OverrideSpatialExtents);

                // Introduce the Request analyzer to the pipeline and spatial extents it requires
                RequestAnalyser.Pipeline     = Pipeline;
                RequestAnalyser.WorldExtents = SpatialExtents;

                ConfigurePipeline();

                _log.LogInformation($"Pipeline processor build phase completed in {stopWatch.Elapsed}");

                return(true);
            }
            catch (Exception e)
            {
                _log.LogError(e, "Exception occurred in pipeline builder");
                throw;
            }
        }
Beispiel #13
0
        ///  <summary>
        ///  Builds a fully analyzed vector of profiled cells from the list of cell passed to it
        ///  </summary>
        public override bool Analyze(List <SummaryVolumeProfileCell> profileCells, ISubGridSegmentCellPassIterator cellPassIterator)
        {
            Log.LogDebug($"Analyze Summary Volume ProfileCells. Processing {profileCells.Count}");

            var                CurrentSubgridOrigin = new SubGridCellAddress(int.MaxValue, int.MaxValue);
            ISubGrid           SubGrid        = null;
            IServerLeafSubGrid _SubGridAsLeaf = null;

            profileCell = null;

            // Construct the set of requestors to query elevation sub grids needed for the summary volume calculations.
            var filterSet = FilterUtilities.ConstructFilters(FilterSet, VolumeType);

            IntermediaryFilterRequired = filterSet.Filters.Length == 3;
            var utilities = DIContext.Obtain <IRequestorUtilities>();

            Requestors = utilities.ConstructRequestors(null, SiteModel, Overrides, LiftParams,
                                                       utilities.ConstructRequestorIntermediaries(SiteModel, filterSet, true, GridDataType.HeightAndTime),
                                                       AreaControlSet.CreateAreaControlSet(), PDExistenceMap);

            var cellOverrideMask = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled);

            for (int I = 0; I < profileCells.Count; I++)
            {
                profileCell = profileCells[I];

                // get sub grid origin for cell address
                var thisSubgridOrigin = new SubGridCellAddress(profileCell.OTGCellX >> SubGridTreeConsts.SubGridIndexBitsPerLevel,
                                                               profileCell.OTGCellY >> SubGridTreeConsts.SubGridIndexBitsPerLevel);

                if (!CurrentSubgridOrigin.Equals(thisSubgridOrigin)) // if we have a new sub grid to fetch
                {
                    // if we have an existing sub grid and a change in sub grid detected process the current sub grid profile cell list
                    if (SubGrid != null)
                    {
                        ProcessSubGroup(new SubGridCellAddress(CurrentSubgridOrigin.X << SubGridTreeConsts.SubGridIndexBitsPerLevel, CurrentSubgridOrigin.Y << SubGridTreeConsts.SubGridIndexBitsPerLevel),
                                        PDExistenceMap[CurrentSubgridOrigin.X, CurrentSubgridOrigin.Y], cellOverrideMask);
                        cellOverrideMask.Clear();
                    }

                    SubGrid     = null;
                    cellCounter = 0;

                    // Does the sub grid tree contain this node in it's existence map? if so get sub grid
                    if (PDExistenceMap[thisSubgridOrigin.X, thisSubgridOrigin.Y])
                    {
                        SubGrid = SubGridTrees.Server.Utilities.SubGridUtilities.LocateSubGridContaining
                                      (SiteModel.PrimaryStorageProxy, SiteModel.Grid, profileCell.OTGCellX, profileCell.OTGCellY, SiteModel.Grid.NumLevels, false, false);
                    }

                    _SubGridAsLeaf = SubGrid as ServerSubGridTreeLeaf;
                    if (_SubGridAsLeaf == null)
                    {
                        continue;
                    }

                    CurrentSubgridOrigin = thisSubgridOrigin; // all good to proceed with this sub grid
                }

                profileCellList[cellCounter++] = profileCell; // add cell to list to process for this sub grid
                cellOverrideMask.SetBit(profileCell.OTGCellX & SubGridTreeConsts.SubGridLocalKeyMask, profileCell.OTGCellY & SubGridTreeConsts.SubGridLocalKeyMask);
            }

            if (cellCounter > 0 && SubGrid != null) // Make sure we process last list
            {
                ProcessSubGroup(new SubGridCellAddress(CurrentSubgridOrigin.X << SubGridTreeConsts.SubGridIndexBitsPerLevel, CurrentSubgridOrigin.Y << SubGridTreeConsts.SubGridIndexBitsPerLevel),
                                PDExistenceMap[CurrentSubgridOrigin.X, CurrentSubgridOrigin.Y], cellOverrideMask);
            }

            return(true);
        }
Beispiel #14
0
        /// <summary>
        /// Executes the profiler
        /// </summary>
        public async Task <StationOffsetReportRequestResponse_ApplicationService> ExecuteAsync(StationOffsetReportRequestArgument_ApplicationService arg)
        {
            _log.LogInformation($"Start {nameof(ComputeStationOffsetReportExecutor_ApplicationService)}");

            try
            {
                if (arg.Filters?.Filters != null && arg.Filters.Filters.Length > 0)
                {
                    // Prepare the filters for use in stationOffset operations. Failure to prepare any filter results in this request terminating
                    if (!(arg.Filters.Filters.Select(x => FilterUtilities.PrepareFilterForUse(x, arg.ProjectID)).All(x => x == RequestErrorStatus.OK)))
                    {
                        return(new StationOffsetReportRequestResponse_ApplicationService {
                            ResultStatus = RequestErrorStatus.FailedToPrepareFilter
                        });
                    }
                }

                // keep alignment design knowledge here and pass points
                var argClusterCompute = new StationOffsetReportRequestArgument_ClusterCompute
                {
                    ProjectID         = arg.ProjectID,
                    Filters           = arg.Filters,
                    ReferenceDesign   = arg.ReferenceDesign,
                    TRexNodeID        = arg.TRexNodeID,
                    ReportElevation   = arg.ReportElevation,
                    ReportCmv         = arg.ReportCmv,
                    ReportMdp         = arg.ReportMdp,
                    ReportPassCount   = arg.ReportPassCount,
                    ReportTemperature = arg.ReportTemperature,
                    ReportCutFill     = arg.ReportCutFill,
                    Points            = new List <StationOffsetPoint>(),
                    Overrides         = arg.Overrides,
                    LiftParams        = arg.LiftParams
                };

                var siteModel   = DIContext.ObtainRequired <ISiteModels>().GetSiteModel(arg.ProjectID);
                var designFiles = DIContext.ObtainRequired <IDesignFiles>();

                // alignment will convert interval/offsets into northing/eastings for the project
                var alignmentDesign = designFiles.Lock(arg.AlignmentDesignUid, siteModel, siteModel.CellSize, out var loadResult) as SVLAlignmentDesign;

                if (alignmentDesign == null || loadResult != DesignLoadResult.Success)
                {
                    return(new StationOffsetReportRequestResponse_ApplicationService {
                        ReturnCode = ReportReturnCode.NoData, ResultStatus = RequestErrorStatus.NoDesignProvided
                    });
                }

                try
                {
                    argClusterCompute.Points = alignmentDesign.GetOffsetPointsInNEE(arg.CrossSectionInterval, arg.StartStation, arg.EndStation, arg.Offsets, out var calcResult);

                    _log.LogInformation($"{nameof(StationOffsetReportRequestResponse_ApplicationService)}: pointCount: {argClusterCompute.Points.Count}, calcResult: {calcResult}");

                    if (argClusterCompute.Points.Count == 0)
                    {
                        return(new StationOffsetReportRequestResponse_ApplicationService {
                            ReturnCode = ReportReturnCode.NoData, ResultStatus = RequestErrorStatus.NoProductionDataFound
                        });
                    }
                }
                finally
                {
                    designFiles.UnLock(arg.AlignmentDesignUid, alignmentDesign);
                }

                var request = new StationOffsetReportRequest_ClusterCompute();
                var clusterComputeResponse = await request.ExecuteAsync(argClusterCompute);

                // Return the core package to the caller
                var applicationResponse = new StationOffsetReportRequestResponse_ApplicationService
                {
                    ReturnCode   = clusterComputeResponse.ReturnCode,
                    ResultStatus = clusterComputeResponse.ResultStatus
                };
                applicationResponse.LoadStationOffsets(clusterComputeResponse.StationOffsetRows);

                _log.LogInformation($"End {nameof(ComputeStationOffsetReportExecutor_ApplicationService)}: ReturnCode {applicationResponse.ReturnCode}.");
                return(applicationResponse);
            }
            catch (Exception e)
            {
                _log.LogError(e, $"{nameof(StationOffsetReportRequestResponse_ApplicationService)}: Unexpected exception.");
                throw;
            }
        }
Beispiel #15
0
        /// <summary>
        /// Executor that implements requesting and rendering sub grid information to create the rendered tile
        /// </summary>
        public async Task <SKBitmap> ExecuteAsync()
        {
            // WorkingColorPalette  : TICDisplayPaletteBase;

            _log.LogInformation($"Performing Execute for DataModel:{DataModelID}, Mode={Mode}");

            ApplicationServiceRequestStatistics.Instance.NumMapTileRequests.Increment();

            /*
             * if Assigned(ASNodeImplInstance.RequestCancellations) and
             * ASNodeImplInstance.RequestCancellations.IsRequestCancelled(FExternalDescriptor) then
             * begin
             * if ...SvcLocations.Debug_LogDebugRequestCancellationToFile then
             *   SIGLogMessage.PublishNoODS(Self, 'Request cancelled: ' + FExternalDescriptor.ToString, ...Debug);
             *
             * ResultStatus  = ...RequestHasBeenCancelled;
             * InterlockedIncrement64(ASNodeRequestStats.NumMapTileRequestsCancelled);
             * Exit;
             * end;
             *
             * // The governor is intended to restrict the numbers of heavy weight processes
             * // such as pipelines that interact with the PC layer to request sub grids
             * ScheduledWithGovernor  = ASNodeImplInstance.Governor.Schedule(FExternalDescriptor, Self, gqWMS, ResultStatus);
             * if not ScheduledWithGovernor then
             * Exit;
             */

            var RequestDescriptor = Guid.NewGuid();

            if (_log.IsDebugEnabled())
            {
                if (CoordsAreGrid)
                {
                    _log.LogDebug($"RenderPlanViewTiles Execute: Performing render for request={RequestDescriptor} Args: Project={DataModelID}, Mode={Mode}, CutFillDesign=''{CutFillDesign}'' " +
                                  $"Bound[BL/TR:X/Y]=({BLPoint.X} {BLPoint.Y}, {TRPoint.X} {TRPoint.Y}), Width={NPixelsX}, Height={NPixelsY}");
                }
                else
                {
                    _log.LogDebug($"RenderPlanViewTiles Execute: Performing render for request={RequestDescriptor} Args: Project={DataModelID}, Mode={Mode}, CutFillDesign=''{CutFillDesign}'' " +
                                  $"Bound[BL/TR:Lon/Lat]=({BLPoint.X} {BLPoint.Y}, {TRPoint.X} {TRPoint.Y}), Width={NPixelsX}, Height={NPixelsY}");
                }

                // Include the details of the filters with the logged tile parameters
                if (Filters != null)
                {
                    for (var i = 0; i < Filters.Filters.Length; i++)
                    {
                        _log.LogDebug($"Filter({i}): {Filters.Filters[i]}");
                    }
                }
            }

            // Determine the grid (NEE) coordinates of the bottom/left, top/right WGS-84 positions
            // given the project's coordinate system. If there is no coordinate system then exit.

            var SiteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(DataModelID);

            if (SiteModel == null)
            {
                _log.LogWarning($"Failed to locate site model {DataModelID}");
                return(null);
            }

            _log.LogInformation($"Got Site model {DataModelID}, production data extents are {SiteModel.SiteModelExtent}");

            LLHCoords = new[]
            {
                new XYZ(BLPoint.X, BLPoint.Y, 0),
                new XYZ(TRPoint.X, TRPoint.Y, 0),
                new XYZ(BLPoint.X, TRPoint.Y, 0),
                new XYZ(TRPoint.X, BLPoint.Y, 0)
            };
            _log.LogInformation($"LLHCoords for tile request {string.Concat(LLHCoords)}, CoordsAreGrid {CoordsAreGrid}");

            if (CoordsAreGrid)
            {
                NEECoords = LLHCoords;
            }
            else
            {
                NEECoords = DIContext
                            .Obtain <ICoreXWrapper>()
                            .LLHToNEE(SiteModel.CSIB(), LLHCoords.ToCoreX_XYZ(), CoreX.Types.InputAs.Radians)
                            .ToTRex_XYZ();
            }
            _log.LogInformation($"After conversion NEECoords are {string.Concat(NEECoords)}");

            WorldTileHeight = MathUtilities.Hypot(NEECoords[0].X - NEECoords[2].X, NEECoords[0].Y - NEECoords[2].Y);
            WorldTileWidth  = MathUtilities.Hypot(NEECoords[0].X - NEECoords[3].X, NEECoords[0].Y - NEECoords[3].Y);

            var dx = NEECoords[2].X - NEECoords[0].X;
            var dy = NEECoords[2].Y - NEECoords[0].Y;

            // Calculate the tile rotation as the mathematical angle turned from 0 (due east) to the vector defined by dy/dx
            TileRotation = Math.Atan2(dy, dx);

            // Convert TileRotation to represent the angular deviation rather than a bearing
            TileRotation = (Math.PI / 2) - TileRotation;

            RotatedTileBoundingExtents.SetInverted();
            NEECoords.ForEach(xyz => RotatedTileBoundingExtents.Include(xyz.X, xyz.Y));

            _log.LogInformation($"Tile render executing across tile: [Rotation:{TileRotation}, {MathUtilities.RadiansToDegrees(TileRotation)} degrees] " +
                                $" [BL:{NEECoords[0].X}, {NEECoords[0].Y}, TL:{NEECoords[2].X},{NEECoords[2].Y}, " +
                                $"TR:{NEECoords[1].X}, {NEECoords[1].Y}, BR:{NEECoords[3].X}, {NEECoords[3].Y}] " +
                                $"World Width, Height: {WorldTileWidth}, {WorldTileHeight}, Rotated bounding extents: {RotatedTileBoundingExtents}");

            // Construct the renderer, configure it, and set it on its way
            //  WorkingColorPalette = Nil;

            using (var Renderer = new PlanViewTileRenderer())
            {
                try
                {
                    // Intersect the site model extents with the extents requested by the caller
                    var adjustedSiteModelExtents = SiteModel.GetAdjustedDataModelSpatialExtents(null);

                    _log.LogInformation($"Calculating intersection of bounding box and site model {DataModelID}:{adjustedSiteModelExtents}");

                    var dataSelectionExtent = new BoundingWorldExtent3D(RotatedTileBoundingExtents);
                    dataSelectionExtent.Intersect(adjustedSiteModelExtents);
                    if (!dataSelectionExtent.IsValidPlanExtent)
                    {
                        ResultStatus = RequestErrorStatus.InvalidCoordinateRange;
                        _log.LogInformation($"Site model extents {adjustedSiteModelExtents}, do not intersect RotatedTileBoundingExtents {RotatedTileBoundingExtents}");

                        using var mapView = new MapSurface();

                        mapView.SetBounds(NPixelsX, NPixelsY);

                        var canvas = mapView.BitmapCanvas;
                        mapView.BitmapCanvas = null;

                        return(canvas);
                    }

                    // Compute the override cell boundary to be used when processing cells in the sub grids
                    // selected as a part of this pipeline
                    // Increase cell boundary by one cell to allow for cells on the boundary that cross the boundary

                    SubGridTree.CalculateIndexOfCellContainingPosition(dataSelectionExtent.MinX,
                                                                       dataSelectionExtent.MinY, SiteModel.CellSize, SubGridTreeConsts.DefaultIndexOriginOffset,
                                                                       out var CellExtents_MinX, out var CellExtents_MinY);
                    SubGridTree.CalculateIndexOfCellContainingPosition(dataSelectionExtent.MaxX,
                                                                       dataSelectionExtent.MaxY, SiteModel.CellSize, SubGridTreeConsts.DefaultIndexOriginOffset,
                                                                       out var CellExtents_MaxX, out var CellExtents_MaxY);

                    var CellExtents = new BoundingIntegerExtent2D(CellExtents_MinX, CellExtents_MinY, CellExtents_MaxX, CellExtents_MaxY);
                    CellExtents.Expand(1);

                    var filterSet = FilterUtilities.ConstructFilters(Filters, VolumeType);
                    // Construct PipelineProcessor
                    using var processor = DIContext.Obtain <IPipelineProcessorFactory>().NewInstanceNoBuild <SubGridsRequestArgument>(
                              RequestDescriptor,
                              DataModelID,
                              GridDataFromModeConverter.Convert(Mode),
                              new SubGridsPipelinedResponseBase(),
                              filterSet,
                              CutFillDesign,
                              DIContext.Obtain <Func <PipelineProcessorTaskStyle, ITRexTask> >()(PipelineProcessorTaskStyle.PVMRendering),
                              DIContext.Obtain <Func <PipelineProcessorPipelineStyle, ISubGridPipelineBase> >()(PipelineProcessorPipelineStyle.DefaultProgressive),
                              DIContext.Obtain <IRequestAnalyser>(),
                              Utilities.DisplayModeRequireSurveyedSurfaceInformation(Mode) && Utilities.FilterRequireSurveyedSurfaceInformation(Filters),
                              requestRequiresAccessToDesignFileExistenceMap: Utilities.RequestRequiresAccessToDesignFileExistenceMap(Mode, CutFillDesign),
                              CellExtents,
                              LiftParams
                              );
                    if (filterSet.Filters.Length == 3)
                    {
                        var pipeline = processor.Pipeline as SubGridPipelineProgressive <SubGridsRequestArgument, SubGridRequestsResponse>;
                        pipeline.SubGridsRequestComputeStyle = SubGridsRequestComputeStyle.SimpleVolumeThreeWayCoalescing;
                    }

                    // Set the PVM rendering rexTask parameters for progressive processing
                    processor.Task.TRexNodeID = RequestingTRexNodeID;
                    ((IPVMRenderingTask)processor.Task).TileRenderer = Renderer;

                    // Set the spatial extents of the tile boundary rotated into the north reference frame of the cell coordinate system to act as
                    // a final restriction of the spatial extent used to govern data requests
                    processor.OverrideSpatialExtents.Assign(RotatedTileBoundingExtents);

                    // Prepare the processor
                    if (!processor.Build())
                    {
                        _log.LogError($"Failed to build pipeline processor for request to model {SiteModel.ID}");
                        ResultStatus = RequestErrorStatus.FailedToConfigureInternalPipeline;
                        return(null);
                    }

                    // Test to see if the tile can be satisfied with a representational render indicating where
                    // data is but not what it is (this is useful when the zoom level is far enough away that we
                    // cannot meaningfully render the data). If the size of s sub grid is smaller than
                    // the size of a pixel in the requested tile then do this. Just check the X dimension
                    // as the data display is isotropic.
                    // TODO: Could this be done before creation of the pipeline processor?
                    if (Utilities.SubGridShouldBeRenderedAsRepresentationalDueToScale(WorldTileWidth, WorldTileHeight, NPixelsX, NPixelsY, processor.OverallExistenceMap.CellSize))
                    {
                        return(RenderTileAsRepresentationalDueToScale(processor.OverallExistenceMap)); // There is no need to do anything else
                    }

                    /* TODO - Create a scaled palette to use when rendering the data
                     * // Create a scaled palette to use when rendering the data
                     * if not CreateAndInitialiseWorkingColorPalette then
                     *  begin
                     *  SIGLogMessage.PublishNoODS(Self, Format('Failed to create and initialise working color palette for data: %s in datamodel %d', [TypInfo.GetEnumName(TypeInfo(TICDisplayMode), Ord(FMode)), FDataModelID]), ...Warning);
                     *  Exit;
                     *  end;
                     */

                    // Renderer.WorkingPalette = WorkingColorPalette;

                    Renderer.IsWhollyInTermsOfGridProjection = true; // Ensure the renderer knows we are using grid projection coordinates

                    Renderer.SetBounds(RotatedTileBoundingExtents.CenterX - WorldTileWidth / 2,
                                       RotatedTileBoundingExtents.CenterY - WorldTileHeight / 2,
                                       WorldTileWidth, WorldTileHeight,
                                       NPixelsX, NPixelsY);
                    Renderer.TileRotation = TileRotation;

                    var performRenderStopWatch = Stopwatch.StartNew();
                    ResultStatus = Renderer.PerformRender(Mode, processor, ColorPalettes, Filters, LiftParams);
                    _log.LogInformation($"Renderer.PerformRender completed in {performRenderStopWatch.Elapsed}");

                    if (processor.Response.ResultStatus == RequestErrorStatus.OK)
                    {
                        var canvas = Renderer.Displayer.MapView.BitmapCanvas;
                        Renderer.Displayer.MapView.BitmapCanvas = null;
                        return(canvas);
                    }
                }
                catch (Exception e)
                {
                    _log.LogError(e, "Exception occurred");
                    ResultStatus = RequestErrorStatus.Exception;
                }
            }

            return(null);
        }
Beispiel #16
0
        /// <summary>
        /// Executes the simple volumes computation returning a SimpleVolumesResponse with the results
        /// </summary>
        public SimpleVolumesResponse Execute()
        {
            var volumesResult         = new SimpleVolumesResponse();
            var resultBoundingExtents = BoundingWorldExtent3D.Null();
            var requestDescriptor     = Guid.NewGuid(); // TODO ASNodeImplInstance.NextDescriptor;

            Log.LogInformation($"#In# Performing {nameof(ComputeSimpleVolumes_Coordinator)}.Execute for DataModel:{SiteModelID}");

            try
            {
                try
                {
                    ApplicationServiceRequestStatistics.Instance.NumSimpleVolumeRequests.Increment();

                    // Prepare filters for use in the request
                    var resultStatus = FilterUtilities.PrepareFiltersForUse(new[] { BaseFilter, TopFilter, AdditionalSpatialFilter }, SiteModelID);
                    if (resultStatus != RequestErrorStatus.OK)
                    {
                        return(volumesResult);
                    }

                    // Obtain the site model context for the request
                    siteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(SiteModelID);

                    if (siteModel == null)
                    {
                        return(volumesResult);
                    }

                    // Create and configure the aggregator that contains the business logic for the
                    // underlying volume calculation
                    Aggregator = new SimpleVolumesCalculationsAggregator
                    {
                        SiteModel     = siteModel,
                        LiftParams    = LiftParams,
                        CellSize      = siteModel.CellSize,
                        VolumeType    = VolumeType,
                        CutTolerance  = CutTolerance,
                        FillTolerance = FillTolerance
                    };

                    // Create and configure the volumes calculation engine
                    var computeVolumes = new VolumesCalculator
                    {
                        RequestDescriptor = requestDescriptor,
                        SiteModel         = siteModel,
                        Aggregator        = Aggregator,
                        BaseFilter        = BaseFilter,
                        TopFilter         = TopFilter,
                        VolumeType        = VolumeType,
                        LiftParams        = LiftParams
                    };

                    InitialiseVolumesCalculator(computeVolumes);

                    // Perform the volume computation
                    if (computeVolumes.ComputeVolumeInformation())
                    {
                        resultStatus = RequestErrorStatus.OK;
                    }
                    else
                    if (computeVolumes.AbortedDueToTimeout)
                    {
                        resultStatus = RequestErrorStatus.AbortedDueToPipelineTimeout;
                    }
                    else
                    {
                        resultStatus = RequestErrorStatus.Unknown;
                    }

                    if (resultStatus != RequestErrorStatus.OK)
                    {
                        Log.LogInformation($"Summary volume result: Failure, error = {resultStatus}");

                        // Send the (empty) results back to the caller
                        return(volumesResult);
                    }

                    // Instruct the Aggregator to perform any finalization logic before reading out the results
                    Aggregator.Finalise();

                    Log.LogInformation($"#Result# Summary volume result: Cut={Aggregator.CutFillVolume.CutVolume:F3}, Fill={Aggregator.CutFillVolume.FillVolume:F3}, Area={Aggregator.CoverageArea:F3}");

                    if (!Aggregator.BoundingExtents.IsValidPlanExtent)
                    {
                        if (Aggregator.CoverageArea == 0 && Aggregator.CutFillVolume.CutVolume == 0 && Aggregator.CutFillVolume.FillVolume == 0)
                        {
                            resultStatus = RequestErrorStatus.NoProductionDataFound;
                        }
                        else
                        {
                            resultStatus = RequestErrorStatus.InvalidPlanExtents;
                        }

                        Log.LogInformation($"Summary volume invalid PlanExtents or no data found: {resultStatus}");

                        return(volumesResult);
                    }

                    // Fill in the result object to pass back to the caller
                    volumesResult.Cut  = Aggregator.CutFillVolume.CutVolume;
                    volumesResult.Fill = Aggregator.CutFillVolume.FillVolume;
                    volumesResult.TotalCoverageArea  = Aggregator.CoverageArea;
                    volumesResult.CutArea            = Aggregator.CutArea;
                    volumesResult.FillArea           = Aggregator.FillArea;
                    volumesResult.BoundingExtentGrid = Aggregator.BoundingExtents;
                    volumesResult.BoundingExtentLLH  = resultBoundingExtents;
                }
                finally
                {
                    ApplicationServiceRequestStatistics.Instance.NumSimpleVolumeRequestsCompleted.Increment();
                    if (volumesResult.ResponseCode != SubGridRequestsResponseResult.OK)
                    {
                        ApplicationServiceRequestStatistics.Instance.NumSimpleVolumeRequestsFailed.Increment();
                    }
                }
            }
            catch (Exception e)
            {
                Log.LogError(e, $"Failed to compute the simple volumes. Site Model ID: {SiteModelID}");
            }

            return(volumesResult);
        }