import logging
from typing import BinaryIO, List
import numpy as np
from .known import vlr_factory, IKnownVLR
from .vlr import VLR
from ..utils import encode_to_len
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
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 pylas
>>> from pylas.vlrs.known import ExtraBytesVlr, WktCoordinateSystemVlr
>>> las = pylas.read("pylastests/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
"""
if user_id != "" and record_ids != (None,):
return [
vlr
for vlr in self
if vlr.user_id == user_id and vlr.record_id in record_ids
]
else:
return [
vlr
for vlr in self
if vlr.user_id == user_id 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 pylas
>>> las = pylas.read("pylastests/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]
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
-------
pylas.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).decode().rstrip("\0")
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 = data_stream.read(DESCRIPTION_LEN).decode().rstrip("\0")
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) -> int:
bytes_written = 0
for vlr in self:
record_data = vlr.record_data_bytes()
stream.write(b"\0\0")
stream.write(encode_to_len(vlr.user_id, USER_ID_LEN))
stream.write(vlr.record_id.to_bytes(2, byteorder="little", signed=False))
if as_extended:
if len(record_data) > np.iinfo("uint16").max:
raise ValueError("vlr record_data is too long")
stream.write(
len(record_data).to_bytes(8, byteorder="little", signed=False)
)
else:
stream.write(
len(record_data).to_bytes(2, byteorder="little", signed=False)
)
stream.write(encode_to_len(vlr.description, DESCRIPTION_LEN))
stream.write(record_data)
bytes_written += 54 if not as_extended else 60
bytes_written += len(record_data)
return bytes_written