Exemplo n.º 1
0
        public void OsmAllWithTerrain(string name, string bboxWKT, bool centerOnOrigin, float ZScale)
        {
            DEMDataSet dataset           = DEMDataSet.NASADEM;
            var        bbox              = GeometryService.GetBoundingBox(bboxWKT);
            bool       computeElevations = false;
            string     outputDir         = Directory.GetCurrentDirectory();

            var transform = new ModelGenerationTransform(bbox, Reprojection.SRID_PROJECTED_MERCATOR, centerOnOrigin: centerOnOrigin, ZScale, centerOnZOrigin: false);
            var heightMap = _elevationService.GetHeightMap(ref bbox, dataset);

            transform.BoundingBox = bbox; // bbox changed by GetHeigtMap

            heightMap = transform.TransformHeightMap(heightMap);
            TileRange tiles = _imageryService.DownloadTiles(bbox, ImageryProvider.EsriWorldImagery, 15);

            string      fileName   = Path.Combine(outputDir, "Texture.jpg");
            TextureInfo texInfo    = _imageryService.ConstructTexture(tiles, bbox, fileName, TextureImageFormat.image_jpeg);
            var         pbrTexture = PBRTexture.Create(texInfo, null);

            ModelRoot model = _osmProcessor.Run(null, OsmLayer.Buildings | OsmLayer.Highways, bbox, transform, computeElevations, dataset, downloadMissingFiles: true);

            model = _gltfService.AddTerrainMesh(model, heightMap, pbrTexture);


            model.SaveGLB(Path.Combine(outputDir, $"{nameof(OsmAllWithTerrain)}_{name}" + ".glb"));
        }
Exemplo n.º 2
0
        internal void ExportVisualTopoToGLB(VisualTopoModel visualTopoModel, DEMDataSet dataset, string outputFile, bool drawOnTexture, float marginMeters, float zFactor, ImageryProvider imageryProvider)
        {
            int   outputSrid       = 3857;
            float lineWidth        = 1.0F;
            int   numTilesPerImage = 4;


            BoundingBox bbox = visualTopoModel.BoundingBox                                                                                                      // relative coords
                               .Translate(visualTopoModel.EntryPoint.Longitude, visualTopoModel.EntryPoint.Latitude, visualTopoModel.EntryPoint.Elevation ?? 0) // absolute coords
                               .Pad(marginMeters)                                                                                                               // margin around model
                               .ReprojectTo(visualTopoModel.SRID, dataset.SRID);                                                                                // DEM coords

            //=======================
            // Height map (get dem elevation for bbox)
            //
            // Get height map
            // Note that ref Bbox means that the bbox will be adjusted to match DEM data
            var heightMap        = elevationService.GetHeightMap(ref bbox, dataset, downloadMissingFiles: true);
            var bboxTerrainSpace = bbox.ReprojectTo(dataset.SRID, outputSrid); // terrain coords

            // Model origin
            GeoPoint axisOriginWorldSpace = visualTopoModel.EntryPoint.ReprojectTo(visualTopoModel.SRID, outputSrid)
                                            .CenterOnOrigin(bboxTerrainSpace);
            Vector3 axisOriginModelSpace = visualTopoModel.EntryPoint.AsVector3();

            //await signalR.ReportGenerateProgressAsync(signalRConnectionId, "Getting GPX extent elevation...", 20);
            //=======================
            // Local transform function from model coordinates (relative to entry, in meters)
            // and global coordinates absolute in final 3D model space
            //
            IEnumerable <GeoPoint> TransformLine(IEnumerable <GeoPoint> line)
            {
                var newLine = line.Translate(visualTopoModel.EntryPoint)     // Translate to entry (=> global topo coord space)
                              .ReprojectTo(visualTopoModel.SRID, outputSrid) // Reproject to terrain coord space
                              .CenterOnOrigin(bboxTerrainSpace)              // Center on terrain space origin
                              .CenterOnOrigin(axisOriginWorldSpace);

                return(newLine);
            };

            //=======================
            // 3D model
            //
            var gltfModel = sharpGltfService.CreateNewModel();

            // Add X/Y/Z axis on entry point
            var axis = meshService.CreateAxis();

            sharpGltfService.AddMesh(gltfModel, "Axis", axis, doubleSided: false);

            int i = 0;

            var triangulation = visualTopoModel.TriangulationFull3D.Clone()
                                .Translate(axisOriginModelSpace)             // already zScaled if zFactor > 1
                                .ReprojectTo(visualTopoModel.SRID, outputSrid)
                                .CenterOnOrigin(bboxTerrainSpace)
                                .CenterOnOrigin(axisOriginWorldSpace.AsVector3());

            gltfModel = sharpGltfService.AddMesh(gltfModel, "Cavite3D", triangulation, VectorsExtensions.CreateColor(0, 255, 0), doubleSided: false);

            ////=======================
            //// 3D cavity model
            //foreach (var line in visualTopoModel.Topology3D) // model.Topology3D is the graph of topo paths
            //{
            //    // Add line to model
            //    gltfModel = sharpGltfService.AddLine(gltfModel
            //                                    , string.Concat("CavitySection", i++)     // name of 3D node
            //                                    , TransformLine(line)               // call transform function
            //                                    , color: VectorsExtensions.CreateColor(255, 0, 0, 128)
            //                                    , lineWidth);
            //}

            // Reproject and center height map coordinates
            heightMap = heightMap.ReprojectTo(dataset.SRID, outputSrid)
                        .CenterOnOrigin(bboxTerrainSpace)
                        .ZScale(zFactor)
                        .CenterOnOrigin(axisOriginWorldSpace);

            //=======================
            // Textures
            //
            PBRTexture pbrTexture = null;

            if (imageryProvider != null)
            {
                TileRange tiles    = imageryService.DownloadTiles(bbox, imageryProvider, numTilesPerImage);
                string    fileName = Path.Combine(Path.GetDirectoryName(outputFile), "Texture.jpg");

                Console.WriteLine("Construct texture...");
                TextureInfo texInfo = null;
                if (drawOnTexture)
                {
                    var topoTexture = visualTopoModel.Topology3D.SelectMany(l => l).Translate(visualTopoModel.EntryPoint).ReprojectTo(visualTopoModel.SRID, 4326);
                    texInfo = imageryService.ConstructTextureWithGpxTrack(tiles, bbox, fileName, TextureImageFormat.image_jpeg
                                                                          , topoTexture, drawGpxVertices: true);
                }
                else
                {
                    texInfo = imageryService.ConstructTexture(tiles, bbox, fileName, TextureImageFormat.image_jpeg);
                }



                pbrTexture = PBRTexture.Create(texInfo, null);
            }
            //
            //=======================

            gltfModel = sharpGltfService.AddTerrainMesh(gltfModel, heightMap, pbrTexture);
            gltfModel.SaveGLB(outputFile);
        }
