internal void Run(string gpxFile, DEMDataSet dataSet, float Z_FACTOR = 2f, int outputSrid = Reprojection.SRID_PROJECTED_MERCATOR) { try { float paddingFactor = 2f; float ModelMaxUnits = 250f; float GpxLineWidthSTLUnits = 0.1f; Matrix4x4 stlTransformMatrix = Matrix4x4.CreateRotationX((float)Math.PI / 2f); string outputDir = Path.GetFullPath("."); //======================= /// Line strip from GPX /// // Get GPX points var segments = GpxImport.ReadGPX_Segments(gpxFile); var points = segments.SelectMany(seg => seg); var bbox = points.GetBoundingBox().ReprojectTo(4326, outputSrid); bbox = bbox.Pad(bbox.Width * paddingFactor, bbox.Height * paddingFactor, 0) .ReprojectTo(outputSrid, 4326); var gpxPointsElevated = _elevationService.GetPointsElevation(points, dataSet); HeightMap hMap = _elevationService.GetHeightMap(ref bbox, dataSet); hMap = hMap.ReprojectTo(4326, outputSrid) .ZScale(Z_FACTOR) .CenterOnOrigin() .FitInto(ModelMaxUnits) .BakeCoordinates(); // generate mesh ModelRoot model = _sharpGltfService.CreateTerrainMesh(hMap, GenOptions.BoxedBaseElevationMin, stlTransformMatrix); List <Attribution> attributions = new List <Attribution>(); attributions.Add(dataSet.Attribution); attributions.Add(new Attribution("Generator", "DEM Net Elevation API", "https://elevationapi.com")); _stlService.STLExport(model.LogicalMeshes[0].Primitives[0], Path.ChangeExtension(gpxFile, ".stl"), false, attributions); var bboxPoints = bbox.ReprojectTo(4326, outputSrid).CenterOnOrigin(); gpxPointsElevated = gpxPointsElevated.ReprojectTo(4326, outputSrid) .ZScale(Z_FACTOR) .CenterOnOrigin(bbox.ReprojectTo(4326, outputSrid)) .FitInto(bboxPoints, ModelMaxUnits) .ToList(); var gpxModel = _sharpGltfService.AddLine(null, "GPX", gpxPointsElevated, Vector4.One, GpxLineWidthSTLUnits, stlTransformMatrix); _stlService.STLExport(gpxModel.LogicalMeshes[0].Primitives[0], Path.ChangeExtension(gpxFile, ".gpx.stl"), false, attributions); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } }
internal void RunGPX(DEMDataSet largeDataSet, DEMDataSet localDataset) { try { string outputDir = Path.GetFullPath("."); string _gpxFile = Path.Combine("SampleData", "20191022-Puch-Pöllau.gpx"); string sensorLogFile = Path.Combine("SampleData", "20191023-Puch-Pöllau-sensorlog.json"); var sensorLog = SensorLog.FromJson(sensorLogFile); //sensorLog.Plot("sensorLog.png"); string balloonModel = Path.Combine("SampleData", "OE-SOE.glb"); float Z_FACTOR = 2f; float trailWidthMeters = 5f; ModelRoot balloon = ModelRoot.Load(balloonModel); //======================= /// Line strip from GPX /// // Get GPX points var segments = GpxImport.ReadGPX_Segments <GpxTrackPoint>(_gpxFile, p => p); var pointsGpx = segments.SelectMany(seg => seg); var geoPoints = pointsGpx.ToGeoPoints(); var model = _sharpGltfService.CreateNewModel(); //var largeMesh = GetMeshFromGpxTrack(outputDir, largeDataSet, geoPoints // , bboxScale: 5 // , zFactor: Z_FACTOR // , generateTIN: false // , tinPrecision: 500d // , drawGpxOnTexture: false // , ImageryProvider.OpenTopoMap); //meshes.Add(largeMesh); model = GetMeshFromGpxTrack(model, outputDir, localDataset, geoPoints , bboxScale: (1.3, 1.5) , zFactor: Z_FACTOR , generateTIN: false , tinPrecision: 50d , drawGpxOnTexture: true , ImageryProvider.EsriWorldImagery); var gpxPoints = geoPoints.ReprojectGeodeticToCartesian().ZScale(Z_FACTOR); model = _sharpGltfService.AddLine(model, gpxPoints, new Vector4(0, 1, 0, 0.5f), trailWidthMeters); // model export Console.WriteLine("GenerateModel..."); var node = model.LogicalNodes.First(); pointsGpx = pointsGpx.ReprojectGeodeticToCartesian().ZScale(Z_FACTOR); // animations node = CreateAnimationFromGpx("GPX", node, pointsGpx, 1f); node = CreateAnimationFromGpx("GPX x500", node, pointsGpx, 500f); var sceneBuilderBalloon = balloon.DefaultScene.ToSceneBuilder(); var sceneBuilderTerrain = model.DefaultScene.ToSceneBuilder(); //sceneBuilderBalloon. model.SaveGLB(Path.Combine(Directory.GetCurrentDirectory(), $"{GetType().Name}.glb")); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } }
public void GenerateModel(string bboxWkt, DEMDataSet litto3DDataset, ImageryProvider imageryProvider, float zFactor = 3f, bool withTexture = true, bool withboat = true, bool withWaterSurface = true, DEMDataSet fallbackDataset = null) { try { bool centerOnOrigin = true; int TEXTURE_TILES = 20; string outputDir = Directory.GetCurrentDirectory(); string modelName = $"{litto3DDataset.ResolutionMeters}m_z{zFactor}_{imageryProvider.Name}-{DateTime.Now:yyyyMMdd-hhmmss}"; _logger.LogInformation($"Getting height map data..."); var bbox = GeometryService.GetBoundingBox(bboxWkt).ReprojectTo(4326, litto3DDataset.SRID); var heightMap = _elevationService.GetHeightMap(ref bbox, litto3DDataset); heightMap = heightMap.BakeCoordinates(); var nullCoordsEnumerator = heightMap.Coordinates.Where(c => (c.Elevation ?? litto3DDataset.NoDataValue) == litto3DDataset.NoDataValue); var interpolator = _elevationService.GetInterpolator(InterpolationMode.Bilinear); using (IRasterFile raster = _rasterService.OpenFile(@"D:\Data\ELEVATION_DO_NOT_DELETE\GEBCO2020\cote azur\gebco_2020_subset.tif", DEMFileType.GEOTIFF)) using (RasterFileDictionary dic = new RasterFileDictionary()) { var metadata = raster.ParseMetaData(fallbackDataset.FileFormat); dic.Add(metadata, raster); foreach (var pt in nullCoordsEnumerator) { var proj = pt.ReprojectTo(litto3DDataset.SRID, fallbackDataset.SRID); pt.Elevation = _elevationService.GetElevationAtPoint(raster, dic, metadata, proj.Latitude, proj.Longitude, 0, interpolator, NoDataBehavior.UseNoDataDefinedInDem); } } var nullCoords = heightMap.Coordinates.Where(c => (c.Elevation ?? litto3DDataset.NoDataValue) == litto3DDataset.NoDataValue).ToList(); var nullCoordsFallbackProj = nullCoords.ReprojectTo(litto3DDataset.SRID, fallbackDataset.SRID, nullCoords.Count); var nullCoordsFallbackProj2 = _elevationService.GetPointsElevation(nullCoordsFallbackProj, fallbackDataset, InterpolationMode.Bilinear, NoDataBehavior.UseNoDataDefinedInDem); ModelGenerationTransform transform = new ModelGenerationTransform(bbox, 3857, centerOnOrigin: centerOnOrigin, zFactor, centerOnZOrigin: false); ModelGenerationTransform transformFrom4326 = new ModelGenerationTransform(bbox.ReprojectTo(2154, 4326), 3857, centerOnOrigin: centerOnOrigin, zFactor, centerOnZOrigin: false); _logger.LogInformation($"Processing height map data ({heightMap.Count} coordinates)..."); heightMap = transform.TransformHeightMap(heightMap); var min = heightMap.Coordinates.Where(g => (g.Elevation ?? 0d) > litto3DDataset.NoDataValue).Min(g => g.Elevation ?? 0d); heightMap.Coordinates = heightMap.Coordinates.Select(p => { if (p.Elevation.GetValueOrDefault(0) <= litto3DDataset.NoDataValue) { p.Elevation = min; } return(p); }); //======================= // Textures // PBRTexture pbrTexture = null; if (withTexture) { var bbox4326 = bbox.ReprojectTo(2154, 4326); Console.WriteLine("Download image tiles..."); TileRange tiles = _imageryService.DownloadTiles(bbox4326, imageryProvider, TEXTURE_TILES); string fileName = Path.Combine(outputDir, "Texture.jpg"); Console.WriteLine("Construct texture..."); //TextureInfo texInfo = _imageryService.ConstructTexture(tiles, bbox4326, fileName, TextureImageFormat.image_jpeg); TextureInfo texInfo; if (withboat) { var trackPoints = GetGeoPointFromGeoJson(BoatCourseGeoJson); texInfo = _imageryService.ConstructTextureWithGpxTrack(tiles, bbox4326, fileName, TextureImageFormat.image_jpeg, trackPoints, drawGpxVertices: true, color: SixLabors.ImageSharp.PixelFormats.Rgba32.Green, 30); } else { texInfo = _imageryService.ConstructTexture(tiles, bbox4326, fileName, TextureImageFormat.image_jpeg); } // //======================= //======================= // Normal map Console.WriteLine("Height map..."); //float Z_FACTOR = 0.00002f; //hMap = hMap.CenterOnOrigin().ZScale(Z_FACTOR); //var normalMap = _imageryService.GenerateNormalMap(heightMap, outputDir); pbrTexture = PBRTexture.Create(texInfo, null);// normalMap); //hMap = hMap.CenterOnOrigin(Z_FACTOR); // //======================= } // Triangulate height map // and add base and sides _logger.LogInformation($"Triangulating height map and generating 3D mesh..."); heightMap = heightMap.BakeCoordinates(); var coords = heightMap.Coordinates.ToList(); var model = _sharpGltfService.CreateTerrainMesh(heightMap, pbrTexture); if (withWaterSurface) { var bottomLeft = coords[heightMap.Width * (heightMap.Height - 1)].AsVector3(); bottomLeft.Z = 0; var topRight = coords[heightMap.Width - 1].AsVector3(); topRight.Z = 0; var topLeft = coords[0].AsVector3(); topLeft.Z = 0; var bottomRight = coords.Last().AsVector3(); bottomRight.Z = 0; var waterSurface = _meshService.CreateWaterSurface(bottomLeft, topRight, topLeft, bottomRight, minZ: (float)min, color: VectorsExtensions.CreateColor(0, 150, 255, 64)); model = _sharpGltfService.AddMesh(model, "Water", waterSurface, doubleSided: true); } if (withboat) { var boatInitPos = centerOnOrigin ? new GeoPoint(0, 0).AsVector3() : new GeoPoint(43.010625204304304, 6.3711613671060086).ReprojectTo(4326, 3857).AsVector3(); var axis = _meshService.CreateAxis(2, 10, 3, 3).Translate(boatInitPos); model = _sharpGltfService.AddMesh(model, "Boat", axis, doubleSided: false); var boatCourse = transformFrom4326.TransformPoints(GetGeoPointFromGeoJson(BoatCourseGeoJson)).ToList(); //ReprojectGeodeticToCartesian().CenterOnOrigin().ToList(); model = _sharpGltfService.AddLine(model, "BoatCourse", boatCourse, VectorsExtensions.CreateColor(255, 0, 0, 128), 4); } model.SaveGLB(Path.Combine(Directory.GetCurrentDirectory(), modelName + ".glb")); _logger.LogInformation($"Model exported as {Path.Combine(Directory.GetCurrentDirectory(), modelName + ".gltf")} and .glb"); //var point = new GeoPoint(43.01142119356318, 6.385200681010872).ReprojectTo(4326, 2154); //point = _elevationService.GetPointElevation(point, litto3DDataset); } catch (Exception e) { _logger.LogError(e, e.Message); } }
internal void Run(DEMDataSet dataSet, bool trackIn3D = true, bool generateTIN = false, int outputSrid = Reprojection.SRID_PROJECTED_LAMBERT_93) { try { string _gpxFile = Path.Combine("SampleData", "GPX", "lake-pleasant-camping.gpx"); bool withTexture = true; float Z_FACTOR = 1.8f; float Z_TRANSLATE_GPX_TRACK_METERS = 5; float trailWidthMeters = 5f; int skipGpxPointsEvery = 1; ImageryProvider provider = ImageryProvider.MapBoxSatellite; // new TileDebugProvider(null, maxDegreeOfParallelism: 1);// ImageryProvider.MapBoxSatellite; string outputDir = Path.GetFullPath("."); //======================= /// Line strip from GPX /// // Get GPX points var segments = GpxImport.ReadGPX_Segments(_gpxFile); var points = segments.SelectMany(seg => seg); var bbox = points.GetBoundingBox().Scale(1.1, 1.1).ReprojectTo(4326, dataSet.SRID); // DEBUG // Test case : ASTER GDEMv3 : 5.5 43.5 Z=315 // 303 307 308 // 309 *315* 317 // 314 321 324 //points = GenerateDebugTrailPointsGenerateDebugTrailPoints(5.003, 5.006, 43.995, 43.997, 0.0001, 0.001); //points = GenerateDebugTrailPointsGenerateDebugTrailPoints(5.4990, 5.501, 43.4990, 43.501, 0.0001, 0.001); //points = GenerateDebugTrailPointsGenerateDebugTrailPoints(5.49, 5.51, 43.49, 43.51, 0.0005, 0.001); //bbox = points.GetBoundingBox().Scale(1.3,1.3); IEnumerable <GeoPoint> gpxPointsElevated = _elevationService.GetPointsElevation(points, dataSet); // //======================= //======================= /// Height map (get dem elevation for bbox) /// HeightMap hMap = _elevationService.GetHeightMap(ref bbox, dataSet); // var refPoint = new GeoPoint(43.5, 5.5); // hMap = hMap.BakeCoordinates(); // var hMapRefPoint = hMap.Coordinates.OrderBy(c => c.DistanceSquaredTo(refPoint)).First(); // var gpxRefPoint = gpxPointsElevated.OrderBy(c => c.DistanceSquaredTo(refPoint)).First(); // hMapRefPoint.Elevation += 60; // gpxRefPoint.Elevation += 60; hMap = hMap.ReprojectTo(dataSet.SRID, outputSrid) //.CenterOnOrigin() .ZScale(Z_FACTOR) .BakeCoordinates(); // //======================= //======================= // Textures // PBRTexture pbrTexture = null; if (withTexture) { Console.WriteLine("Download image tiles..."); TileRange tiles = _imageryService.DownloadTiles(bbox, provider, 12); string fileName = Path.Combine(outputDir, "Texture.jpg"); Console.WriteLine("Construct texture..."); //TextureInfo texInfo = _imageryService.ConstructTextureWithGpxTrack(tiles, bbox, fileName, TextureImageFormat.image_jpeg, gpxPointsElevated, false); TextureInfo texInfo = _imageryService.ConstructTexture(tiles, bbox, fileName, TextureImageFormat.image_jpeg); // //======================= //======================= // Normal map Console.WriteLine("Height map..."); //float Z_FACTOR = 0.00002f; //hMap = hMap.CenterOnOrigin().ZScale(Z_FACTOR); //var normalMap = _imageryService.GenerateNormalMap(hMap, outputDir); //pbrTexture = PBRTexture.Create(texInfo, normalMap); pbrTexture = PBRTexture.Create(texInfo, null); //hMap = hMap.CenterOnOrigin(Z_FACTOR); // //======================= } //======================= // MESH 3D terrain Console.WriteLine("Height map..."); Console.WriteLine("GenerateTriangleMesh..."); //hMap = _elevationService.GetHeightMap(bbox, _dataSet); ModelRoot model = null; if (generateTIN) { model = TINGeneration.GenerateTIN(hMap, 10d, _sharpGltfService, pbrTexture, outputSrid); } else { //hMap = hMap.CenterOnOrigin().ZScale(Z_FACTOR); // generate mesh with texture model = _sharpGltfService.CreateTerrainMesh(hMap, pbrTexture); } if (trackIn3D) { // take 1 point evert nth gpxPointsElevated = gpxPointsElevated.Where((x, i) => (i + 1) % skipGpxPointsEvery == 0); gpxPointsElevated = gpxPointsElevated.ZTranslate(Z_TRANSLATE_GPX_TRACK_METERS) .ReprojectTo(dataSet.SRID, outputSrid) //.CenterOnOrigin() //.CenterOnOrigin(hMap.BoundingBox) .ZScale(Z_FACTOR); model = _sharpGltfService.AddLine(model, "GPX", gpxPointsElevated, VectorsExtensions.CreateColor(255, 0, 0, 128), trailWidthMeters); } // model export Console.WriteLine("GenerateModel..."); model.SaveGLB(Path.Combine(Directory.GetCurrentDirectory(), $"{GetType().Name} dst{dataSet.Name} TIN{generateTIN} Srid{outputSrid}.glb")); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } }
/// <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); } }