diff --git a/.coveragerc b/.coveragerc index daf25d7..4622271 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,7 +1,7 @@ # .coveragerc to control coverage.py [run] branch = True -source = lsgeonetadj +source = lsgeolib # omit = bad_file.py [paths] diff --git a/.gitignore b/.gitignore index 8aae206..b496f25 100644 --- a/.gitignore +++ b/.gitignore @@ -47,8 +47,10 @@ MANIFEST # Per-project virtualenvs .venv*/ +venv/ # Other .pylintrc test_notebooks -.vscode/ \ No newline at end of file +.vscode/ +testing/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 226e6f5..79525fa 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,9 +2,7 @@ Changelog ========= -Version 0.1 -=========== +Version 0.0.1 +============= -- Feature A added -- FIX: nasty bug #1729 fixed -- add your changes here! +- Added building blocks for networks - Measurements diff --git a/README.rst b/README.rst index cec6ccc..6d522fd 100644 --- a/README.rst +++ b/README.rst @@ -3,13 +3,16 @@ lsgeolib =========== -Object oriented python implementation for geodetic network adjustment using the least square adjustment Gauss-Markov model. +Python library for geodetic network adjustment using the least square adjustment defined by the Gauss-Markov model. Description =========== -A longer description of your project goes here... +The functional model is built using the parametric adjustment. Currently supported networks are: + +* 1D with height differences +* 2D with distances, directions and angles Note diff --git a/docs/Makefile b/docs/Makefile index 60c2ec1..3e8dfaf 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -8,8 +8,8 @@ PAPER = BUILDDIR = _build AUTODOCDIR = api AUTODOCBUILD = sphinx-apidoc -PROJECT = lsGeoNetAdj -MODULEDIR = ../src/lsgeonetadj +PROJECT = lsgeolib +MODULEDIR = ../src/lsgeolib # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $?), 1) diff --git a/docs/conf.py b/docs/conf.py index 1124491..9914727 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,7 +35,7 @@ from sphinx import apidoc output_dir = os.path.join(__location__, "api") -module_dir = os.path.join(__location__, "../src/lsgeonetadj") +module_dir = os.path.join(__location__, "../src/lsgeolib") try: shutil.rmtree(output_dir) except FileNotFoundError: @@ -81,7 +81,7 @@ master_doc = 'index' # General information about the project. -project = u'lsGeoNetAdj' +project = u'lsgeolib' copyright = u'2018, jankovic_gd' # The version info for the project you're documenting, acts as replacement for @@ -151,7 +151,7 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". try: - from lsgeonetadj import __version__ as version + from lsgeolib import __version__ as version except ImportError: pass else: @@ -216,7 +216,7 @@ # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'lsgeonetadj-doc' +htmlhelp_basename = 'lsgeolib-doc' # -- Options for LaTeX output -------------------------------------------------- @@ -235,7 +235,7 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'user_guide.tex', u'lsGeoNetAdj Documentation', + ('index', 'user_guide.tex', u'lsgeolib Documentation', u'jankovic_gd', 'manual'), ] diff --git a/docs/index.rst b/docs/index.rst index c36d855..ac835d9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,8 +1,8 @@ =========== -lsGeoNetAdj +lsgeolib =========== -This is the documentation of **lsGeoNetAdj**. +This is the documentation of **lsgeolib**. .. note:: diff --git a/examples/data_1.yml b/examples/data_1.yml index 24d2175..91ea256 100644 --- a/examples/data_1.yml +++ b/examples/data_1.yml @@ -20,40 +20,38 @@ points: y: 1367.459 state: approximate measurements: - directions: - dir1: - from: T0 - to: 1 - value: 0.0 - dir2: - from: T0 - to: 2 - value: 90.000833 - dir3: - from: T0 - to: 3 - value: 179.9975 - dir4: - from: T0 - to: 4 - value: 270.000833 - distances: - dist1: - from: T0 - to: 1 - value: 453.446 - dist2: - from: T0 - to: 2 - value: 679.682 - dist3: - from: T0 - to: 3 - value: 453.452 - dist4: - from: T0 - to: 4 - value: 679.642 + - type: direction + from: T0 + to: p1 + value: 0.0 + - type: direction + from: T0 + to: p2 + value: 90.000833 + - type: direction + from: T0 + to: p3 + value: 179.9975 + - type: direction + from: T0 + to: p4 + value: 270.000833 + - type: distance + from: T0 + to: p1 + value: 453.446 + - type: distance + from: T0 + to: p2 + value: 679.682 + - type: distance + from: T0 + to: p3 + value: 453.452 + - type: distance + from: T0 + to: p4 + value: 679.642 standards: directions: angular_seconds: 3 diff --git a/examples/data_2.yml b/examples/data_2.yml index ec92199..93507a2 100644 --- a/examples/data_2.yml +++ b/examples/data_2.yml @@ -9,24 +9,24 @@ points: z: 100.012 state: fixed measurements: - height_difs: - h1: - from: R1 + height_differences: + - from: R1 to: R2 value: 1.024 - h2: - from: R2 + - from: R2 to: R3 value: 40.846 - h3: - from: R1 + - from: R1 to: R3 value: 41.879 standards: - height_difs_no_stations: - n11: - value: 10 - n22: - value: 21 - n3: - value: 29 \ No newline at end of file + number_of_measurements_between_points: + - from: R1 + to: R2 + number_of_measurements: 10 + - from: R1 + to: R2 + number_of_measurements: 21 + - from: R1 + to: R2 + number_of_measurements: 29 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 434a5f2..0000000 --- a/requirements.txt +++ /dev/null @@ -1,17 +0,0 @@ -# ============================================================================= -# DEPRECATION WARNING: -# -# The file `requirements.txt` does not influence the package dependencies and -# will not be automatically created in the next version of PyScaffold (v4.x). -# -# Please have look at the docs for better alternatives -# (`Dependency Management` section). -# ============================================================================= -# -# Add your pinned requirements so that they can be easily installed with: -# pip install -r requirements.txt -# Remember to also add them in setup.cfg but unpinned. -# Example: -# numpy==1.13.3 -# scipy==1.0 - diff --git a/setup.cfg b/setup.cfg index 341e28d..9481392 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,22 +1,23 @@ -# This file is used to configure your project. -# Read more about the various options under: -# http://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files - [metadata] -name = lsGeoNetAdj -description = Add a short description here! +name = lsgeolib +description = Library for defining Geodetic Networks and performing least square adjustments. author = jankovic_gd author-email = jankovic.gd@gmail.com license = mit url = https://pyscaffold.org/ long-description = file: README.rst -# Change if running only on Windows, Mac or Linux (comma-separated) platforms = any -# Add here all kinds of additional classifiers as defined under -# https://pypi.python.org/pypi?%3Aaction=list_classifiers +keywords = geodesy, least squares, adjustment, network classifiers = - Development Status :: 4 - Beta - Programming Language :: Python + Development Status :: 2 - Pre-Alpha + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.7 + License :: OSI Approved :: MIT + Topic :: Scientific/Engineering + Topic :: Scientific/Engineering :: Mathematics + Topic :: Education + Intended Audience :: Developers + Intended Audience :: Education [options] zip_safe = False @@ -24,14 +25,14 @@ packages = find: include_package_data = True package_dir = =src -# DON'T CHANGE THE FOLLOWING LINE! IT WILL BE UPDATED BY PYSCAFFOLD! setup_requires = pyscaffold>=3.1a0,<3.2a0 -# Add here dependencies of your project (semicolon/line-separated), e.g. -install_requires = pandas; pyyaml -# The usage of test_requires is discouraged, see `Dependency Management` docs -# tests_require = pytest; pytest-cov -# Require a specific Python version, e.g. Python 2.7 or >= 3.4 -# python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* +install_requires = + pandas>=1.0.0,<1.1 + pyyaml>=5.3,<5.4 +tests_require = + pytest + pytest-cov +python_requires = >=3.7.* [options.packages.find] where = src @@ -39,37 +40,35 @@ exclude = tests [options.extras_require] -# Add here additional requirements for extra features, to install with: -# `pip install lsGeoNetAdj[PDF]` like: -# PDF = ReportLab; RXP -# Add here test requirements (semicolon/line-separated) testing = pytest pytest-cov +dev = + black + flake8 +all = + pytest + pytest-cov + black + flake8 [options.entry_points] # Add here console scripts like: # console_scripts = -# script_name = lsgeonetadj.module:function +# script_name = lsgeolib.module:function # For example: # console_scripts = -# fibonacci = lsgeonetadj.skeleton:run +# fibonacci = lsgeolib.skeleton:run # And any other entry points, for example: # pyscaffold.cli = # awesome = pyscaffoldext.awesome.extension:AwesomeExtension [test] -# py.test options when running `python setup.py test` -# addopts = --verbose extras = True [tool:pytest] -# Options for py.test: -# Specify command line options as you would do when invoking py.test directly. -# e.g. --cov-report html (or xml) for html/xml output or --junitxml junit.xml -# in order to write a coverage file that can be read by Jenkins. addopts = - --cov lsgeonetadj --cov-report term-missing + --cov lsgeolib --cov-report term-missing --verbose norecursedirs = dist @@ -82,7 +81,6 @@ build = bdist_wheel release = build upload [bdist_wheel] -# Use this option if your package is pure-python universal = 1 [build_sphinx] @@ -90,22 +88,19 @@ source_dir = docs build_dir = docs/_build [devpi:upload] -# Options for the devpi: PyPI server and packaging tool -# VCS export must be deactivated since we are using setuptools-scm no-vcs = 1 formats = bdist_wheel [flake8] -# Some sane defaults for the code style checker flake8 exclude = .tox build dist .eggs docs/conf.py +max-line-length = 89 + [pyscaffold] -# PyScaffold's parameters when the project was created. -# This will be used when updating. Do not change! version = 3.1 -package = lsgeonetadj +package = lsgeolib diff --git a/setup.py b/setup.py index 1ab14bc..67edf7d 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ - Setup file for lsgeonetadj. + Setup file for lsgeolib. Use setup.cfg to configure your project. This file was generated with PyScaffold 3.1. diff --git a/src/lsgeonetadj/__init__.py b/src/lsgeolib/__init__.py similarity index 84% rename from src/lsgeonetadj/__init__.py rename to src/lsgeolib/__init__.py index c592805..9d08256 100644 --- a/src/lsgeonetadj/__init__.py +++ b/src/lsgeolib/__init__.py @@ -3,9 +3,9 @@ try: # Change here if project is renamed and does not equal the package name - dist_name = 'lsGeoNetAdj' + dist_name = "lsgeolib" __version__ = get_distribution(dist_name).version except DistributionNotFound: - __version__ = 'unknown' + __version__ = "unknown" finally: del get_distribution, DistributionNotFound diff --git a/src/lsgeolib/measurement/__init__.py b/src/lsgeolib/measurement/__init__.py new file mode 100644 index 0000000..86289d8 --- /dev/null +++ b/src/lsgeolib/measurement/__init__.py @@ -0,0 +1,6 @@ +from .point import Point, FixedPoint, ApproximatePoint +from .distance import Distance +from .direction import Direction +from .height_difference import HeightDifference +from .angle import Angle +from .utils import OrientationAngle diff --git a/src/lsgeolib/measurement/angle.py b/src/lsgeolib/measurement/angle.py new file mode 100644 index 0000000..1242058 --- /dev/null +++ b/src/lsgeolib/measurement/angle.py @@ -0,0 +1,81 @@ +import math +from typing import Type, Dict + +from .measurement import Measurement +from .point import Point, ApproximatePoint, FixedPoint +from .utils import Azimuth +from .distance import Distance + + +class Angle(Measurement): + def __init__( + self, + point_from: Type[Point], + point_to: Type[Point], + point_base: Type[Point], + measured: float, + ): + super(Angle).__init__(point_from, point_to, measured, point_base) + self.azimuth_base_from = Azimuth(point_base, point_from) + self.azimuth_base_to = Azimuth(point_base, point_to) + + @property + def measured(self): + return self._measured + + @measured.setter + def measured(self, value): + # TODO add possibility for converting DMS to float + if value < 0: + raise ValueError("Angle measurements cannot be negative") + + self._measured = float(value) + + def calculate_approximate(self, *args, **kwargs) -> float: + self.approximate = self.azimuth_to.azimuth - self.azimuth_from.azimuth + return self.approximate + + def calculate_adjusted(self) -> float: + pass + + def calculate_coefficients(self) -> Dict[str, float]: + distance_base_from = Distance( + self.point_base, self.point_from, 0 + ).calculate_approximate() + distance_base_to = Distance( + self.point_base, self.point_to, 0 + ).calculate_approximate() + + a_base_from = 206264.806 * ( + math.sin(math.radians(self.azimuth_base_from.azimuth)) / distance_base_from + ) + a_from_base = -a_base_from + a_base_to = 206264.806 * ( + math.sin(math.radians(self.azimuth_base_to.azimuth)) / distance_base_to + ) + a_to_base = -a_base_to + + b_base_from = -206264.806 * ( + math.cos(math.radians(self.azimuth_base_from.azimuth)) / distance_base_from + ) + b_from_base = -b_base_from + b_base_to = -206264.806 * ( + math.cos(math.radians(self.azimuth_base_to.azimuth)) / distance_base_to + ) + b_to_base = -b_base_to + + self.coefficients = { + f"a_({self.point_base.id}": a_base_to - a_base_from, + f"b_({self.point_base.id}": b_base_to - b_base_from, + f"a_{self.point_to.id}": a_to_base, + f"b_{self.point_to.id}": b_to_base, + f"a_{self.point_from.id}": -a_from_base, + f"b_{self.point_from.id}": -b_from_base, + } + + self.pop_coefficients() + + return self.coefficients + + def calculate_free_value(self) -> float: + self.free_value = self.approximate - self.measured diff --git a/src/lsgeolib/measurement/direction.py b/src/lsgeolib/measurement/direction.py new file mode 100644 index 0000000..985b65e --- /dev/null +++ b/src/lsgeolib/measurement/direction.py @@ -0,0 +1,98 @@ +import math +from typing import Type, Dict +from .point import Point, ApproximatePoint, FixedPoint +from .measurement import Measurement +from .distance import Distance +from .utils import Azimuth, OrientationAngle + + +class Direction(Measurement): + """Direction class that represents a measurement of a direction read while measuring + a point. + + Attributes: + point_from (Point): Point from which the measurement is taken. + point_to (Point): Point to which the measurement is taken. + measured (float): Measured direction + """ + + def __init__(self, point_from: Type[Point], point_to: Type[Point], measured: float): + super().__init__(point_from, point_to, measured) + self._orientation_angle: OrientationAngle = None + self.approximate_orientation_angle: float = 0.0 + self.azimuth: Azimuth = Azimuth(point_from, point_to) + + @property + def measured(self): + return self._measured + + @measured.setter + def measured(self, value): + # TODO add possibility for converting DMS to float + if value < 0: + raise ValueError("Direction measurements cannot be negative") + + if value > 360: + raise ValueError("Direction mesaurements cannot be higher than 360 degrees") + + self._measured = float(value) + + @property + def orientation_angle(self): + return self._orientation_angle + + @orientation_angle.setter + def orientation_angle(self, value): + self._orientation_angle = value + + def calculate_approximate(self, *args, **kwargs) -> float: + """Computes the approximate direction using the average orientation + and approximate azimuth""" + + self.approximate = ( + self.azimuth.azimuth + self._orientation_angle.average_orientation_angle + ) + + return self.approximate + + def calculate_adjusted(self) -> float: + pass + + def calculate_free_value(self) -> float: + self.free_value = self.approximate - self.measured + + def calculate_approximate_orientation_angle(self) -> float: + approximate_orientation_angle = self.measured - self.azimuth.azimuth + + if approximate_orientation_angle < 0: + approximate_orientation_angle += 360 + + self.approximate_orientation_angle = approximate_orientation_angle + return self.approximate_orientation_angle + + def calculate_coefficients(self) -> Dict["str", "float"]: + + distance = Distance(self.point_from, self.point_to, 0).calculate_approximate() + + a_from_to = 206264.806 * ( + math.sin(math.radians(self.azimuth.azimuth)) / distance + ) + a_to_from = -a_from_to + b_from_to = -206264.806 * ( + math.cos(math.radians(self.azimuth.azimuth)) / distance + ) + b_to_from = -b_from_to + z_coef = 1 + + self.coefficients = { + f"a_{self.point_from.id}": a_from_to, + f"b_{self.point_from.id}": b_from_to, + f"z_{self.point_from.id}": z_coef, + f"a_{self.point_to.id}": a_to_from, + f"b_{self.point_to.id}": b_to_from, + f"z_{self.point_to.id}": z_coef, + } + + self.pop_coefficients() + + return self.coefficients diff --git a/src/lsgeolib/measurement/distance.py b/src/lsgeolib/measurement/distance.py new file mode 100644 index 0000000..330cd79 --- /dev/null +++ b/src/lsgeolib/measurement/distance.py @@ -0,0 +1,59 @@ +import math +from .point import Point, ApproximatePoint, FixedPoint +from .measurement import Measurement + +from typing import Type, Dict + + +class Distance(Measurement): + """Distance class that represents a measurement of length between two points. + + Attributes: + point_from (Point): Point from which the measurement is taken. + point_to (Point): Point to which the measurement is taken. + measured (float): Measured distance + """ + + def __init__( + self, point_from: Type[Point], point_to: Type[Point], measured: float, + ): + super().__init__(point_from, point_to, measured) + + @property + def measured(self): + return self._measured + + @measured.setter + def measured(self, value): + if value < 0: + raise ValueError("Distance measurements cannot be negative") + + self._measured = float(value) + + def calculate_approximate(self, *args, **kwargs) -> float: + self.approximate = math.sqrt(self.dx ** 2 + self.dy ** 2) + return self.approximate + + def calculate_adjusted(self) -> float: + pass + + def calculate_free_value(self) -> float: + self.free_value = self.approximate - self.measured + return self.free_value + + def calculate_coefficients(self) -> Dict[str, float]: + a_from_to = -(self.point_to.x - self.point_from.x) / self.approximate + a_to_from = -a_from_to + b_from_to = -(self.point_to.y - self.point_from.y) / self.approximate + b_to_from = -b_from_to + + self.coefficients = { + f"a_{self.point_from.id}": a_from_to, + f"b_{self.point_from.id}": b_from_to, + f"a_{self.point_to.id}": a_to_from, + f"b_{self.point_to.id}": b_to_from, + } + + self.pop_coefficients() + + return self.coefficients diff --git a/src/lsgeolib/measurement/height_difference.py b/src/lsgeolib/measurement/height_difference.py new file mode 100644 index 0000000..1948006 --- /dev/null +++ b/src/lsgeolib/measurement/height_difference.py @@ -0,0 +1,52 @@ +from .point import Point, ApproximatePoint, FixedPoint +from .measurement import Measurement +from typing import Type, Dict + + +class HeightDifference(Measurement): + """Measurement where the difference in height is measured between two points + + [extended_summary] + + Attributes: + point_from (Point): Point from which the measurement is taken. + point_to (Point): Point to which the measurement is taken. + measured (float): Measured height difference + """ + + def __init__( + self, point_from: Type[Point], point_to: Type[Point], measured: float, + ): + super().__init__(point_from, point_to, measured) + + @property + def measured(self): + return self._measured + + @measured.setter + def measured(self, value): + self._measured = float(value) + + def calculate_approximate(self, *args, **kwargs) -> float: + self.approximate = self.point_to.z - self.point_from.z + return self.approximate + + def calculate_adjusted(self) -> float: + pass + + def calculate_coefficients(self) -> Dict[str, float]: + a_from_to = -1 + a_to_from = 1 + + self.coefficients = { + f"a_{self.point_from.id}_{self.point_to.id}": a_from_to, + f"a_{self.point_to.id}_{self.point_from.id}": a_to_from, + } + + self.pop_coefficients() + + return self.coefficients + + def calculate_free_value(self) -> float: + self.free_value = self.approximate - self.measured + return self.free_value diff --git a/src/lsgeolib/measurement/measurement.py b/src/lsgeolib/measurement/measurement.py new file mode 100644 index 0000000..8427330 --- /dev/null +++ b/src/lsgeolib/measurement/measurement.py @@ -0,0 +1,71 @@ +from .point import Point, FixedPoint +from abc import abstractmethod, abstractproperty, ABCMeta +from typing import Type, Dict + + +class Measurement(object): + + __metaclass__ = ABCMeta + + @abstractmethod + def __init__( + self, + point_from: Type[Point], + point_to: Type[Point], + measured: float, + point_base: Type[Point] = None, + ): + # Known values + self.point_from = point_from + self.point_to = point_to + self.measured = measured + self.point_base = point_base + + # Computed values + self.approximate: float = None + self.adjusted: float = None + self.free_value: float = None + self.coefficients = dict() + + if not point_base: + self.dx = point_to.x - point_from.x + self.dy = point_to.y - point_from.y + + def __repr__(self) -> str: + """Representation function for measurements""" + if not self.point_base: + return f"{type(self).__name__}_{self.point_from.id}-\ + {self.point_to.id}({self.measured})" + + return f"{type(self).__name__}_{self.point_from.id}-\ + {self.point_base.id}-{self.point_to.id}({self.measured})" + + @abstractproperty + def measured(self): + raise NotImplementedError + + @abstractmethod + def calculate_approximate(self, *args, **kwargs) -> float: + raise NotImplementedError + + @abstractmethod + def calculate_adjusted(self) -> float: + raise NotImplementedError + + @abstractmethod + def calculate_free_value(self) -> float: + raise NotImplementedError + + @abstractmethod + def calculate_coefficients(self) -> Dict[str, float]: + raise NotImplementedError + + def pop_coefficients(self) -> None: + for point in [self.point_base, self.point_from, self.point_to]: + if isinstance(point, FixedPoint): + try: + self.coefficients.pop(f"a_{point.id}") + self.coefficients.pop(f"b_{point.id}") + self.coefficients.pop(f"z_{point.id}") + except KeyError: + pass diff --git a/src/lsgeolib/measurement/point.py b/src/lsgeolib/measurement/point.py new file mode 100644 index 0000000..209d51c --- /dev/null +++ b/src/lsgeolib/measurement/point.py @@ -0,0 +1,35 @@ +"""Module that contains the Point class and its relevant methods""" + + +class Point: + """Point class that represents a 0 dimensional entity with 3 spatial coordinates. + In least square terms each point can be: + * fixed - coordinates of the point cannot change + * approximate - coordinates of the point are an unknown and will be adjusted + + Attributes: + identifier (str): identifier of the point. + x (float): x coordinate of point. + y (float): y coordinate of point. + z (float, optional): z coordinate of point, also height of point. + """ + + def __init__( + self, identifier: str, x: float = 0.0, y: float = 0.0, z: float = 0.0, + ): + self.id = str(identifier) + self.x = float(x) + self.y = float(y) + self.z = float(z) + + def __repr__(self): + return f"{type(self).__name__}_{self.id}(x: {self.x}, y: {self.y}, z: {self.z})" + + +class FixedPoint(Point): + pass + + +class ApproximatePoint(Point): + def calculate_adjusted_coordinates(self): + pass diff --git a/src/lsgeolib/measurement/utils.py b/src/lsgeolib/measurement/utils.py new file mode 100644 index 0000000..28df0ce --- /dev/null +++ b/src/lsgeolib/measurement/utils.py @@ -0,0 +1,46 @@ +import math +from typing import Type + +from .point import Point + + +class OrientationAngle: + def __init__(self): + self._orientation_angles = [] + self.average_orientation_angle = 0.0 + + def add_orientation_angle(self, orientation_angle): + self._orientation_angles.append(orientation_angle) + self.average_orientation_angle = sum(self._orientation_angles) / len( + self._orientation_angles + ) + + +class Azimuth: + def __init__(self, point_from, point_to): + self.point_from: Type[Point] = point_from + self.point_to: Type[Point] = point_to + self.azimuth: float = 0 + + self.dx = point_to.x - point_from.x + self.dy = point_to.y - point_from.y + self.calculate_azimuth() + + def calculate_azimuth(self) -> float: + if self.dx > 0 > self.dy: + self.azimuth = math.degrees(math.atan(self.dx / self.dy)) + 180 + elif self.dx < 0 > self.dy: + self.azimuth = math.degrees(math.atan(self.dx / self.dy)) + 180 + elif self.dx < 0 < self.dy: + self.azimuth = math.degrees(math.atan(self.dx / self.dy)) + 360 + elif self.dy == 0 < self.dx: + self.azimuth = 90 + elif self.dy == 0 > self.dx: + self.azimuth = 270 + elif self.dx == 0 > self.dy: + self.azimuth = 180 + elif self.dx == 0 < self.dy: + self.azimuth = 0 + elif self.dx > 0 < self.dy: + self.azimuth = math.degrees(math.atan(self.dx / self.dy)) + return self.azimuth diff --git a/src/lsgeonetadj/model/__init__.py b/src/lsgeolib/model/__init__.py similarity index 100% rename from src/lsgeonetadj/model/__init__.py rename to src/lsgeolib/model/__init__.py diff --git a/src/lsgeonetadj/model/functional.py b/src/lsgeolib/model/functional.py similarity index 100% rename from src/lsgeonetadj/model/functional.py rename to src/lsgeolib/model/functional.py diff --git a/src/lsgeonetadj/model/stochastic.py b/src/lsgeolib/model/stochastic.py similarity index 100% rename from src/lsgeonetadj/model/stochastic.py rename to src/lsgeolib/model/stochastic.py diff --git a/src/lsgeonetadj/network/__init__.py b/src/lsgeolib/network/__init__.py similarity index 100% rename from src/lsgeonetadj/network/__init__.py rename to src/lsgeolib/network/__init__.py diff --git a/src/lsgeolib/network/geodetic_network.py b/src/lsgeolib/network/geodetic_network.py new file mode 100644 index 0000000..a9d0611 --- /dev/null +++ b/src/lsgeolib/network/geodetic_network.py @@ -0,0 +1,21 @@ +"""Module Docstring""" +import yaml +import pandas as pd + +from lsgeolib.model.functional import ( + direction_eq, + angle_eq, + oriented_angle_eq, + distance_eq, + height_dif_eq, +) +from lsgeolib.network.utils import check_directions + + +class GeodeticNetwork: + """docstring for GeodeticNetwork.""" + + def __init__(self): + super(GeodeticNetwork, self).__init__() + self.measurements = None + self.covariances = None diff --git a/src/lsgeonetadj/network/utils.py b/src/lsgeolib/network/utils.py similarity index 100% rename from src/lsgeonetadj/network/utils.py rename to src/lsgeolib/network/utils.py diff --git a/src/lsgeonetadj/element/__init__.py b/src/lsgeonetadj/element/__init__.py deleted file mode 100644 index 931b9a2..0000000 --- a/src/lsgeonetadj/element/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from lsgeonetadj.element.point import Point -from lsgeonetadj.element.line import Line -from lsgeonetadj.element.angle import Angle diff --git a/src/lsgeonetadj/element/angle.py b/src/lsgeonetadj/element/angle.py deleted file mode 100644 index 85c1e97..0000000 --- a/src/lsgeonetadj/element/angle.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Module docstring""" - -from lsgeonetadj.element import Line - - -class Angle(): - """docstring for Angle.""" - - def __init__(self, l1: Line, l2: Line, m_ang: float = None): - super(Angle, self).__init__() - assert isinstance(l1, Line) - assert isinstance(l2, Line) - self.l1 = l1 - self.l2 = l2 - if m_ang: - self.m_ang = m_ang - - def calculate_approximate_angle_direction(self): - """Docstring""" - assert self.l2.m_dir > self.l1.m_dir - return self.l2.m_dir - self.l1.m_dir - - def calculate_approximate_angle_azimuth(self): - """Docstring""" - assert self.l2.calculate_approximate_azimuth( - ) > self.l1.calculate_approximate_azimuth() - return self.l2.calculate_approximate_azimuth() - self.l1.calculate_approximate_azimuth() diff --git a/src/lsgeonetadj/element/line.py b/src/lsgeonetadj/element/line.py deleted file mode 100644 index 40638dc..0000000 --- a/src/lsgeonetadj/element/line.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Module Docstring""" - -import math -from lsgeonetadj.element import Point - - -class Line(): - """Line is defined as a 1D element and its measurements are defined as going from p1 to p2 - - Measurements on a line for a 1D network are height difference, for a 2D network are distance, - direction and azimuth. - - Attributes: - p1 (Point): first point - p2 (Point): second point - m_dist (double, optional): distance measured, positive - m_dir (double, optional): direction measured, positive - m_dh (double, optional): height difference measured - m_azi (double, optional): azimuth measured, positive - """ - - def __init__(self, p1: Point, p2: Point, m_dist: float = 0.0, m_dir: float = 0.0, - m_dh: float = 0.0, m_azi: float = 0.0): - super(Line, self).__init__() - assert isinstance(p1, Point) - assert isinstance(p2, Point) - self.p1 = p1 - self.p2 = p2 - self.m_dist = float(m_dist) - self.m_dir = float(m_dir) - self.m_dh = float(m_dh) - self.m_azi = float(m_azi) - self.dx = p2.x - p1.x - self.dy = p2.y - p1.y - - def calculate_approximate_distance(self) -> float: - """Docstring""" - setattr(self, 'a_dist', math.sqrt(self.dx**2 + self.dy**2)) - return self.a_dist # noqa pylint: disable=E1101 - - def calculate_approximate_azimuth(self) -> float: - """Docstring""" - if self.dx > 0 > self.dy: - setattr(self, 'a_azi', math.degrees( - math.atan(self.dx/self.dy)) + 180) - if self.dx < 0 > self.dy: - setattr(self, 'a_azi', math.degrees( - math.atan(self.dx/self.dy)) + 180) - if self.dx < 0 < self.dy: - setattr(self, 'a_azi', math.degrees( - math.atan(self.dx/self.dy)) + 360) - if self.dy == 0 < self.dx: - setattr(self, 'a_azi', 90) - if self.dy == 0 > self.dx: - setattr(self, 'a_azi', 270) - if self.dx == 0 > self.dy: - setattr(self, 'a_azi', 180) - if self.dx == 0 < self.dy: - setattr(self, 'a_azi', 0) - if self.dx > 0 < self.dy: - setattr(self, 'a_azi', math.degrees(math.atan(self.dx/self.dy))) - return self.a_azi # noqa pylint: disable=E1101 - - def calculate_approximate_height_difference(self) -> float: - """Docstring""" - setattr(self, 'a_dh', self.p2.z - self.p1.z) - return self.a_dh # noqa pylint: disable=E1101 - - def calculate_approximate_orientation(self) -> float: - """Docstring""" - orientation = self.m_dir - self.calculate_approximate_azimuth() - if orientation < 0: - setattr(self, 'ori', orientation + 360) - return self.ori # noqa pylint: disable=E1101 - setattr(self, 'ori', orientation) - return self.ori # noqa pylint: disable=E1101 - - def calculate_approximate_direction(self, avg_orientation: float) -> float: - """Computes the approximate direction using the average orientation - and approximate azimuth""" - setattr(self, 'a_dir', self.calculate_approximate_azimuth() + avg_orientation) - return self.a_dir # noqa pylint: disable=E1101 - - def __str__(self): - """Representation function""" - return 'Line_{}-{}'.format(self.p1.p_id, self.p2.p_id) diff --git a/src/lsgeonetadj/element/point.py b/src/lsgeonetadj/element/point.py deleted file mode 100644 index afb70cc..0000000 --- a/src/lsgeonetadj/element/point.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Module that contains the Point class and its relevant methods""" - - -class Point(): - """Point class that represents a 0 dimensional entity with 3 spatial coordinates - - Attributes: - p_id (str): id of the point. - state ['fixed'|'approximate']: state of point. - x (double): x coordinate of point. - y (double): y coordinate of point. - z (double, optional): z coordinate of point, also height of point. - """ - states = ['fixed', 'approximate'] - - def __init__(self, p_id: str, state: str, x: float = 0.0, y: float = 0.0, z: float = 0): - super(Point, self).__init__() - self.p_id = p_id - if state not in self.states: - raise TypeError('Point state must be "fixed" or "approximate"') - self.state = state - self.x = float(x) - self.y = float(y) - self.z = float(z) - - def __eq__(self, other) -> bool: - return self.x == other.x and \ - self.y == other.y and \ - self.z == other.z - - def __str__(self) -> str: - """Representation function""" - return 'Point_{}({}, {}, {}, {})'.format(self.p_id, self.x, self.y, self.z, self.state) diff --git a/src/lsgeonetadj/element/utils.py b/src/lsgeonetadj/element/utils.py deleted file mode 100644 index 45fba1a..0000000 --- a/src/lsgeonetadj/element/utils.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Module Docstring""" - - -def calculate_avg_orientation(orientation_list: list) -> float: - """Docstring""" - return sum(orientation_list) / len(orientation_list) diff --git a/src/lsgeonetadj/network/geodetic_network.py b/src/lsgeonetadj/network/geodetic_network.py deleted file mode 100644 index ede45d4..0000000 --- a/src/lsgeonetadj/network/geodetic_network.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Module Docstring""" -import yaml -import pandas as pd - -from lsgeonetadj.model.functional import direction_eq, \ - angle_eq, oriented_angle_eq, distance_eq, height_dif_eq -from lsgeonetadj.network.utils import check_directions - -class GeodeticNetwork(): - """docstring for GeodeticNetwork.""" - points = {} - measurements = {} - - def __init__(self, yml_path): - super(GeodeticNetwork, self).__init__() - self.__read_yml(yml_path) - columns, index = self.__set_headers() - self.A = pd.DataFrame(columns=columns, index=index) - self.f = pd.Series(name='f') - - def __set_headers(self): - """Sets the headers to unknown params and index - to measurements""" - columns = [] - for point in self.points: - if self.points[point]['state'] == 'approximate': - for coord in self.points[point]: - if coord == 'state': - continue - columns.append('{}.d{}'.format(point, coord)) - index = [] - for measurement_type in self.measurements: - for measurement in self.measurements[measurement_type]: - index_str = '{}.{}_{}'.format( - measurement, - self.measurements[measurement_type][measurement]['from'], - self.measurements[measurement_type][measurement]['to']) - index.append(index_str) - columns = check_directions(columns, self.points, self.measurements) - return columns, index - - def __read_yml(self, yml_path): - """Docstring""" - with open(yml_path) as f: - data = yaml.safe_load(f) - for element in data: - setattr(self, element, data[element]) - - def init_functional(self): - # TODO - # if self.measurements['directions']: - # direction_eq() - # if self.measurements['angles']: - # angle_eq() - # if self.measurements['oriented_angles']: - # oriented_angle_eq() - if self.measurements['distances']: - for distance in self.measurements['distances']: - a_ij, b_ij, a_ji, b_ji, f = distance_eq() - self.A.update({distance: [a_ij, b_ij, a_ji, b_ji]}) - # if self.measurements['height_difs']: - # height_dif_eq() - pass - - def init_stochastic(self): - pass \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 58b8f6c..cb352b2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ - Dummy conftest.py for lsgeonetadj. + Dummy conftest.py for lsgeolib. If you don't know what this is for, just leave it empty. Read more about conftest.py under: diff --git a/tests/test_elements.py b/tests/test_elements.py index d5a6ec2..70f112f 100644 --- a/tests/test_elements.py +++ b/tests/test_elements.py @@ -1,72 +1,10 @@ -"""This is the docstring for test_element""" - # -*- coding: utf-8 -*- -import math -from lsgeonetadj.element.point import Point -from lsgeonetadj.element.line import Line -from lsgeonetadj.element.angle import Angle - __author__ = "jankovic_gd" __copyright__ = "jankovic_gd" __license__ = "mit" -p1 = Point(p_id=1, state='approximate', x=10.0, y=20.0) -p2 = Point(p_id=2, state='approximate', x=20.0, y=10.0) -p3 = Point(p_id=3, state='approximate', x=30.0, y=30.0) -p4 = Point(p_id=4, state='approximate', x=10.0, y=10.0, z=10.0) -p5 = Point(p_id=5, state='approximate', x=10.0, y=10.0) -p6 = Point(p_id=6, state='approximate', x=20.0, y=20.0) -l1 = Line(p1, p2, m_dir=60) -l2 = Line(p2, p3, m_dir=80) -l3_1 = Line(p5, p3) -l3_2 = Line(p3, p5) -l4 = Line(p5, p6) -l5 = Line(p5, p2) -a1 = Angle(l1, l2) -a2 = Angle(l4, l5) - - -def test_calculate_approximate_distance(): - """Test function for Line.calculate_approximate_distance()""" - - test_approximate_distance = l1.calculate_approximate_distance() - assert test_approximate_distance == 10 * math.sqrt(2) - # error when impossible to calculate distance - # with pytest.raises(AssertionError): - # l1.calculateDistance() - - -def test_calculate_approximate_angle(): - """Test function for Angle.calculate_approximate_angle()""" - - test_approximate_angle = a1.calculate_approximate_angle_direction() - assert test_approximate_angle == 20 - - -def test_calculate_approximate_azimuth(): - """Test function for Line.calculate_approximate_azimuth()""" - - test1_approximate_azimuth = l3_1.calculate_approximate_azimuth() - test2_approximate_azimuth = l3_2.calculate_approximate_azimuth() - assert test1_approximate_azimuth == 45.0 - assert test2_approximate_azimuth == 225.0 - # TODO add more cases here - - -def test_points_equal(): - """Test function for Point.__eq__()""" - - assert p1 == p1 - assert p1 != p3 - assert p4 == p4 - - -def test_calculate_approximate_angle_azimuth(): - """Test function for Angle().calculate_approximate_angle_azimuth""" - - test_approximate_angle_azimuth = a2.calculate_approximate_angle_azimuth() - assert test_approximate_angle_azimuth == 45 - # TODO add more cases +def test_sample(): + assert True