Exemplo n.º 3
0
        public static ModelRoot AddTINMesh(ModelRoot model, HeightMap hMap, double precision, SharpGltfService gltf, PBRTexture textures, int srid)
        {
            Triangulation triangulation = TINGeneration.GenerateTIN(hMap, precision, srid);

            return(gltf.AddTerrainMesh(model, triangulation, textures, doubleSided: true));
        }
Exemplo n.º 4
0
        private void Buildings3DOnly()
        {
            string outputDir = Directory.GetCurrentDirectory();

            string WKT_SF_BIG        = "POLYGON((-122.53517427420718 37.81548554152065,-122.35149660086734 37.81548554152065,-122.35149660086734 37.70311455416941,-122.53517427420718 37.70311455416941,-122.53517427420718 37.81548554152065))";
            string WKT_SF_SMALL      = "POLYGON((-122.42722692299768 37.81034598808797, -122.38886060524865 37.81034598808797, -122.38886060524865 37.784573673820816, -122.42722692299768 37.784573673820816, -122.42722692299768 37.81034598808797))";
            string WKT_SF_SUPERSMALL = "POLYGON((-122.41063177228989 37.80707295150412,-122.40904390455307 37.80707295150412,-122.40904390455307 37.806064225434206,-122.41063177228989 37.806064225434206,-122.41063177228989 37.80707295150412))";

            // FULL
            string WKT_PRIPYAT_FULL = "POLYGON((30.021919548702254 51.41813804241615,30.083030998897566 51.41813804241615,30.083030998897566 51.389438684773985,30.021919548702254 51.389438684773985,30.021919548702254 51.41813804241615))";
            // Cas 1
            //string WKT_PRIPYAT = "POLYGON((30.05430313958436 51.404795567637294,30.055118531124887 51.404795567637294,30.055118531124887 51.40432372279637,30.05430313958436 51.40432372279637,30.05430313958436 51.404795567637294))";
            // cas 2
            //string WKT_PRIPYAT = "POLYGON((30.062877280833636 51.40748141189236,30.063456637980853 51.40748141189236,30.063456637980853 51.40716017522757,30.062877280833636 51.40716017522757,30.062877280833636 51.40748141189236))";
            // cas 3
            //string WKT_PRIPYAT = "POLYGON((30.065251398582948 51.407283441091266,30.066243815918458 51.407283441091266,30.066243815918458 51.40558353075506,30.065251398582948 51.40558353075506,30.065251398582948 51.407283441091266))";

            string WKT_PRIPRYAT_SOMEROADS = "POLYGON((30.062684362726948 51.40792711901257,30.064690655070088 51.40792711901257,30.064690655070088 51.40693664170241,30.062684362726948 51.40693664170241,30.062684362726948 51.40792711901257))";

            // SF Big: POLYGON((-122.53517427420718 37.81548554152065,-122.35149660086734 37.81548554152065,-122.35149660086734 37.70311455416941,-122.53517427420718 37.70311455416941,-122.53517427420718 37.81548554152065))
            // SF Small: POLYGON((-122.41967382241174 37.81034598808797,-122.39761533547326 37.81034598808797,-122.39761533547326 37.79162804294824,-122.41967382241174 37.79162804294824,-122.41967382241174 37.81034598808797))

            // Napoli, multi polygon (https://www.openstreetmap.org/relation/8955771)
            //new BoundingBox(14.364430059744153, 14.365218629194532, 40.78433307340424, 40.785023575175295);

            string WKT_AIX_FULL        = "POLYGON((5.402291662243135 43.565714431347274,5.48056925013376 43.565714431347274,5.48056925013376 43.50797300081391,5.402291662243135 43.50797300081391,5.402291662243135 43.565714431347274))";
            string WKT_AIX_WITHTERRAIN = "POLYGON((5.440657648511835 43.55957815383877,5.444434198804804 43.55957815383877,5.444434198804804 43.5579454365131,5.440657648511835 43.5579454365131,5.440657648511835 43.55957815383877))";
            string WKT_AIX_SMALLOSMBUG = "POLYGON((5.441805234256467 43.55910060792738,5.442684998813352 43.55910060792738,5.442684998813352 43.55877017799191,5.441805234256467 43.55877017799191,5.441805234256467 43.55910060792738))";
            string WKT_MONACO          = "POLYGON((7.392147587957001 43.75577569838535,7.4410710803886415 43.75577569838535,7.4410710803886415 43.71757458493263,7.392147587957001 43.71757458493263,7.392147587957001 43.75577569838535))";

            string WKT_MONACO_DEBUG = "POLYGON((7.421709439122424 43.73663530909531,7.433961769902453 43.73663530909531,7.433961769902453 43.733007331111345,7.421709439122424 43.733007331111345,7.421709439122424 43.73663530909531))";//"POLYGON((7.426780270757294 43.73870913810349,7.432520198049164 43.73870913810349,7.432520198049164 43.73501926928533,7.426780270757294 43.73501926928533,7.426780270757294 43.73870913810349))";
            string WKT_HK           = "POLYGON((114.13119740014092 22.360520982593926,114.21050495629326 22.360520982593926,114.21050495629326 22.28874575980822,114.13119740014092 22.28874575980822,114.13119740014092 22.360520982593926))";
            string WKT_FRISCO       = "POLYGON((-122.5235839391063 37.81433638393927,-122.36222224477036 37.81433638393927,-122.36222224477036 37.71228516909579,-122.5235839391063 37.71228516909579,-122.5235839391063 37.81433638393927))";
            string WKT_DEFENSE      = "POLYGON((2.222075572216413 48.902615468120246,2.3024130966304757 48.902615468120246,2.3024130966304757 48.86355756505397,2.222075572216413 48.86355756505397,2.222075572216413 48.902615468120246))";

            DEMDataSet dataset           = DEMDataSet.NASADEM;
            var        name              = nameof(WKT_DEFENSE);
            var        bbox              = GeometryService.GetBoundingBox(WKT_DEFENSE);
            bool       computeElevations = true;

            ModelRoot model       = _gltfService.CreateNewModel();
            bool      withTerrain = false;

            if (withTerrain)
            {
                var heightMap = _elevationService.GetHeightMap(ref bbox, dataset);
                var transform = new ModelGenerationTransform(bbox, Reprojection.SRID_PROJECTED_MERCATOR, centerOnOrigin: true, ZScale, centerOnZOrigin: true);
                heightMap = transform.TransformHeightMap(heightMap);
                TileRange tiles = _imageryService.DownloadTiles(bbox, ImageryProvider.MapBoxSatellite, 15);

                string      fileName   = Path.Combine(outputDir, "Texture.jpg");
                TextureInfo texInfo    = _imageryService.ConstructTexture(tiles, bbox, fileName, TextureImageFormat.image_jpeg);
                var         pbrTexture = PBRTexture.Create(texInfo, null);

                model = _osmProcessor.Run(model, OsmLayer.Buildings | OsmLayer.Highways, bbox, transform, computeElevations, dataset, downloadMissingFiles: true);
                model = _gltfService.AddTerrainMesh(model, heightMap, pbrTexture);
            }
            else
            {
                var transform = new ModelGenerationTransform(bbox, Reprojection.SRID_PROJECTED_MERCATOR, centerOnOrigin: true, ZScale, centerOnZOrigin: true);
                model = _osmProcessor.Run(model, OsmLayer.Buildings | OsmLayer.Highways, bbox, transform, computeElevations, dataset, downloadMissingFiles: true);
            }

            model.SaveGLB(Path.Combine(Directory.GetCurrentDirectory(), name + ".glb"));
        }
