Source code for py3dtiles.utils

from __future__ import annotations

from enum import Enum
from io import StringIO
from pathlib import Path, PurePath
from typing import Callable

import numpy as np
from pyproj import CRS


[docs] def str_to_CRS(srs: str | CRS | None) -> CRS | None: """ Convert a string in a pyproj CRS object. The string could be an epsg code or a Proj4 string. If srs is already a CRS object, the function returns the same CRS definition. """ if srs is None: return None try: return CRS.from_epsg(int(srs)) # type: ignore except ValueError: return CRS(srs)
[docs] class CommandType(Enum): READ_FILE = b'read_file' WRITE_PNTS = b'write_pnts' PROCESS_JOBS = b'process_jobs' SHUTDOWN = b'shutdown'
[docs] class ResponseType(Enum): IDLE = b'idle' HALTED = b'halted' READ = b'read' PROCESSED = b'processed' PNTS_WRITTEN = b'pnts_written' NEW_TASK = b'new_task' ERROR = b'error'
[docs] def profile(func: Callable) -> Callable: from line_profiler import LineProfiler def wrapper(*args, **kwargs): lp = LineProfiler() deco = lp(func) res = deco(*args, **kwargs) s = StringIO() lp.print_stats(stream=s) print(s.getvalue()) return res return wrapper
[docs] class SubdivisionType(Enum): OCTREE = 1 QUADTREE = 2
[docs] def node_name_to_path(working_dir: Path, name: bytes, suffix: str = '', split_len: int = 8) -> Path: """ Get the path of a tile from its name and the working directory. If the name is '222262175' with the suffix '.pnts', the result is 'working_dir/22226217/r5.pnts' """ str_name = name.decode('ascii') if len(str_name) <= split_len: filename = PurePath("r" + str_name + suffix) else: # the name is split on every 'split_len' char to avoid to have too many tiles on the same folder. sub_folders = [str_name[i:i + split_len] for i in range(0, len(str_name), split_len)] working_dir = working_dir.joinpath(*sub_folders[:-1]) filename = PurePath("r" + sub_folders[-1] + suffix) full_path = working_dir / filename working_dir.mkdir(parents=True, exist_ok=True) return full_path
[docs] def compute_spacing(aabb: np.ndarray) -> float: return float(np.linalg.norm(aabb[1] - aabb[0]) / 125)
[docs] def aabb_size_to_subdivision_type(size: np.ndarray) -> SubdivisionType: if size[2] / min(size[0], size[1]) < 0.5: return SubdivisionType.QUADTREE else: return SubdivisionType.OCTREE
[docs] def split_aabb(aabb: np.ndarray, index: int, force_quadtree: bool = False) -> np.ndarray: half = (aabb[1] - aabb[0]) * 0.5 t = aabb_size_to_subdivision_type(half) new_aabb = np.array([np.copy(aabb[0]), aabb[0] + half]) if index & 4: new_aabb[0][0] += half[0] new_aabb[1][0] += half[0] if index & 2: new_aabb[0][1] += half[1] new_aabb[1][1] += half[1] if force_quadtree or t == SubdivisionType.QUADTREE: new_aabb[1][2] += half[2] elif index & 1: new_aabb[0][2] += half[2] new_aabb[1][2] += half[2] return new_aabb
[docs] def make_aabb_cubic(aabb): s = max(aabb[1] - aabb[0]) aabb[1][0] = aabb[0][0] + s aabb[1][1] = aabb[0][1] + s aabb[1][2] = aabb[0][2] + s return aabb
[docs] def node_from_name(name, parent_aabb, parent_spacing): from py3dtiles.tilers.node import Node spacing = parent_spacing * 0.5 aabb = split_aabb(parent_aabb, int(name[-1])) if len(name) > 0 else parent_aabb #  let's build a new Node return Node(name, aabb, spacing)