Esempio n. 1
0
        /// <summary>
        /// Setup GridSize for tile
        /// </summary>
        private void Initialise()
        {
            QMTileResponse.ResultStatus = RequestErrorStatus.FailedToBuildQuantizedMeshTile;
            ResultStatus = RequestErrorStatus.FailedToBuildQuantizedMeshTile;

            // Determine the QM tile resolution by the zoom level
            if (OverrideGridSize != QMConstants.NoGridSize)
            {
                TileGridSize = OverrideGridSize;
            }
            else
            {
                if (TileZ >= QMConstants.HighResolutionLevel)
                {
                    TileGridSize = QMConstants.HighResolutionGridSize;
                }
                else if (TileZ >= QMConstants.MidResolutionLevel)
                {
                    TileGridSize = QMConstants.MidResolutionGridSize;
                }
                else
                {
                    TileGridSize = QMConstants.FlatResolutionGridSize;
                }
            }

            // Setup for return. In most cases you want to at least return an empty tile
            ElevData = new ElevationData(LowestElevation, TileGridSize); // elevation grid
        }
Esempio n. 2
0
        /// <summary>
        /// Creates a demo tile. Useful for development
        /// </summary>
        /// <returns></returns>
        private bool BuildDemoTile()
        {
            _log.LogDebug($"#Tile.({TileX},{TileY}) Returning demo tile. (X:{TileX}, Y:{TileX},{TileY}, Z:{TileZ}), GridSize{QMConstants.DemoResolutionGridSize}");
            // Even empty tiles must have header info correctly calculated
            if (ElevData.GridSize == QMConstants.NoGridSize)
            {
                ElevData = new ElevationData(LowestElevation, QMConstants.DemoResolutionGridSize); // elevation grid
            }
            ElevData.MakeDemoTile(TileBoundaryLL);

            QMTileBuilder tileBuilder = new QMTileBuilder()
            {
                TileData = ElevData,
                GridSize = ElevData.GridSize
            };

            if (!tileBuilder.BuildQuantizedMeshTile())
            {
                _log.LogError($"Tile.({TileX},{TileY}) failed to build demo tile. Error code: {tileBuilder.BuildTileFaultCode}");
                return(false);
            }

            QMTileResponse.ResultStatus = RequestErrorStatus.OK;
            QMTileResponse.data         = tileBuilder.QuantizedMeshTile; // return QM tile in response
            ResultStatus = RequestErrorStatus.OK;
            return(true);
        }
Esempio n. 3
0
        /// <summary>
        /// No data so return an empty tile
        /// </summary>
        /// <returns></returns>
        private bool BuildEmptyTile()
        {
            _log.LogDebug($"#Tile#.({TileX},{TileY}) Execute End. Returning empty tile. Zoom:{TileZ}, GridSize{QMConstants.FlatResolutionGridSize}");
            // Even empty tiles must have header info correctly calculated
            if (ElevData.GridSize == QMConstants.NoGridSize)
            {
                ElevData = new ElevationData(LowestElevation, QMConstants.FlatResolutionGridSize); // elevation grid
            }
            ElevData.MakeEmptyTile(TileBoundaryLL, HasLighting);
            if (ElevData.HasLighting)
            {
                ComputeNormalMap();
            }

            QMTileBuilder tileBuilder = new QMTileBuilder()
            {
                TileData = ElevData,
                GridSize = ElevData.GridSize
            };

            if (!tileBuilder.BuildQuantizedMeshTile())
            {
                _log.LogError($"Tile.({TileX},{TileY}) failed to build empty tile. Error code: {tileBuilder.BuildTileFaultCode}");
                return(false);
            }

            QMTileResponse.ResultStatus = RequestErrorStatus.OK;
            QMTileResponse.data         = tileBuilder.QuantizedMeshTile; // return QM tile in response
            ResultStatus = RequestErrorStatus.OK;
            return(true);
        }
Esempio n. 4
0
        public override void InternalFromBinary(IBinaryRawReader reader)
        {
            var version = VersionSerializationHelper.CheckVersionByte(reader, VERSION_NUMBER);

            if (version == 1)
            {
                ResultStatus = (RequestErrorStatus)reader.ReadInt();
            }
        }