Exemplo n.º 5
0
        /// <summary>
        /// Generates a VisualTopo file 3D model
        /// </summary>
        /// <remarks>LT* (Lambert Carto) projections are not supported and could produce imprecise results (shifted by +10meters)</remarks>
        /// <param name="vtopoFile">VisualTopo .TRO file</param>
        /// <param name="imageryProvider">Imagery provider for terrain texture. Set to null for untextured model</param>
        /// <param name="bboxMarginMeters">Terrain margin (meters) around VisualTopo model</param>
        public void Run_3DModelGeneration(string vtopoFile, ImageryProvider imageryProvider, float bboxMarginMeters = 1000, bool generateTopoOnlyModel = false, float zFactor = 1f)
        {
            try
            {
                //=======================
                // Generation params
                //
                int    outputSRID      = 3857;                         // Output SRID
                float  lineWidth       = 1.0F;                         // Topo lines width (meters)
                var    dataset         = DEMDataSet.AW3D30;            // DEM dataset for terrain and elevation
                int    TEXTURE_TILES   = 8;                            // Texture quality (number of tiles for bigger side) 4: med, 8: high, 12: ultra
                string outputDir       = Directory.GetCurrentDirectory();
                bool   GENERATE_LINE3D = false;

                //=======================
                // Open and parse file
                //
                // model will have available properties
                // => Graph (nodes/arcs)
                // => BoundingBox
                // => Topology3D -> list of point-to-point lines
                // => SRID of model file
                StopwatchLog    timeLog = StopwatchLog.StartNew(_logger);
                VisualTopoModel model   = _visualTopoService.LoadFile(vtopoFile, Encoding.GetEncoding("ISO-8859-1")
                                                                      , decimalDegrees: true
                                                                      , ignoreRadialBeams: true
                                                                      , zFactor);
                timeLog.LogTime($"Loading {vtopoFile} model file");

                // for debug,
                //var b = GetBranches(model); // graph list of all nodes
                //var lowestPoint = model.Sets.Min(s => s.Data.Min(d => d.GlobalGeoPoint?.Elevation ?? 0));

                BoundingBox bbox = model.BoundingBox                                                                                  // relative coords
                                   .Translate(model.EntryPoint.Longitude, model.EntryPoint.Latitude, model.EntryPoint.Elevation ?? 0) // absolute coords
                                   .Pad(bboxMarginMeters)                                                                             // margin around model
                                   .ReprojectTo(model.SRID, dataset.SRID);                                                            // DEM coords
                                                                                                                                      // Get height map
                                                                                                                                      // Note that ref Bbox means that the bbox will be adjusted to match DEM data
                var heightMap        = _elevationService.GetHeightMap(ref bbox, dataset, downloadMissingFiles: true);
                var bboxTerrainSpace = bbox.ReprojectTo(dataset.SRID, outputSRID);                                                    // terrain coords
                timeLog.LogTime("Terrain height map");

                //=======================
                // Get entry elevation (need to reproject to DEM coordinate system first)
                // and sections entry elevations
                //
                _visualTopoService.ComputeFullCavityElevations(model, dataset, zFactor); // will add TerrainElevationAbove and entry elevations
                _visualTopoService.Create3DTriangulation(model);
                timeLog.LogTime("Cavity points elevation");

                // Model origin
                GeoPoint axisOriginWorldSpace = model.EntryPoint.ReprojectTo(model.SRID, outputSRID)
                                                .CenterOnOrigin(bboxTerrainSpace);
                Vector3 axisOriginModelSpace = model.EntryPoint.AsVector3();

                //=======================
                // Local transform function from model coordinates (relative to entry, in meters)
                // and global coordinates absolute in final 3D model space
                //
                IEnumerable <GeoPoint> TransformLine(IEnumerable <GeoPoint> line)
                {
                    var newLine = line.Translate(model.EntryPoint)             // Translate to entry (=> global topo coord space)
                                  .ReprojectTo(model.SRID, outputSRID)         // Reproject to terrain coord space
                                  .CenterOnOrigin(bboxTerrainSpace)            // Center on terrain space origin
                                  .CenterOnOrigin(axisOriginWorldSpace);

                    return(newLine);
                };


                //=======================
                // 3D model
                //
                var gltfModel = _gltfService.CreateNewModel();

                // Add X/Y/Z axis on entry point
                var axis = _meshService.CreateAxis();
                _gltfService.AddMesh(gltfModel, "Axis", axis, doubleSided: false);

                int i = 0;

                var triangulation = model.TriangulationFull3D.Clone()
                                    .Translate(axisOriginModelSpace)             // already zScaled if zFactor > 1
                                    .ReprojectTo(model.SRID, outputSRID)
                                    .CenterOnOrigin(bboxTerrainSpace)
                                    .CenterOnOrigin(axisOriginWorldSpace.AsVector3());
                gltfModel = _gltfService.AddMesh(gltfModel, "Cavite3D", model.TriangulationFull3D, VectorsExtensions.CreateColor(0, 255, 0), doubleSided: false);

                if (GENERATE_LINE3D)
                {
                    foreach (var line in model.Topology3D) // model.Topology3D is the graph of topo paths
                    {
                        // Add line to model
                        gltfModel = _gltfService.AddLine(gltfModel
                                                         , string.Concat("GPX", i++)    // name of 3D node
                                                         , TransformLine(line)          // call transform function
                                                         , color: VectorsExtensions.CreateColor(255, 0, 0, 128)
                                                         , lineWidth);
                    }
                }
                timeLog.LogTime("Topo 3D model");


                //axis = _meshService.CreateAxis(10,100);
                //_gltfService.AddMesh(gltfModel, "Axis", axis, doubleSided: false);

                if (generateTopoOnlyModel)
                {
                    // Uncomment this to save 3D model for topo only (without terrain)
                    gltfModel.SaveGLB(string.Concat(Path.GetFileNameWithoutExtension(vtopoFile) + $"Z{zFactor}_TopoOnly.glb"));
                }

                // Reproject and center height map coordinates
                heightMap = heightMap.ReprojectTo(dataset.SRID, outputSRID)
                            .CenterOnOrigin(bboxTerrainSpace)
                            .ZScale(zFactor)
                            .CenterOnOrigin(axisOriginWorldSpace);
                //.BakeCoordinates();
                timeLog.LogTime("Height map transform");

                //=======================
                // Textures
                //
                PBRTexture pbrTexture = null;
                if (imageryProvider != null)
                {
                    TileRange tiles    = _imageryService.DownloadTiles(bbox, imageryProvider, TEXTURE_TILES);
                    string    fileName = Path.Combine(outputDir, "Texture.jpg");
                    timeLog.LogTime("Imagery download");

                    Console.WriteLine("Construct texture...");
                    //TextureInfo texInfo = _imageryService.ConstructTexture(tiles, bbox, fileName, TextureImageFormat.image_jpeg);
                    var         topoTexture = model.Topology3D.SelectMany(l => l).Translate(model.EntryPoint).ReprojectTo(model.SRID, 4326);
                    TextureInfo texInfo     = _imageryService.ConstructTextureWithGpxTrack(tiles, bbox, fileName, TextureImageFormat.image_jpeg
                                                                                           , topoTexture, false);

                    pbrTexture = PBRTexture.Create(texInfo, null);
                    timeLog.LogTime("Texture creation");
                }
                //
                //=======================

                // Triangulate height map
                _logger.LogInformation($"Triangulating height map and generating 3D mesh...");

                gltfModel = _gltfService.AddTerrainMesh(gltfModel, heightMap, pbrTexture);
                gltfModel.SaveGLB(string.Concat(Path.GetFileNameWithoutExtension(vtopoFile) + $"Z{zFactor}.glb"));
                timeLog.LogTime("3D model");
            }
            catch (Exception ex)
            {
                _logger.LogError("Error :" + ex.Message);
            }
        }
