Source code for laspy.vlrs.vlrlist

import logging
from typing import BinaryIO, List

import numpy as np

from .known import vlr_factory, IKnownVLR
from .vlr import VLR
from ..utils import read_string, write_as_c_string

logger = logging.getLogger(__name__)

RESERVED_LEN = 2
USER_ID_LEN = 16
DESCRIPTION_LEN = 32


[docs]class VLRList(list): """Class responsible for managing the vlrs""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)
[docs] def index(self, value, start: int = 0, stop: int = None) -> int: if stop is None: stop = len(self) if isinstance(value, str): for i, vlr in enumerate(self[start:stop]): if vlr.__class__.__name__ == value: return i + start raise ValueError(f"VLR '{value}' could not be found in the list") else: return super().index(value, start, stop)
[docs] def get_by_id(self, user_id="", record_ids=(None,)): """Function to get vlrs by user_id and/or record_ids. Always returns a list even if only one vlr matches the user_id and record_id >>> import laspy >>> from laspy.vlrs.known import ExtraBytesVlr, WktCoordinateSystemVlr >>> las = laspy.read("tests/data/extrabytes.las") >>> las.vlrs [<ExtraBytesVlr(extra bytes structs: 5)>] >>> las.vlrs.get(WktCoordinateSystemVlr.official_user_id()) [] >>> las.vlrs.get(WktCoordinateSystemVlr.official_user_id())[0] Traceback (most recent call last): IndexError: list index out of range >>> las.vlrs.get_by_id(ExtraBytesVlr.official_user_id()) [<ExtraBytesVlr(extra bytes structs: 5)>] >>> las.vlrs.get_by_id(ExtraBytesVlr.official_user_id())[0] <ExtraBytesVlr(extra bytes structs: 5)> Parameters ---------- user_id: str, optional the user id record_ids: iterable of int, optional THe record ids of the vlr(s) you wish to get Returns ------- :py:class:`list` a list of vlrs matching the user_id and records_ids """ return [ vlr for vlr in self if (user_id == "" or vlr.user_id == user_id) and (record_ids == (None,) or vlr.record_id in record_ids) ]
[docs] def get(self, vlr_type: str) -> List[IKnownVLR]: """Returns the list of vlrs of the requested type Always returns a list even if there is only one VLR of type vlr_type. >>> import laspy >>> las = laspy.read("tests/data/extrabytes.las") >>> las.vlrs [<ExtraBytesVlr(extra bytes structs: 5)>] >>> las.vlrs.get("WktCoordinateSystemVlr") [] >>> las.vlrs.get("WktCoordinateSystemVlr")[0] Traceback (most recent call last): IndexError: list index out of range >>> las.vlrs.get('ExtraBytesVlr') [<ExtraBytesVlr(extra bytes structs: 5)>] >>> las.vlrs.get('ExtraBytesVlr')[0] <ExtraBytesVlr(extra bytes structs: 5)> Parameters ---------- vlr_type: str the class name of the vlr Returns ------- :py:class:`list` a List of vlrs matching the user_id and records_ids """ return [v for v in self if v.__class__.__name__ == vlr_type]
[docs] def extract(self, vlr_type: str) -> List[IKnownVLR]: """Returns the list of vlrs of the requested type The difference with get is that the returned vlrs will be removed from the list Parameters ---------- vlr_type: str the class name of the vlr Returns ------- list a List of vlrs matching the user_id and records_ids """ kept_vlrs, extracted_vlrs = [], [] for vlr in self: if vlr.__class__.__name__ == vlr_type: extracted_vlrs.append(vlr) else: kept_vlrs.append(vlr) self.clear() self.extend(kept_vlrs) return extracted_vlrs
def __repr__(self): return "[{}]".format(", ".join(repr(vlr) for vlr in self))
[docs] @classmethod def read_from( cls, data_stream: BinaryIO, num_to_read: int, extended: bool = False ) -> "VLRList": """Reads vlrs and parse them if possible from the stream Parameters ---------- data_stream : io.BytesIO stream to read from num_to_read : int number of vlrs to be read extended : bool whether the vlrs are regular vlr or extended vlr Returns ------- laspy.vlrs.vlrlist.VLRList List of vlrs """ vlrlist = cls() for _ in range(num_to_read): data_stream.read(RESERVED_LEN) user_id = data_stream.read(USER_ID_LEN).split(b"\0")[0].decode() record_id = int.from_bytes( data_stream.read(2), byteorder="little", signed=False ) if extended: record_data_len = int.from_bytes( data_stream.read(8), byteorder="little", signed=False ) else: record_data_len = int.from_bytes( data_stream.read(2), byteorder="little", signed=False ) description = read_string(data_stream, DESCRIPTION_LEN) record_data_bytes = data_stream.read(record_data_len) vlr = VLR(user_id, record_id, description, record_data_bytes) vlrlist.append(vlr_factory(vlr)) return vlrlist
[docs] def write_to( self, stream: BinaryIO, as_extended: bool = False, encoding_errors: str = "strict", ) -> int: bytes_written = 0 for vlr in self: record_data = vlr.record_data_bytes() stream.write(b"\0\0") write_as_c_string(stream, vlr.user_id, USER_ID_LEN) stream.write(vlr.record_id.to_bytes(2, byteorder="little", signed=False)) if as_extended: stream.write( len(record_data).to_bytes(8, byteorder="little", signed=False) ) else: max_length = np.iinfo("uint16").max if len(record_data) > max_length: raise ValueError( f"VLR record_date length ({len(record_data)}) exceeds the maximum length ({max_length})" ) stream.write( len(record_data).to_bytes(2, byteorder="little", signed=False) ) write_as_c_string( stream, vlr.description, DESCRIPTION_LEN, encoding_errors=encoding_errors, ) stream.write(record_data) bytes_written += 54 if not as_extended else 60 bytes_written += len(record_data) return bytes_written