Esempio n. 5
0
        protected ServiceException CreateServiceException <T>(RequestErrorStatus resultStatus = RequestErrorStatus.OK)
        {
            var errorMessage = string.Format(ERROR_MESSAGE_PRODUCTION_DATA, typeof(T).Name);

            if (resultStatus != RequestErrorStatus.OK)
            {
                errorMessage = string.Format(ERROR_MESSAGE_EX, errorMessage, ContractExecutionStates.FirstNameWithOffset((int)resultStatus));
            }

            return(new ServiceException(HttpStatusCode.BadRequest, new ContractExecutionResult(ContractExecutionStatesEnum.FailedToGetResults, errorMessage)));
        }
Esempio n. 6
0
 /// <summary>
 /// These root tiles are static
 /// </summary>
 /// <returns></returns>
 private void MakeRootTile()
 {
     _log.LogDebug($"#Tile.({TileX},{TileY}) Returning root tile");
     QMTileResponse.ResultStatus = RequestErrorStatus.OK;
     if (TileY == 0)
     {
         QMTileResponse.data = QMConstants.Terrain0;
     }
     else
     {
         QMTileResponse.data = QMConstants.Terrain1;
     }
     ResultStatus = RequestErrorStatus.OK;
 }
Esempio n. 7
0
        /// <summary>
        /// Generate quantized mesh tile from the supplied grid
        /// </summary>
        /// <returns></returns>
        public bool BuildQuantizedMeshTile()
        {
            try
            {
                ComputeHeaderInfo();

                // Turn grid into a quantized mesh
                var vertices = MeshBuilder.MakeQuantizedMesh(ref TileData);

                var tileHeader = new TerrainTileHeader()
                {
                    MaximumHeight          = TileData.MaximumHeight,
                    MinimumHeight          = TileData.MinimumHeight,
                    CenterX                = TileData.CenterX,
                    CenterY                = TileData.CenterY,
                    CenterZ                = TileData.CenterZ,
                    BoundingSphereCenterX  = TileData.BoundingSphereCenterX,
                    BoundingSphereCenterY  = TileData.BoundingSphereCenterY,
                    BoundingSphereCenterZ  = TileData.BoundingSphereCenterZ,
                    BoundingSphereRadius   = TileData.BoundingSphereRadius,
                    HorizonOcclusionPointX = TileData.HorizonOcclusionPointX,
                    HorizonOcclusionPointY = TileData.HorizonOcclusionPointY,
                    HorizonOcclusionPointZ = TileData.HorizonOcclusionPointZ
                };

                // This class constructs a tile from the computed mesh
                var tileBuilder = new TileBuilder();
                var tile        = tileBuilder.MakeTile(vertices, ref TileData.VertexNormals, tileHeader, MapUtils.GridSizeToTriangleCount(TileData.GridSize), TileData.GridSize, TileData.HasLighting);
                QuantizedMeshTile = CompressTile ? MapUtils.Compress(tile) : tile;

                BuildTileFaultCode = RequestErrorStatus.OK;
            }
            catch (Exception E)
            {
                Log.LogError(E, "BuildQuantizedMeshTile: Exception:");
                return(false);
            }
            return(true);
        }
Esempio n. 8
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);
        }
Esempio n. 9
0
        protected ServiceException CreateServiceException <T>(HttpStatusCode statusCode, int contractExecutionStatesEnum, RequestErrorStatus resultStatus = RequestErrorStatus.OK, string detailedMessage = null)
        {
            var errorMessage = string.Format(ERROR_MESSAGE, typeof(T).Name);

            if (resultStatus != RequestErrorStatus.OK)
            {
                errorMessage = string.Format(ERROR_MESSAGE_EX, errorMessage, ContractExecutionStates.FirstNameWithOffset((int)resultStatus));
            }

            if (!string.IsNullOrEmpty(detailedMessage))
            {
                errorMessage += $" ({detailedMessage})";
            }

            return(new ServiceException(statusCode, new ContractExecutionResult(contractExecutionStatesEnum, errorMessage)));
        }
