Source code for dtcc_model.geometry.linestring

# Copyright(C) 2024 Dag Wästberg
# Licensed under the MIT License

from dataclasses import dataclass, field
from typing import Union
from .geometry import Geometry, Bounds
import numpy as np

from shapely.geometry import (
    LineString as ShapelyLineString,
    MultiLineString as ShapelyMultiLineString,
)

from dtcc_model import dtcc_pb2 as proto


[docs] @dataclass class LineString(Geometry): vertices: np.ndarray = field(default_factory=lambda: np.empty(0))
[docs] def calculate_bounds(self): """Calculate the bounding box of the line string.""" if len(self.vertices) == 0: self._bounds = Bounds() return self._bounds xmin = np.min(self.vertices[:, 0]) ymin = np.min(self.vertices[:, 1]) xmax = np.max(self.vertices[:, 0]) ymax = np.max(self.vertices[:, 1]) if self.vertices.shape[1] == 3: zmin = np.min(self.vertices[:, 2]) zmax = np.max(self.vertices[:, 2]) else: zmin = 0 zmax = 0 self._bounds = Bounds( xmin=xmin, ymin=ymin, zmin=zmin, xmax=xmax, ymax=ymax, zmax=zmax, ) return self._bounds
@property def length(self): """Calculate the length of the line string.""" return np.sum(np.linalg.norm(np.diff(self.vertices, axis=0), axis=1))
[docs] def to_shapely(self): """Convert the LineString to a Shapely LineString.""" return ShapelyLineString(self.vertices)
[docs] def from_shapely(self, shape): """Initialize the LineString from a Shapely LineString.""" self.vertices = np.array(shape.coords) return self
[docs] def to_proto(self) -> proto.Geometry: """Return a protobuf representation of the LineString. Returns ------- proto.Geometry A protobuf representation of the MultiSurface as a Geometry. """ # Handle Geometry fields pb = Geometry.to_proto(self) _pb = proto.LineString() _pb.vertices.extend(self.vertices.flatten()) _pb.dim = self.vertices.shape[1] pb.line_string.CopyFrom(_pb) return pb
[docs] def from_proto(self, pb: Union[proto.Geometry, bytes], only_linestring_field=False): # Handle byte representation if isinstance(pb, bytes): pb = proto.Geometry.FromString(pb) # Handle Geometry fields if not only_linestring_field: Geometry.from_proto(self, pb) _pb = pb if only_linestring_field else pb.line_string dim = _pb.dim self.vertices = np.array(_pb.vertices).reshape(-1, dim)
[docs] @dataclass class MultiLineString(Geometry): linestrings: list[LineString] = field(default_factory=lambda: [])
[docs] def calculate_bounds(self): """Calculate the bounding box of the multi line string.""" if len(self.linestrings) == 0: return Bounds() bounds = [line.calculate_bounds() for line in self.linestrings] self._bounds = Bounds( xmin=np.min([b.xmin for b in bounds]), ymin=np.min([b.ymin for b in bounds]), zmin=np.min([b.zmin for b in bounds]), xmax=np.max([b.xmax for b in bounds]), ymax=np.max([b.ymax for b in bounds]), zmax=np.max([b.zmax for b in bounds]), ) return self._bounds
@property def length(self): """Calculate the length of the multi line string.""" return sum([line.length for line in self.linestrings])
[docs] def to_shapely(self): """Convert the MultiLineString to a Shapely MultiLineString.""" return ShapelyMultiLineString([line.to_shapely() for line in self.linestrings])
[docs] def from_shapely(self, shape): """Initialize the MultiLineString from a Shapely MultiLineString.""" self.linestrings = [LineString().from_shapely(l) for l in shape] return self
[docs] def to_proto(self): """Return a protobuf representation of the MultiSurface. Returns ------- proto.Geometry A protobuf representation of the MultiSurface as a Geometry. """ # Handle Geometry fields pb = Geometry.to_proto(self) _pb = proto.MultiLineString() _pb.line_strings.extend( [line.to_proto().line_string for line in self.linestrings] ) pb.multi_line_string.CopyFrom(_pb) return pb
[docs] def from_proto(self, pb: Union[proto.Geometry, bytes]): # Handle byte representation if isinstance(pb, bytes): pb = proto.Geometry.FromString(pb) # Handle Geometry fields Geometry.from_proto(self, pb) # Handle specific fields _pb = pb.multi_line_string for line_string in _pb.line_strings: _linestring = LineString() _linestring.from_proto(line_string, only_linestring_field=True) self.linestrings.append(_linestring)