Source code for py3dtiles.tileset.content.pnts
from __future__ import annotations
import struct
from typing import Any
import numpy as np
import numpy.typing as npt
from py3dtiles.tileset.batch_table import BatchTable
from py3dtiles.tileset.feature_table import Feature, FeatureTable
from py3dtiles.tileset.tile_content import TileContent, TileContentBody, TileContentHeader, TileContentType
[docs]
class Pnts(TileContent):
[docs]
@staticmethod
def from_features(pd_type: npt.DTypeLike, cd_type: npt.DTypeLike, features: list[Feature]) -> Pnts:
"""
Creates a Pnts from features defined by pd_type and cd_type.
"""
ft = FeatureTable.from_features(pd_type, cd_type, features)
tb = PntsBody()
tb.feature_table = ft
th = PntsHeader()
t = Pnts()
t.body = tb
t.header = th
return t
[docs]
@staticmethod
def from_array(array: npt.NDArray) -> Pnts:
"""
Creates a Pnts from an array
"""
# build tile header
h_arr = array[0:PntsHeader.BYTE_LENGTH]
h = PntsHeader.from_array(h_arr)
if h.tile_byte_length != len(array):
raise RuntimeError("Invalid byte length in header")
# build tile body
b_len = h.ft_json_byte_length + h.ft_bin_byte_length
b_arr = array[PntsHeader.BYTE_LENGTH:PntsHeader.BYTE_LENGTH + b_len]
b = PntsBody.from_array(h, b_arr)
# build TileContent with header and body
t = Pnts()
t.header = h
t.body = b
return t
[docs]
class PntsHeader(TileContentHeader):
BYTE_LENGTH = 28
def __init__(self) -> None:
super().__init__()
self.type = TileContentType.POINT_CLOUD
self.magic_value = b"pnts"
self.version = 1
[docs]
def to_array(self) -> npt.NDArray[np.uint8]:
"""
Returns the header as a numpy array.
"""
header_arr = np.frombuffer(self.magic_value, np.uint8)
header_arr2 = np.array([self.version,
self.tile_byte_length,
self.ft_json_byte_length,
self.ft_bin_byte_length,
self.bt_json_byte_length,
self.bt_bin_byte_length], dtype=np.uint32)
return np.concatenate((header_arr, header_arr2.view(np.uint8)))
[docs]
def sync(self, body: PntsBody) -> None:
"""
Synchronizes headers with the Pnts body.
"""
# extract arrays
feature_table_header_array = body.feature_table.header.to_array()
feature_table_body_array = body.feature_table.body.to_array()
batch_table_header_array = body.batch_table.to_array() # for now, there is only json part in the batch table
batch_table_body_array: list[Any] = [] # body.batch_table.body.to_array()
# sync the tile header with feature table contents
self.tile_byte_length = (
len(feature_table_header_array) + len(feature_table_body_array)
+ len(batch_table_header_array) + len(batch_table_body_array)
+ PntsHeader.BYTE_LENGTH
)
self.ft_json_byte_length = len(feature_table_header_array)
self.ft_bin_byte_length = len(feature_table_body_array)
self.bt_json_byte_length = len(batch_table_header_array)
self.bt_bin_byte_length = len(batch_table_body_array)
[docs]
@staticmethod
def from_array(array: npt.NDArray) -> PntsHeader:
"""
Create a PntsHeader from an array
"""
h = PntsHeader()
if len(array) != PntsHeader.BYTE_LENGTH:
raise RuntimeError("Invalid header length")
h.version = struct.unpack("i", array[4:8].tobytes())[0]
h.tile_byte_length = struct.unpack("i", array[8:12].tobytes())[0]
h.ft_json_byte_length = struct.unpack("i", array[12:16].tobytes())[0]
h.ft_bin_byte_length = struct.unpack("i", array[16:20].tobytes())[0]
h.bt_json_byte_length = struct.unpack("i", array[20:24].tobytes())[0]
h.bt_bin_byte_length = struct.unpack("i", array[24:28].tobytes())[0]
return h
[docs]
class PntsBody(TileContentBody):
def __init__(self) -> None:
self.feature_table = FeatureTable()
self.batch_table = BatchTable()
[docs]
def to_array(self) -> npt.NDArray:
"""
Returns the body as a numpy array.
"""
feature_table_array = self.feature_table.to_array()
batch_table_array = self.batch_table.to_array()
return np.concatenate((feature_table_array, batch_table_array))
[docs]
@staticmethod
def from_array(header: PntsHeader, array: npt.NDArray) -> PntsBody:
"""
Creates a PntsBody from an array and the header
"""
# build feature table
feature_table_size = header.ft_json_byte_length + header.ft_bin_byte_length
feature_table_array = array[0:feature_table_size]
feature_table = FeatureTable.from_array(header, feature_table_array)
# build batch table
batch_table_size = header.bt_json_byte_length + header.bt_bin_byte_length
batch_table_array = array[feature_table_size:feature_table_size + batch_table_size]
batch_table = BatchTable.from_array(header, batch_table_array)
# build tile body with feature table
body = PntsBody()
body.feature_table = feature_table
body.batch_table = batch_table
return body