Esempio n. 10
0
        /// <summary>
        /// Executor that implements requesting and rendering grid information to create the grid rows
        /// </summary>
        /// <returns></returns>
        public async Task <bool> ExecuteAsync()
        {
            // Get the lat lon boundary from xyz tile request
            TileBoundaryLL = MapGeo.TileXYZToRectLL(TileX, TileY, TileZ, out var yFlip);
            _log.LogInformation($"#Tile#.({TileX},{TileY}) Execute Start.  DMode:{DisplayMode}, HasLighting:{HasLighting}, Zoom:{TileZ} FlipY:{yFlip}. TileBoundary:{TileBoundaryLL.ToDisplay()}, DataModel:{DataModelUid}");

            if (TileZ == 0) // Send back default root tile
            {
                MakeRootTile();
                return(true);
            }

            SiteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(DataModelUid);
            if (SiteModel == null)
            {
                ResultStatus = RequestErrorStatus.NoSuchDataModel;
                _log.LogError($"Tile.({TileX},{TileY}) Failed to obtain site model for {DataModelUid}");
                return(false);
            }

            var siteModelExtentWithSurveyedSurfaces = SiteModel.GetAdjustedDataModelSpatialExtents(null);

            _log.LogDebug($"Tile.({TileX},{TileY}) Site model extents are {siteModelExtentWithSurveyedSurfaces}. TileBoundary:{TileBoundaryLL.ToDisplay()}");
            if (!siteModelExtentWithSurveyedSurfaces.IsValidPlanExtent) // No data return empty tile
            {
                return(BuildEmptyTile());
            }

            // We will draw all missing data just below lowest elevation for site
            LowestElevation = (float)siteModelExtentWithSurveyedSurfaces.MinZ - 1F;

            Initialise();                                           // setup tile requirements

            if (TileGridSize == QMConstants.FlatResolutionGridSize) // Too far out to see detail so return empty tile
            {
                return(BuildEmptyTile());
            }

            if (DisplayMode == QMConstants.DisplayModeDemo) // development use only
            {
                return(BuildDemoTile());
            }

            try
            {
                var setupResult = await SetupPipelineTask(siteModelExtentWithSurveyedSurfaces, SiteModel.CellSize);

                if (!setupResult)
                {
                    if (ResultStatus != RequestErrorStatus.InvalidCoordinateRange)
                    {
                        _log.LogError($"Tile.({TileX},{TileY}) Unable to setup pipelinetask.");
                    }
                    return(BuildEmptyTile());
                }

                processor.Process();

                if (GriddedElevationsResponse.ResultStatus != RequestErrorStatus.OK)
                {
                    _log.LogError($"Tile.({TileX},{TileY}) Unable to obtain data for gridded data. GriddedElevationRequestResponse: {GriddedElevationsResponse.ResultStatus.ToString()}");
                    return(BuildEmptyTile());
                }

                ElevData.HasData = !float.IsPositiveInfinity(task.MinElevation); // check for data

                // Developer Debugging Only
                if (ElevData.HasData)
                {
                    OutputDebugTile("Raw");
                }

                if (!ElevData.HasData)
                {
                    return(BuildEmptyTile());
                }

                _log.LogInformation($"#Tile#.({TileX},{TileY}) Data successfully sampled. GridSize:{TileGridSize} Min:{task.MinElevation}, Max:{task.MaxElevation} FirstElev:{GriddedElevDataArray[0, 0].Elevation}, LastElev:{GriddedElevDataArray[TileGridSize - 1, TileGridSize - 1].Elevation}");

                ElevData.HasLighting = HasLighting;
                // Transform gridded data into a format the mesh builder can use
                ConvertGridToDEM(task.MinElevation, task.MaxElevation);

                // Build a quantized mesh from sampled elevations
                QMTileBuilder tileBuilder = new QMTileBuilder()
                {
                    TileData = ElevData, GridSize = TileGridSize
                };
                _log.LogInformation($"Tile.({TileX},{TileY}) BuildQuantizedMeshTile. GridSize:{TileGridSize} Min:{ElevData.MinimumHeight}, Max:{ElevData.MaximumHeight}");
                if (!tileBuilder.BuildQuantizedMeshTile())
                {
                    _log.LogError($"Tile.({TileX},{TileY}) BuildQuantizedMeshTile returned false with error code: {tileBuilder.BuildTileFaultCode}");
                    return(false);
                }

                QMTileResponse.data         = tileBuilder.QuantizedMeshTile; // Make tile from mesh
                ResultStatus                = RequestErrorStatus.OK;
                QMTileResponse.ResultStatus = ResultStatus;
                _log.LogDebug($"#Tile#.({TileX},{TileY}) Execute End. Returning production tile. CesiumY:{yFlip}, Zoom:{TileZ}, GridSize:{TileGridSize}");

                return(true);
            }
            catch (Exception ex)
            {
                _log.LogError(ex, $"#Tile#.({TileX},{TileY}). Exception building QuantizedMesh tile: ");
                return(false);
            }
        }
