ProfileXYZ.py 6.62 KiB
# -*- coding: utf-8 -*-

import numpy as np
import pandas as pd
from typing import List

from Model.Geometry.Profile import Profile
from Model.Geometry.PointXYZ import PointXYZ
from Model.Geometry.Vector_1d import Vector1d

class ProfileXYZ(Profile):
    def __init__(self, num: int = 0,
                 code1: int = 0, code2: int = 0,
                 nb_point: int = 0,
                 kp: float = 0., name: str = "",
                 reach = None):
        """ProfileXYZ constructor

        Args:
            num: The number of this profile
            code1: The interpolation code 1
            code2: The interpolation code 2
            kp: Kilometer point
            name: The name of profile

        Returns:
            Nothing.
        """
        super(ProfileXYZ, self).__init__(
            num = num,
            name = name,
            kp = kp,
            code1 = code1, code2 = code2,
            _type = "XYZ",
            reach = reach,
        )

    def __repr__(self):
        df = pd.DataFrame(columns=["X", "Y", "Z", "Name"],
                          data=[[p.x, p.y, p.z, p.name] for p in self._points])
        return f"\nProfileXYZ : {self.name}\n{df}"

    @property
    def header(self):
        """
        Returns:
            Profile header.
        """
        return np.array(
            [self._num, self._code1, self._code2,
             self.nb_points(), self._kp, self._name]
        )

    def x(self):
        return [point.x for point in self._points]

    def y(self):
        return [point.y for point in self._points]

    def z(self):
        return [point.z for point in self._points]

    def names(self):
        return [point.name for point in self._points]

    def x_max(self):
        return max(self.filter_isnan(self.x()))

    def x_min(self):
        return min(self.filter_isnan(self.x()))

    def y_max(self):
        return max(self.filter_isnan(self.y()))

    def y_min(self):
        return min(self.filter_isnan(self.y()))

    def z_max(self):
        return max(self.filter_isnan(self.z()))

    def z_min(self):
        return min(self.filter_isnan(self.z()))

    def import_points(self, list_points: list):
        """Import a list of points to profile

        Args:
            list_points: Liste of PointXYZ

        Returns:
            Nothing.
        """
        for point in list_points:
            pt = PointXYZ(*point)
            self._points.append(pt)

    def get_point_i(self, index: int) -> PointXYZ:
        """Get point at index.

        Args:
            index: Index of point.

        Returns:
            The point.
        """
        try:
            return self._points[index]
        except IndexError:
            raise IndexError(f"Invalid point index: {index}")

    def add(self):
        """Add a new PointXYZ to profile.

        Returns:
            Nothing.
        """
        point_xyz = PointXYZ(0., 0., 0.)
        self._points.append(point_xyz)

    def insert(self, index: int):
        """Insert a new point at index.

        Args:
            index: The index of new profile.

        Returns:
            Nothing.
        """
        point = PointXYZ(0., 0., 0.)
        self._points.insert(index, point)

    def filter_isnan(self, lst):
        """Returns the input list without 'nan' element

        Args:
            lst: The list to filter

        Returns:
            The list without 'nan'
        """
        return [x for x in lst if not np.isnan(x)]

    def _first_point_not_nan(self):
        first_point = self._points[0]

        for point in self._points:
            if not point.is_nan():
                first_point = point
                break

        return first_point

    def _last_point_not_nan(self):
        last_point = self._points[-1]

        for point in self._points[::-1]:
            if not point.is_nan():
                last_point = point
                break

        return last_point

    def get_station(self) -> np.ndarray:
        """Projection of the points of the profile on a plane.

        Args:
            profile: The profile

        Returns:
            Projection of the points of the profile on a plane.
        """
        if self.nb_points < 3:
            return np.array(np.nan)
        else:
            first_named_point = None
            index_first_named_point = None
            last_named_point = None

            first_point_not_nan = self._first_point_not_nan()
            last_point_not_nan = self._last_point_not_nan()

            for index, point in enumerate(self._points):
                if point.point_is_named():
                    index_first_named_point = index
                    first_named_point = point
                    break

            for point in self._points[::-1]:
                if point.point_is_named():
                    last_named_point = point
                    break

            station = []
            constant = 0.0

            if (first_named_point is not None and
                last_named_point is not None):
                if (first_named_point != last_named_point and
                    first_named_point.x != last_named_point.x):
                    vector = Vector1d(first_named_point, last_named_point)
                    normalized_direction_vec = vector.normalized_direction_vector()
                else:
                    vector = Vector1d(first_point_not_nan,
                                      last_point_not_nan)
                    normalized_direction_vec = vector.normalized_direction_vector()

                for point in self._points:
                    xi = point.x - first_named_point.x
                    yi = point.y - first_named_point.y
                    station_i = (normalized_direction_vec[0] * xi +
                                 normalized_direction_vec[1] * yi)
                    station.append(station_i)

                ret = np.array(station)
                constant = ret[index_first_named_point]
            elif first_named_point is None:
                vector = Vector1d(first_point_not_nan,
                                  last_point_not_nan)
                normalized_direction_vec = vector.normalized_direction_vector()

                for point in self._points:
                    xi = point.x - first_point_not_nan.x
                    yi = point.y - first_point_not_nan.y
                    station_i = (normalized_direction_vec[0] * xi +
                                 normalized_direction_vec[1] * yi)
                    station.append(station_i)

                ret = np.array(station)
                index_profile_z_min = np.where(np.array(self.z) == self.z_min)[0][0]
                constant = ret[index_profile_z_min]

            return (ret - constant)