From eb52826a36648427eb066194878632158bc19fb9 Mon Sep 17 00:00:00 2001 From: tebben Date: Thu, 18 Jan 2024 12:19:06 +0100 Subject: [PATCH] Start support delatin based meshing --- .../cog_processor_quantized_mesh_delatin.py | 35 +++++++++++++++ .../cog_processor_quantized_mesh_grid.py | 9 ++-- ...errain_generator_quantized_mesh_delatin.py | 43 +++++++++++++++++++ ctod/handlers/terrain.py | 29 ++++++++----- ctod/templates/static/controls.js | 21 +++++++-- ctod/templates/static/index.js | 7 +-- 6 files changed, 122 insertions(+), 22 deletions(-) create mode 100644 ctod/core/cog/processor/cog_processor_quantized_mesh_delatin.py create mode 100644 ctod/core/terrain/generator/terrain_generator_quantized_mesh_delatin.py diff --git a/ctod/core/cog/processor/cog_processor_quantized_mesh_delatin.py b/ctod/core/cog/processor/cog_processor_quantized_mesh_delatin.py new file mode 100644 index 0000000..ef7c07d --- /dev/null +++ b/ctod/core/cog/processor/cog_processor_quantized_mesh_delatin.py @@ -0,0 +1,35 @@ +from ctod.core.cog.cog_request import CogRequest +from ctod.core.cog.processor.cog_processor import CogProcessor +from ctod.core.normals import calculate_normals +from ctod.core.utils import rescale_positions +from pydelatin import Delatin +from quantized_mesh_encoder.constants import WGS84 +from quantized_mesh_encoder.ecef import to_ecef +from quantized_mesh_encoder.ellipsoid import Ellipsoid + +class CogProcessorQuantizedMeshDelatin(CogProcessor): + """A CogProcessor for a Delatin based mesh. + + - Create Delatin mesh + - Calculate normals + """ + + def __init__(self): + self.ellipsoid: Ellipsoid = WGS84 + + def process(self, cog_request: CogRequest) -> tuple: + """Process a CogRequest and return the vertices, triangles and normals created with PyDelatin. + + Args: + cog_request (CogRequest): The CogRequest to process. + + Returns: + tuple: vertices, triangles and normals + """ + + tin = Delatin(cog_request.data.data[0], max_error=1) + rescaled = rescale_positions(tin.vertices, cog_request.tile_bounds, flip_y=False) + cartesian = to_ecef(rescaled, ellipsoid=self.ellipsoid) + normals = calculate_normals(cartesian, tin.triangles) if cog_request.generate_normals else None + + return (tin.vertices, tin.triangles, normals) \ No newline at end of file diff --git a/ctod/core/cog/processor/cog_processor_quantized_mesh_grid.py b/ctod/core/cog/processor/cog_processor_quantized_mesh_grid.py index 26889a5..fea1bd9 100644 --- a/ctod/core/cog/processor/cog_processor_quantized_mesh_grid.py +++ b/ctod/core/cog/processor/cog_processor_quantized_mesh_grid.py @@ -2,13 +2,12 @@ from ctod.core.cog.cog_request import CogRequest from ctod.core.cog.processor.cog_processor import CogProcessor -from quantized_mesh_encoder.ecef import to_ecef -from quantized_mesh_encoder.constants import WGS84 -from quantized_mesh_encoder.ellipsoid import Ellipsoid -from ctod.core.utils import rescale_positions from ctod.core.cog.processor.grid import generate_grid from ctod.core.normals import calculate_normals - +from ctod.core.utils import rescale_positions +from quantized_mesh_encoder.constants import WGS84 +from quantized_mesh_encoder.ecef import to_ecef +from quantized_mesh_encoder.ellipsoid import Ellipsoid class CogProcessorQuantizedMeshGrid(CogProcessor): """A CogProcessor for a grid based mesh. diff --git a/ctod/core/terrain/generator/terrain_generator_quantized_mesh_delatin.py b/ctod/core/terrain/generator/terrain_generator_quantized_mesh_delatin.py new file mode 100644 index 0000000..1e540d3 --- /dev/null +++ b/ctod/core/terrain/generator/terrain_generator_quantized_mesh_delatin.py @@ -0,0 +1,43 @@ +import logging +import numpy as np + +from ctod.core.cog.cog_request import CogRequest +from ctod.core.direction import Direction +from ctod.core.terrain.generator.terrain_generator import TerrainGenerator +from ctod.core.terrain.empty_tile import generate_empty_tile +from ctod.core.terrain.terrain_request import TerrainRequest +from ctod.core.terrain.quantize import quantize +from ctod.core.utils import rescale_positions + + +class TerrainGeneratorQuantizedMeshDelatin(TerrainGenerator): + """A TerrainGenerator for a delatin based mesh.""" + + def __init__(self): + pass + + def generate(self, terrain_request: TerrainRequest) -> bytes: + """Generate a quantized mesh grid based on the terrain request. + + Args: + terrain_request (TerrainRequest): The terrain request. + + Returns: + quantized_mesh (bytes): The generated quantized mesh + """ + + main_cog = terrain_request.get_main_file() + + # should not happen, in case it does return empty tile + if main_cog.processed_data is None: + logging.debug("main_cog.processed_data is None") + quantized_empty_tile = generate_empty_tile(main_cog.tms, main_cog.z, main_cog.x, main_cog.y) + return quantized_empty_tile + + vertices, triangles, normals = main_cog.processed_data + + # Rescale the vertices to the tile bounds and create quantized mesh + rescaled_vertices = rescale_positions(vertices, main_cog.tile_bounds, flip_y=False) + quantized = quantize(rescaled_vertices, triangles, normals) + + return quantized diff --git a/ctod/handlers/terrain.py b/ctod/handlers/terrain.py index 1efe530..75d84c9 100644 --- a/ctod/handlers/terrain.py +++ b/ctod/handlers/terrain.py @@ -4,10 +4,12 @@ from ctod.core import utils from ctod.core.cog.processor.cog_processor import CogProcessor from ctod.core.cog.processor.cog_processor_quantized_mesh_grid import CogProcessorQuantizedMeshGrid +from ctod.core.cog.processor.cog_processor_quantized_mesh_delatin import CogProcessorQuantizedMeshDelatin from ctod.core.terrain.generator.terrain_generator import TerrainGenerator from ctod.core.terrain.terrain_request import TerrainRequest from ctod.core.terrain.empty_tile import generate_empty_tile from ctod.core.terrain.generator.terrain_generator_quantized_mesh_grid import TerrainGeneratorQuantizedMeshGrid +from ctod.core.terrain.generator.terrain_generator_quantized_mesh_delatin import TerrainGeneratorQuantizedMeshDelatin from ctod.core.tile_cache import get_tile_from_disk, save_tile_to_disk from ctod.handlers.base import BaseHandler from morecantile import TileMatrixSet @@ -27,6 +29,17 @@ def __init__(self, application, request, **kwargs): self.terrain_factory = kwargs.pop('terrain_factory') self.tile_cache_path = kwargs.pop('tile_cache_path') self.cog_reader_pool = kwargs.pop('cog_reader_pool') + self.cog_processors = { + "default": CogProcessorQuantizedMeshGrid, + "grid": CogProcessorQuantizedMeshGrid, + "delatin": CogProcessorQuantizedMeshDelatin, + } + self.terrain_generators = { + "default": TerrainGeneratorQuantizedMeshGrid, + "grid": TerrainGeneratorQuantizedMeshGrid, + "delatin": TerrainGeneratorQuantizedMeshDelatin + } + super(TerrainHandler, self).__init__(application, request, **kwargs) async def get(self, z: int, x: int, y: int): @@ -65,7 +78,7 @@ async def get(self, z: int, x: int, y: int): return - cog_processor = self.get_cog_processor(meshing_method) + cog_processor = self._get_cog_processor(meshing_method) terrain_generator = self._get_terrain_generator(meshing_method) self.terrain_request = TerrainRequest(tms, cog, z, x, y, resampling_method, cog_processor, terrain_generator, self.cog_reader_pool, extensions["octvertexnormals"]) quantized = await self.terrain_factory.handle_request(self.terrain_request) @@ -101,12 +114,10 @@ def _get_terrain_generator(self, meshing_method: str) -> TerrainGenerator: TerrainGenerator: Terrain generator based on given meshing method """ - if meshing_method == "grid": - return TerrainGeneratorQuantizedMeshGrid() - - return TerrainGeneratorQuantizedMeshGrid() + terrain_generator = self.terrain_generators.get(meshing_method, self.terrain_generators["default"]) + return terrain_generator() - def get_cog_processor(self, meshing_method: str) -> CogProcessor: + def _get_cog_processor(self, meshing_method: str) -> CogProcessor: """Get the cog processor based on the meshing method Args: @@ -116,10 +127,8 @@ def get_cog_processor(self, meshing_method: str) -> CogProcessor: CogProcessor: Cog processor based on given meshing method """ - if meshing_method == "grid": - return CogProcessorQuantizedMeshGrid() - - return CogProcessorQuantizedMeshGrid() + cog_processor = self.cog_processors.get(meshing_method, self.cog_processors["default"]) + return cog_processor() def _return_empty_terrain(self, tms: TileMatrixSet, cog: str, meshing_method: str, resampling_method, z: int, x: int, y: int): """Return an empty terrain tile diff --git a/ctod/templates/static/controls.js b/ctod/templates/static/controls.js index a05577f..011e9c8 100644 --- a/ctod/templates/static/controls.js +++ b/ctod/templates/static/controls.js @@ -2,6 +2,7 @@ var module, pane, terrainFolder, layerFolder, materialFolder, urlParams; var minZoomValue = 1; var maxZoomValue = 21; +var meshingMethodValue = "grid"; var cogValue = "./ctod/files/test_cog.tif"; var resamplingValue = "bilinear"; @@ -26,6 +27,7 @@ function setupTweakpane() { cogValue = getStringParameterValue("cog", cogValue); resamplingValue = getStringParameterValue("resamplingMethod", resamplingValue); skipCacheValue = getBoolParameterValue("skipCache", skipCacheValue); + meshingMethodValue = getStringParameterValue("meshingMethod", meshingMethodValue); pane = new module.Pane({ title: "CTOD", @@ -58,8 +60,6 @@ function setupTweakpane() { expanded: false, }); - - createTerrainPane(); createLayerPane(); createMaterialPane(); @@ -102,7 +102,8 @@ function createMaterialPane() { function createTerrainPane() { const PARAMS = { cog: cogValue, - resampling: resamplingValue + resampling: resamplingValue, + meshing: meshingMethodValue }; cog = terrainFolder.addBinding(PARAMS, "cog", {}); @@ -167,6 +168,18 @@ function createTerrainPane() { resamplingValue = ev.value; updateTerrainProvider(); }); + + const meshingMethod = terrainFolder.addBinding(PARAMS, "meshing", { + options: { + grid: "grid", + delatin: "delatin" + }, + }); + + meshingMethod.on("change", (ev) => { + meshingMethodValue = ev.value; + updateTerrainProvider(); + }); } function createLayerPane() { @@ -206,7 +219,7 @@ function createLayerPane() { } function updateTerrainProvider() { - setTerrainProvider(minZoomValue, maxZoomValue, cogValue, resamplingValue, skipCacheValue); + setTerrainProvider(minZoomValue, maxZoomValue, cogValue, resamplingValue, skipCacheValue, meshingMethodValue); } function getStringParameterValue(param, defaultValue) { diff --git a/ctod/templates/static/index.js b/ctod/templates/static/index.js index e08ce1b..ceaf023 100644 --- a/ctod/templates/static/index.js +++ b/ctod/templates/static/index.js @@ -52,7 +52,8 @@ function initializeLayers() { urlParams.get("cog") || "./ctod/files/test_cog.tif"; const skipCache = urlParams.get("skipCache") || false; - setTerrainProvider(minZoom, maxZoom, cog, "bilinear", skipCache); + const meshingMethod = urlParams.get("meshingMethod") || "grid"; + setTerrainProvider(minZoom, maxZoom, cog, "bilinear", skipCache, meshingMethod); streetsLayer.show = true; satelliteLayer.show = false; @@ -110,8 +111,8 @@ function configureViewer() { }); } -function setTerrainProvider(minZoom, maxZoom, cog, resamplingMethod, skipCache) { - const terrainProviderUrl = `${window.location.origin}/tiles?minZoom=${minZoom}&maxZoom=${maxZoom}&cog=${cog}&resamplingMethod=${resamplingMethod}&skipCache=${skipCache}`; +function setTerrainProvider(minZoom, maxZoom, cog, resamplingMethod, skipCache, meshingMethod) { + const terrainProviderUrl = `${window.location.origin}/tiles?minZoom=${minZoom}&maxZoom=${maxZoom}&cog=${cog}&resamplingMethod=${resamplingMethod}&skipCache=${skipCache}&meshingMethod=${meshingMethod}`; terrainProvider = new Cesium.CesiumTerrainProvider({ url: terrainProviderUrl,