Esempio n. 11
0
        /// <summary>
        /// Setup pipeline for tile request
        /// </summary>
        /// <param name="siteModelExtent">Site Model Extent</param>
        /// <param name="cellSize">Cell Size</param>
        /// <returns></returns>
        private async Task <bool> SetupPipelineTask(BoundingWorldExtent3D siteModelExtent, double cellSize)
        {
            var requestDescriptor = Guid.NewGuid();

            if (DisplayMode == QMConstants.DisplayModeStandard)
            {
                // Note coords are always supplied lat long
                if (SiteModel.CSIB() == string.Empty)
                {
                    ResultStatus = RequestErrorStatus.EmptyCoordinateSystem;
                    _log.LogError($"Failed to obtain site model coordinate system CSIB file for Project:{DataModelUid}");
                    return(false);
                }
            }

            LLHCoords = new[] { new XYZ(MapUtils.Deg2Rad(TileBoundaryLL.West), MapUtils.Deg2Rad(TileBoundaryLL.South), 0),
                                new XYZ(MapUtils.Deg2Rad(TileBoundaryLL.East), MapUtils.Deg2Rad(TileBoundaryLL.North), 0),
                                new XYZ(MapUtils.Deg2Rad(TileBoundaryLL.West), MapUtils.Deg2Rad(TileBoundaryLL.North), 0),
                                new XYZ(MapUtils.Deg2Rad(TileBoundaryLL.East), MapUtils.Deg2Rad(TileBoundaryLL.South), 0) };

            // This will change in Part3 once development is complete
            var strCSIB   = DisplayMode == QMConstants.DisplayModeStandard ? SiteModel.CSIB() : DIMENSIONS_2012_DC_CSIB;
            var NEECoords = DIContext.Obtain <ICoreXWrapper>().LLHToNEE(strCSIB, LLHCoords.ToCoreX_XYZ(), CoreX.Types.InputAs.Radians).ToTRex_XYZ();

            GridIntervalX = (NEECoords[1].X - NEECoords[0].X) / (TileGridSize - 1);
            GridIntervalY = (NEECoords[1].Y - NEECoords[0].Y) / (TileGridSize - 1);

            _log.LogDebug($"#Tile#.({TileX},{TileY}) TileInfo: Zoom:{TileZ}, TileSizeXY:{Math.Round(NEECoords[1].X - NEECoords[0].X, 3)}m x {Math.Round(NEECoords[2].Y - NEECoords[0].Y, 3)}m, GridInterval(m) X:{Math.Round(GridIntervalX, 3)}, Y:{Math.Round(GridIntervalY, 3)}, GridSize:{TileGridSize}");

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

            double dx = NEECoords[2].X - NEECoords[0].X;

            CenterX = NEECoords[2].X + dx / 2;
            double dy = NEECoords[2].Y - NEECoords[0].Y;

            CenterY = NEECoords[0].Y + dy / 2;

            // 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;

            SetRotation(TileRotation);

            _log.LogDebug($"QMTile render executing across tile: [Rotation:{ MathUtilities.RadiansToDegrees(TileRotation)}] " +
                          $" [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}");

            RotatedTileBoundingExtents.SetInverted();
            foreach (var xyz in NEECoords)
            {
                RotatedTileBoundingExtents.Include(xyz.X, xyz.Y);
            }


            // Intersect the site model extents with the extents requested by the caller
            _log.LogDebug($"Tile.({TileX},{TileY}) Calculating intersection of bounding box and site model {DataModelUid}:{siteModelExtent}");
            var dataSelectionExtent = new BoundingWorldExtent3D(RotatedTileBoundingExtents);

            dataSelectionExtent.Intersect(siteModelExtent);
            if (!dataSelectionExtent.IsValidPlanExtent)
            {
                ResultStatus = RequestErrorStatus.InvalidCoordinateRange;
                _log.LogInformation($"Tile.({TileX},{TileY}) Site model extents {siteModelExtent}, do not intersect RotatedTileBoundingExtents {RotatedTileBoundingExtents}");
                return(false);
            }

            // 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, cellSize, SubGridTreeConsts.DefaultIndexOriginOffset,
                                                               out var CellExtents_MinX, out var CellExtents_MinY);
            SubGridTree.CalculateIndexOfCellContainingPosition(dataSelectionExtent.MaxX,
                                                               dataSelectionExtent.MaxY, 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);

            // Setup Task
            task      = DIContext.Obtain <Func <PipelineProcessorTaskStyle, ITRexTask> >()(PipelineProcessorTaskStyle.QuantizedMesh) as QuantizedMeshTask;
            processor = DIContext.Obtain <IPipelineProcessorFactory>().NewInstanceNoBuild <SubGridsRequestArgument>(requestDescriptor: requestDescriptor,
                                                                                                                    dataModelID: DataModelUid,
                                                                                                                    gridDataType: GridDataType.Height,
                                                                                                                    response: GriddedElevationsResponse,
                                                                                                                    filters: Filters,
                                                                                                                    cutFillDesign: new DesignOffset(),
                                                                                                                    task: task,
                                                                                                                    pipeline: DIContext.Obtain <Func <PipelineProcessorPipelineStyle, ISubGridPipelineBase> >()(PipelineProcessorPipelineStyle.DefaultProgressive),
                                                                                                                    requestAnalyser: DIContext.Obtain <IRequestAnalyser>(),
                                                                                                                    requireSurveyedSurfaceInformation: true, //Rendering.Utilities.DisplayModeRequireSurveyedSurfaceInformation(DisplayMode.Height) && Rendering.Utilities.FilterRequireSurveyedSurfaceInformation(Filters),
                                                                                                                    requestRequiresAccessToDesignFileExistenceMap: false,
                                                                                                                    overrideSpatialCellRestriction: CellExtents,
                                                                                                                    liftParams: LiftParams
                                                                                                                    );

            // Set the grid TRexTask parameters for progressive processing
            processor.Task.RequestDescriptor = requestDescriptor;
            processor.Task.TRexNodeID        = RequestingTRexNodeID;
            processor.Task.GridDataType      = GridDataType.Height;

            // 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);

            // Setup new grid array for results
            GriddedElevDataArray = new GriddedElevDataRow[TileGridSize, TileGridSize];
            // build up a data sample grid from SW to NE
            for (int y = 0; y < TileGridSize; y++)
            {
                for (int x = 0; x < TileGridSize; x++)
                {
                    var x1 = NEECoords[0].X + (GridIntervalX * x);
                    var y1 = NEECoords[0].Y + (GridIntervalY * y);
                    if (Rotating)
                    {
                        Rotate_point(x1, y1, out x1, out y1);
                    }
                    GriddedElevDataArray[x, y].Easting   = x1;
                    GriddedElevDataArray[x, y].Northing  = y1;
                    GriddedElevDataArray[x, y].Elevation = CellPassConsts.NullHeight;
                }
            }

            _log.LogDebug($"Tile.({TileX},{TileY}) Boundary grid coords:{string.Concat(NEECoords)}");
            _log.LogDebug($"Tile.({TileX},{TileY}) First Easting:{GriddedElevDataArray[0, 0].Easting} Northing:{GriddedElevDataArray[0, 0].Northing}");
            _log.LogDebug($"Tile.({TileX},{TileY}) Last Easting:{GriddedElevDataArray[TileGridSize - 1, TileGridSize - 1].Easting} Northing:{GriddedElevDataArray[TileGridSize - 1, TileGridSize - 1].Northing}");

            // point to container for results
            task.GriddedElevDataArray = GriddedElevDataArray;
            task.GridIntervalX        = GridIntervalX;
            task.GridIntervalY        = GridIntervalY;
            task.GridSize             = TileGridSize;
            // Tile boundary
            task.TileMinX        = NEECoords[0].X;
            task.TileMinY        = NEECoords[0].Y;
            task.TileMaxX        = NEECoords[1].X;
            task.TileMaxY        = NEECoords[1].Y;
            task.LowestElevation = LowestElevation;

            Azimuth       = 0;
            StartNorthing = NEECoords[0].Y;
            StartEasting  = NEECoords[0].X;

            // Commented out for purposes of demo until relationship between TRex mediated skip/step selection and the quantised mesh tile vertex based selection are better understood
            //    processor.Pipeline.AreaControlSet =
            //     new AreaControlSet(false, GridIntervalX, GridIntervalY, StartEasting, StartNorthing, Azimuth);

            if (!processor.Build())
            {
                _log.LogError($"Tile.({TileX},{TileY}) Failed to build pipeline processor for request to model {DataModelUid}");
                return(false);
            }

            return(true);
        }