Exemplo n.º 6
0
        private Location3DModelResponse Generate3DLocationModel(Location3DModelRequest request, Location3DModelSettings settings)
        {
            Location3DModelResponse response = new Location3DModelResponse();

            try
            {
                bool imageryFailed = false;
                using (TimeSpanBlock timer = new TimeSpanBlock($"3D model {request.Id}", _logger))
                {
                    BoundingBox bbox = GetBoundingBoxAroundLocation(request.Latitude, request.Longitude, settings.SideSizeKm);

                    HeightMap hMap      = _elevationService.GetHeightMap(ref bbox, settings.Dataset);
                    var       transform = new ModelGenerationTransform(bbox, Reprojection.SRID_PROJECTED_MERCATOR, centerOnOrigin: true, settings.ZScale, centerOnZOrigin: true);

                    response.Attributions.AddRange(settings.Attributions);   // will be added to the model
                    response.Attributions.Add(settings.Dataset.Attribution); // will be added to the model


                    PBRTexture pbrTexture = null;
                    if (settings.ImageryProvider != null)
                    {
                        response.Attributions.Add(settings.ImageryProvider.Attribution); // will be added to the model

                        // Imagery
                        TileRange tiles = _imageryService.ComputeBoundingBoxTileRange(bbox, settings.ImageryProvider, settings.MinTilesPerImage);
                        Debug.Assert(tiles.Count < 400);

                        tiles = _imageryService.DownloadTiles(tiles, settings.ImageryProvider);

                        string      fileName = Path.Combine(settings.OutputDirectory, $"{request.Id}_Texture.jpg");
                        TextureInfo texInfo  = _imageryService.ConstructTexture(tiles, bbox, fileName, TextureImageFormat.image_jpeg);

                        transform.BoundingBox = bbox;
                        hMap = transform.TransformHeightMap(hMap);


                        //var normalMap = _imageryService.GenerateNormalMap(hMap, settings.OutputDirectory, $"{request.Id}_normalmap.png");
                        pbrTexture = PBRTexture.Create(texInfo);
                    }

                    // Center on origin
                    //hMap = hMap.CenterOnOrigin(out Matrix4x4 transform).BakeCoordinates();
                    //response.Origin = new GeoPoint(request.Latitude, request.Longitude).ReprojectTo(Reprojection.SRID_GEODETIC, Reprojection.SRID_PROJECTED_MERCATOR);

                    ModelRoot model = _gltfService.CreateNewModel();

                    //=======================
                    // Buildings
                    if (settings.OsmBuildings)
                    {
                        model = _sampleOsmProcessor.Run(model, OsmLayer.Buildings, bbox, transform, computeElevations: true, settings.Dataset, settings.DownloadMissingFiles);
                    }


                    if (settings.GenerateTIN)
                    {
                        model = AddTINMesh(model, hMap, 2d, _gltfService, pbrTexture, Reprojection.SRID_PROJECTED_MERCATOR);
                    }
                    else
                    {
                        model = _gltfService.AddTerrainMesh(model, hMap, pbrTexture);
                    }
                    model.Asset.Generator = "DEM Net Elevation API with SharpGLTF";
                    //model.TryUseExtrasAsList(true).AddRange(response.Attributions);
                    model.SaveGLB(Path.Combine(settings.OutputDirectory, string.Concat(imageryFailed ? "imageryFailed_" : "", settings.ModelFileNameGenerator(settings, request))));

                    // cleanup
                    //if (pbrTexture != null)
                    //{
                    //    if (pbrTexture.NormalTexture != null) File.Delete(pbrTexture.NormalTexture.FilePath);
                    //    File.Delete(pbrTexture.BaseColorTexture.FilePath);
                    //}

                    response.Elapsed  = timer.Elapsed;
                    response.NumTiles = pbrTexture.BaseColorTexture.TileCount;
                }
            }
            catch (Exception)
            {
                throw;
            }
            return(response);
        }