# PointXYZ.py -- Pamhyr # Copyright (C) 2023 INRAE # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. # -*- coding: utf-8 -*- from math import dist import numpy as np from Model.Tools.PamhyrDB import SQLSubModel from Model.Geometry.Point import Point class PointXYZ(Point, SQLSubModel): _sub_classes = [] def __init__(self, x:float = 0.0, y:float = 0.0, z:float = 0.0, name:str = "", profile = None, status = None): super(PointXYZ, self).__init__(name=name, profile=profile, status=status) self._x = float(x) self._y = float(y) self._z = float(z) @classmethod def _sql_create(cls, execute): execute(""" CREATE TABLE geometry_pointXYZ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ind INTEGER NOT NULL, name TEXT, x INTEGER NOT NULL, y INTEGER NOT NULL, z INTEGER NOT NULL, profile INTEGER NOT NULL, sl INTEGER, FOREIGN KEY(profile) REFERENCES profileXYZ(id), FOREIGN KEY(sl) REFERENCES sedimentary_layer(id) ) """) return cls._create_submodel(execute) @classmethod def _sql_update(cls, execute, version): cls._update_submodel(execute, version) major, minor, release = version.strip().split(".") if major == minor == "0": if int(release) < 2: execute( """ ALTER TABLE geometry_pointXYZ ADD COLUMN sl INTEGER REFERENCES sedimentary_layer(id) """ ) return True @classmethod def _sql_load(cls, execute, data = None): status = data["status"] profile = data["profile"] table = execute( "SELECT ind, name, x, y, z, sl " + "FROM geometry_pointXYZ " + f"WHERE profile = {profile.id}" ) # Fill points list with new point for row in table: ind = row[0] name = row[1] x = row[2] y = row[3] z = row[4] sl = row[5] new = cls( name = name, x = x, y = y, z = z, profile = profile, status = status ) if sl == -1 or sl == None: new._sl = None else: new._sl = next( filter( lambda s: s.id == sl, data["sediment_layers_list"].sediment_layers ) ) yield ind, new def _sql_save(self, execute, data = None): profile = data["profile"] ind = data["ind"] sl = self._sl.id if self._sl is not None else -1 sql = ( "INSERT OR REPLACE INTO " + "geometry_pointXYZ(ind, name, x, y, z, profile, sl) "+ "VALUES (" + f"{ind}, '{self._sql_format(self._name)}', " + f"{self.x}, {self.y}, {self.z}, " + f"{profile.id}, {sl}" + ")" ) execute(sql) return True @classmethod def from_data(cls, header, data): point = None try: if len(header) == 0: point = cls( *data ) else: valid_header = {'name', 'x', 'y', 'z', 'profile'} d = {} for i, v in enumerate(data): h = header[i].strip().lower().split(' ')[0] if h in valid_header: d[h] = v point = cls(**d) except Exception as e: raise ClipboardFormatError(header, data) return point def __repr__(self): return f"({self._x}, {self._y}, {self._z}, {self._name})" @property def x(self): return self._x @x.setter def x(self, value): self._x = float(value) self._status.modified() @property def y(self): return self._y @y.setter def y(self, value): self._y = float(value) self._status.modified() @property def z(self): return self._z @z.setter def z(self, value): self._z = float(value) self._status.modified() def is_nan(self): """ Returns: True if at least one coordinate is as np.nan """ return (np.isnan(self.x) or np.isnan(self.y) or np.isnan(self.z)) def dist(self, p2): return PointXYZ.distance(self, p2) @staticmethod def distance(p1, p2): """Euclidean distance between p1 and p2. Args: p1: A XYZ Point p2: A XYZ Point Returns: Euclidean distance between the two points """ return dist((p1.x, p1.y, p1.z), (p2.x, p2.y, p2.z))